rewrite Sttb utils to handle complex cases

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1177643 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Sergey Vladimirov 2011-09-30 13:16:50 +00:00
parent b814328848
commit ef2876e71f
5 changed files with 257 additions and 216 deletions

View File

@ -21,12 +21,10 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
import org.apache.poi.hwpf.model.io.HWPFOutputStream; import org.apache.poi.hwpf.model.io.HWPFOutputStream;
import org.apache.poi.util.Internal; import org.apache.poi.util.Internal;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
@Internal @Internal
public class BookmarksTables public class BookmarksTables
@ -120,8 +118,8 @@ public class BookmarksTables
int namesLength = fib.getLcbSttbfbkmk(); int namesLength = fib.getLcbSttbfbkmk();
if ( namesStart != 0 && namesLength != 0 ) if ( namesStart != 0 && namesLength != 0 )
this.names = new ArrayList<String>( Arrays.asList( SttbfUtils.read( this.names = new ArrayList<String>( Arrays.asList( SttbUtils
tableStream, namesStart ) ) ); .readSttbfBkmk( tableStream, namesStart ) ) );
int firstDescriptorsStart = fib.getFcPlcfbkf(); int firstDescriptorsStart = fib.getFcPlcfbkf();
int firstDescriptorsLength = fib.getLcbPlcfbkf(); int firstDescriptorsLength = fib.getLcbPlcfbkf();
@ -196,8 +194,8 @@ public class BookmarksTables
} }
int start = tableStream.getOffset(); int start = tableStream.getOffset();
SttbfUtils SttbUtils.writeSttbfBkmk( names.toArray( new String[names.size()] ),
.write( tableStream, names.toArray( new String[names.size()] ) ); tableStream );
int end = tableStream.getOffset(); int end = tableStream.getOffset();
fib.setFcSttbfbkmk( start ); fib.setFcSttbfbkmk( start );

View File

@ -24,8 +24,6 @@ import java.util.List;
import org.apache.poi.hwpf.model.io.HWPFOutputStream; import org.apache.poi.hwpf.model.io.HWPFOutputStream;
import org.apache.poi.util.Internal; import org.apache.poi.util.Internal;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.StringUtil;
/** /**
* String table containing the names of authors of revision marks, e-mails and * String table containing the names of authors of revision marks, e-mails and
@ -35,21 +33,6 @@ import org.apache.poi.util.StringUtil;
*/ */
@Internal @Internal
public final class RevisionMarkAuthorTable { public final class RevisionMarkAuthorTable {
/**
* must be 0xFFFF
*/
private short fExtend = (short) 0xFFFF;
/**
* the number of entries in the table
*/
private short cData = 0;
/**
* must be 0
*/
private short cbExtra = 0;
/** /**
* Array of entries. * Array of entries.
*/ */
@ -62,36 +45,11 @@ public final class RevisionMarkAuthorTable {
* @param offset the offset into the byte array. * @param offset the offset into the byte array.
* @param size the size of the table in the byte array. * @param size the size of the table in the byte array.
*/ */
public RevisionMarkAuthorTable(byte[] tableStream, int offset, int size) throws IOException { public RevisionMarkAuthorTable( byte[] tableStream, int offset, int size )
// Read fExtend - it isn't used throws IOException
fExtend = LittleEndian.getShort(tableStream, offset); {
if(fExtend != 0xFFFF) { entries = SttbUtils.readSttbfRMark( tableStream, offset );
//TODO: throw an exception here? }
}
offset += 2;
// Read the number of entries
cData = LittleEndian.getShort(tableStream, offset);
offset += 2;
// Read cbExtra - it isn't used
cbExtra = LittleEndian.getShort(tableStream, offset);
if(cbExtra != 0) {
//TODO: throw an exception here?
}
offset += 2;
entries = new String[cData];
for (int i = 0; i < cData; i++) {
int len = LittleEndian.getShort(tableStream, offset);
offset += 2;
String name = StringUtil.getFromUnicodeLE(tableStream, offset, len);
offset += len * 2;
entries[i] = name;
}
}
/** /**
* Gets the entries. The returned list cannot be modified. * Gets the entries. The returned list cannot be modified.
@ -121,7 +79,7 @@ public final class RevisionMarkAuthorTable {
* @return the number of entries. * @return the number of entries.
*/ */
public int getSize() { public int getSize() {
return cData; return entries.length;
} }
/** /**
@ -130,19 +88,9 @@ public final class RevisionMarkAuthorTable {
* @param tableStream the table stream to write to. * @param tableStream the table stream to write to.
* @throws IOException if an error occurs while writing. * @throws IOException if an error occurs while writing.
*/ */
public void writeTo(HWPFOutputStream tableStream) throws IOException { public void writeTo( HWPFOutputStream tableStream ) throws IOException
byte[] header = new byte[6]; {
LittleEndian.putShort(header, 0, fExtend); SttbUtils.writeSttbfRMark( entries, tableStream );
LittleEndian.putShort(header, 2, cData); }
LittleEndian.putShort(header, 4, cbExtra);
tableStream.write(header);
for (String name : entries) {
byte[] buf = new byte[name.length() * 2 + 2];
LittleEndian.putShort(buf, 0, (short) name.length());
StringUtil.putUnicodeLE(name, buf, 2);
tableStream.write(buf);
}
}
} }

View File

@ -14,7 +14,6 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.hwpf.model; package org.apache.poi.hwpf.model;
import java.io.IOException; import java.io.IOException;
@ -26,55 +25,32 @@ import org.apache.poi.hwpf.model.io.HWPFOutputStream;
import org.apache.poi.util.Internal; import org.apache.poi.util.Internal;
/** /**
* String table containing the history of the last few revisions ("saves") of the document. * String table containing the history of the last few revisions ("saves") of
* Read-only for the time being. * the document. Read-only for the time being.
* *
* @author Daniel Noll * @author Daniel Noll
*/ */
@Internal @Internal
public final class SavedByTable public final class SavedByTable
{ {
/**
* Array of entries.
*/
private SavedByEntry[] entries;
/** /**
* Array of entries. * Constructor to read the table from the table stream.
*/ *
private SavedByEntry[] entries; * @param tableStream
* the table stream.
/** * @param offset
* Constructor to read the table from the table stream. * the offset into the byte array.
* * @param size
* @param tableStream the table stream. * the size of the table in the byte array.
* @param offset the offset into the byte array. */
* @param size the size of the table in the byte array. public SavedByTable( byte[] tableStream, int offset, int size )
*/ {
public SavedByTable(byte[] tableStream, int offset, int size) String[] strings = SttbUtils.readSttbSavedBy( tableStream, offset );
{
// // Read the value that I don't know what it does. :-)
// unknownValue = LittleEndian.getShort(tableStream, offset);
// offset += 2;
//
// // The stored int is the number of strings, and there are two strings per entry.
// int numEntries = LittleEndian.getInt(tableStream, offset) / 2;
// offset += 4;
//
// entries = new SavedByEntry[numEntries];
// for (int i = 0; i < numEntries; i++)
// {
// int len = LittleEndian.getShort(tableStream, offset);
// offset += 2;
// String userName = StringUtil.getFromUnicodeLE(tableStream, offset, len);
// offset += len * 2;
// len = LittleEndian.getShort(tableStream, offset);
// offset += 2;
// String saveLocation = StringUtil.getFromUnicodeLE(tableStream, offset, len);
// offset += len * 2;
//
// entries[i] = new SavedByEntry(userName, saveLocation);
// }
// first value is mark for extended STTBF ;) -- sergey
String[] strings = SttbfUtils.read( tableStream, offset );
int numEntries = strings.length / 2; int numEntries = strings.length / 2;
entries = new SavedByEntry[numEntries]; entries = new SavedByEntry[numEntries];
for ( int i = 0; i < numEntries; i++ ) for ( int i = 0; i < numEntries; i++ )
@ -83,15 +59,15 @@ public final class SavedByTable
} }
} }
/** /**
* Gets the entries. The returned list cannot be modified. * Gets the entries. The returned list cannot be modified.
* *
* @return the list of entries. * @return the list of entries.
*/ */
public List<SavedByEntry> getEntries() public List<SavedByEntry> getEntries()
{ {
return Collections.unmodifiableList(Arrays.asList(entries)); return Collections.unmodifiableList( Arrays.asList( entries ) );
} }
/** /**
* Writes this table to the table stream. * Writes this table to the table stream.
@ -110,7 +86,7 @@ public final class SavedByTable
toSave[counter++] = entry.getUserName(); toSave[counter++] = entry.getUserName();
toSave[counter++] = entry.getSaveLocation(); toSave[counter++] = entry.getSaveLocation();
} }
SttbfUtils.write( tableStream, toSave ); SttbUtils.writeSttbSavedBy( toSave, tableStream );
} }
} }

View File

@ -0,0 +1,211 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hwpf.model;
import java.io.IOException;
import org.apache.poi.hwpf.model.io.HWPFOutputStream;
import org.apache.poi.util.Internal;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.StringUtil;
/**
* Utils class for storing and reading "STring TaBle stored in File"
*
* @author Sergey Vladimirov (vlsergey {at} gmail {dot} com)
*/
@Internal
class SttbUtils
{
static class Sttb
{
public int cbExtra;
public int cDataLength;
public String[] data;
public byte[][] extraData;
}
private static final int CBEXTRA_STTB_SAVED_BY = 0; // bytes
private static final int CBEXTRA_STTBF_BKMK = 0; // bytes
private static final int CBEXTRA_STTBF_R_MARK = 0; // bytes
private static final int CDATA_SIZE_STTB_SAVED_BY = 2; // bytes
private static final int CDATA_SIZE_STTBF_BKMK = 2; // bytes
private static final int CDATA_SIZE_STTBF_R_MARK = 2; // bytes
static Sttb read( int cDataLength, byte[] buffer, int startOffset )
{
short ffff = LittleEndian.getShort( buffer, startOffset );
int offset = startOffset + 2;
if ( ffff != (short) 0xffff )
{
// Non-extended character Pascal strings
throw new UnsupportedOperationException(
"Non-extended character Pascal strings are not supported right now. "
+ "Please, contact POI developers for update." );
}
// strings are extended character strings
int cData = cDataLength == 2 ? LittleEndian.getUShort( buffer, offset )
: LittleEndian.getInt( buffer, offset );
offset += cDataLength;
Sttb sttb = new Sttb();
sttb.cDataLength = cDataLength;
sttb.cbExtra = LittleEndian.getUShort( buffer, offset );
offset += 2;
sttb.data = new String[cData];
sttb.extraData = new byte[cData][];
for ( int i = 0; i < cData; i++ )
{
int cchData = LittleEndian.getShort( buffer, offset );
offset += 2;
if ( cchData < 0 )
continue;
sttb.data[i] = StringUtil
.getFromUnicodeLE( buffer, offset, cchData );
offset += cchData * 2;
sttb.extraData[i] = LittleEndian.getByteArray( buffer, offset,
sttb.cbExtra );
offset += sttb.cbExtra;
}
return sttb;
}
static String[] readSttbfBkmk( byte[] buffer, int startOffset )
{
return read( CDATA_SIZE_STTBF_BKMK, buffer, startOffset ).data;
}
static String[] readSttbfRMark( byte[] buffer, int startOffset )
{
return read( CDATA_SIZE_STTBF_R_MARK, buffer, startOffset ).data;
}
static String[] readSttbSavedBy( byte[] buffer, int startOffset )
{
return read( CDATA_SIZE_STTB_SAVED_BY, buffer, startOffset ).data;
}
static void write( Sttb sttb, HWPFOutputStream tableStream )
throws IOException
{
final int headerSize = sttb.cDataLength == 2 ? 6 : 8;
byte[] header = new byte[headerSize];
LittleEndian.putShort( header, 0, (short) 0xffff );
if ( sttb.data == null || sttb.data.length == 0 )
{
if ( sttb.cDataLength == 4 )
{
LittleEndian.putInt( header, 2, 0 );
LittleEndian.putUShort( header, 6, sttb.cbExtra );
tableStream.write( header );
return;
}
LittleEndian.putUShort( header, 2, 0 );
LittleEndian.putUShort( header, 4, sttb.cbExtra );
tableStream.write( header );
return;
}
if ( sttb.cDataLength == 4 )
{
LittleEndian.putInt( header, 2, sttb.data.length );
LittleEndian.putUShort( header, 6, sttb.cbExtra );
tableStream.write( header );
}
else
{
LittleEndian.putUShort( header, 2, sttb.data.length );
LittleEndian.putUShort( header, 4, sttb.cbExtra );
tableStream.write( header );
}
for ( int i = 0; i < sttb.data.length; i++ )
{
String entry = sttb.data[i];
if ( entry == null )
{
// is it correct?
tableStream.write( new byte[] { -1, 0 } );
continue;
}
byte[] buf = new byte[entry.length() * 2 + sttb.cbExtra + 2];
LittleEndian.putShort( buf, 0, (short) entry.length() );
StringUtil.putUnicodeLE( entry, buf, 2 );
if ( sttb.extraData != null && i < sttb.extraData.length
&& sttb.extraData[i] != null )
System.arraycopy( sttb.extraData[i], 0, buf,
entry.length() * 2,
Math.min( sttb.extraData[i].length, sttb.cbExtra ) );
tableStream.write( buf );
}
}
static void writeSttbfBkmk( String[] data, HWPFOutputStream tableStream )
throws IOException
{
Sttb sttb = new Sttb();
sttb.cDataLength = CDATA_SIZE_STTBF_BKMK;
sttb.data = data;
sttb.cbExtra = CBEXTRA_STTBF_BKMK;
write( sttb, tableStream );
}
static void writeSttbfRMark( String[] data, HWPFOutputStream tableStream )
throws IOException
{
Sttb sttb = new Sttb();
sttb.cDataLength = CDATA_SIZE_STTBF_R_MARK;
sttb.data = data;
sttb.cbExtra = CBEXTRA_STTBF_R_MARK;
write( sttb, tableStream );
}
static void writeSttbSavedBy( String[] data, HWPFOutputStream tableStream )
throws IOException
{
Sttb sttb = new Sttb();
sttb.cDataLength = CDATA_SIZE_STTB_SAVED_BY;
sttb.data = data;
sttb.cbExtra = CBEXTRA_STTB_SAVED_BY;
write( sttb, tableStream );
}
}

View File

@ -1,92 +0,0 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hwpf.model;
import java.io.IOException;
import org.apache.poi.hwpf.model.io.HWPFOutputStream;
import org.apache.poi.util.Internal;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.StringUtil;
/**
* Utils for storing and reading "STring TaBle stored in File"
*
* @author Sergey Vladimirov (vlsergey {at} gmail {dot} com)
*/
@Internal
class SttbfUtils
{
public static String[] read( byte[] data, int startOffset )
{
short ffff = LittleEndian.getShort( data, startOffset );
if ( ffff != (short) 0xffff )
{
// Non-extended character Pascal strings
throw new UnsupportedOperationException(
"Non-extended character Pascal strings are not supported right now. "
+ "Please, contact POI developers for update." );
}
// strings are extended character strings
int offset = startOffset + 2;
int numEntries = LittleEndian.getInt( data, offset );
offset += 4;
String[] entries = new String[numEntries];
for ( int i = 0; i < numEntries; i++ )
{
int len = LittleEndian.getShort( data, offset );
offset += 2;
String value = StringUtil.getFromUnicodeLE( data, offset, len );
offset += len * 2;
entries[i] = value;
}
return entries;
}
public static int write( HWPFOutputStream tableStream, String[] entries )
throws IOException
{
byte[] header = new byte[6];
LittleEndian.putShort( header, 0, (short) 0xffff );
if ( entries == null || entries.length == 0 )
{
LittleEndian.putInt( header, 2, 0 );
tableStream.write( header );
return 6;
}
LittleEndian.putInt( header, 2, entries.length );
tableStream.write( header );
int size = 6;
for ( String entry : entries )
{
byte[] buf = new byte[entry.length() * 2 + 2];
LittleEndian.putShort( buf, 0, (short) entry.length() );
StringUtil.putUnicodeLE( entry, buf, 2 );
tableStream.write( buf );
size += buf.length;
}
return size;
}
}