From 164fc0c9aae58bee14b82d0952a6d8e10aaa34d7 Mon Sep 17 00:00:00 2001 From: Sergey Vladimirov Date: Fri, 30 Sep 2011 13:16:50 +0000 Subject: [PATCH] rewrite Sttb utils to handle complex cases git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1177643 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/hwpf/model/BookmarksTables.java | 14 +- .../hwpf/model/RevisionMarkAuthorTable.java | 72 +----- .../apache/poi/hwpf/model/SavedByTable.java | 84 +++---- .../org/apache/poi/hwpf/model/SttbUtils.java | 211 ++++++++++++++++++ .../org/apache/poi/hwpf/model/SttbfUtils.java | 92 -------- 5 files changed, 257 insertions(+), 216 deletions(-) create mode 100644 src/scratchpad/src/org/apache/poi/hwpf/model/SttbUtils.java delete mode 100644 src/scratchpad/src/org/apache/poi/hwpf/model/SttbfUtils.java diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/BookmarksTables.java b/src/scratchpad/src/org/apache/poi/hwpf/model/BookmarksTables.java index bc2d21f15c..29910b0b91 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/model/BookmarksTables.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/BookmarksTables.java @@ -21,12 +21,10 @@ import java.util.ArrayList; import java.util.Arrays; 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.util.Internal; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; @Internal public class BookmarksTables @@ -120,8 +118,8 @@ public class BookmarksTables int namesLength = fib.getLcbSttbfbkmk(); if ( namesStart != 0 && namesLength != 0 ) - this.names = new ArrayList( Arrays.asList( SttbfUtils.read( - tableStream, namesStart ) ) ); + this.names = new ArrayList( Arrays.asList( SttbUtils + .readSttbfBkmk( tableStream, namesStart ) ) ); int firstDescriptorsStart = fib.getFcPlcfbkf(); int firstDescriptorsLength = fib.getLcbPlcfbkf(); @@ -196,8 +194,8 @@ public class BookmarksTables } int start = tableStream.getOffset(); - SttbfUtils - .write( tableStream, names.toArray( new String[names.size()] ) ); + SttbUtils.writeSttbfBkmk( names.toArray( new String[names.size()] ), + tableStream ); int end = tableStream.getOffset(); fib.setFcSttbfbkmk( start ); diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/RevisionMarkAuthorTable.java b/src/scratchpad/src/org/apache/poi/hwpf/model/RevisionMarkAuthorTable.java index 1f86dc64fb..b1347d0e76 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/model/RevisionMarkAuthorTable.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/RevisionMarkAuthorTable.java @@ -24,8 +24,6 @@ import java.util.List; 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; /** * String table containing the names of authors of revision marks, e-mails and @@ -35,21 +33,6 @@ import org.apache.poi.util.StringUtil; */ @Internal 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. */ @@ -62,36 +45,11 @@ public final class RevisionMarkAuthorTable { * @param offset the offset into the byte array. * @param size the size of the table in the byte array. */ - public RevisionMarkAuthorTable(byte[] tableStream, int offset, int size) throws IOException { - // Read fExtend - it isn't used - fExtend = LittleEndian.getShort(tableStream, offset); - if(fExtend != 0xFFFF) { - //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; - } - } + public RevisionMarkAuthorTable( byte[] tableStream, int offset, int size ) + throws IOException + { + entries = SttbUtils.readSttbfRMark( tableStream, offset ); + } /** * Gets the entries. The returned list cannot be modified. @@ -121,7 +79,7 @@ public final class RevisionMarkAuthorTable { * @return the number of entries. */ public int getSize() { - return cData; + return entries.length; } /** @@ -130,19 +88,9 @@ public final class RevisionMarkAuthorTable { * @param tableStream the table stream to write to. * @throws IOException if an error occurs while writing. */ - public void writeTo(HWPFOutputStream tableStream) throws IOException { - byte[] header = new byte[6]; - LittleEndian.putShort(header, 0, fExtend); - 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); - } - } + public void writeTo( HWPFOutputStream tableStream ) throws IOException + { + SttbUtils.writeSttbfRMark( entries, tableStream ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/SavedByTable.java b/src/scratchpad/src/org/apache/poi/hwpf/model/SavedByTable.java index 078acd1096..87c7a8ef98 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/model/SavedByTable.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/SavedByTable.java @@ -14,7 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - package org.apache.poi.hwpf.model; import java.io.IOException; @@ -26,55 +25,32 @@ import org.apache.poi.hwpf.model.io.HWPFOutputStream; import org.apache.poi.util.Internal; /** - * String table containing the history of the last few revisions ("saves") of the document. - * Read-only for the time being. - * + * String table containing the history of the last few revisions ("saves") of + * the document. Read-only for the time being. + * * @author Daniel Noll */ @Internal public final class SavedByTable { + /** + * Array of entries. + */ + private SavedByEntry[] entries; - /** - * Array of entries. - */ - private SavedByEntry[] entries; - - /** - * Constructor to read the table from the table stream. - * - * @param tableStream the table stream. - * @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) - { -// // 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 ); - + /** + * Constructor to read the table from the table stream. + * + * @param tableStream + * the table stream. + * @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 ) + { + String[] strings = SttbUtils.readSttbSavedBy( tableStream, offset ); int numEntries = strings.length / 2; entries = new SavedByEntry[numEntries]; for ( int i = 0; i < numEntries; i++ ) @@ -83,15 +59,15 @@ public final class SavedByTable } } - /** - * Gets the entries. The returned list cannot be modified. - * - * @return the list of entries. - */ - public List getEntries() - { - return Collections.unmodifiableList(Arrays.asList(entries)); - } + /** + * Gets the entries. The returned list cannot be modified. + * + * @return the list of entries. + */ + public List getEntries() + { + return Collections.unmodifiableList( Arrays.asList( entries ) ); + } /** * Writes this table to the table stream. @@ -110,7 +86,7 @@ public final class SavedByTable toSave[counter++] = entry.getUserName(); toSave[counter++] = entry.getSaveLocation(); } - SttbfUtils.write( tableStream, toSave ); + SttbUtils.writeSttbSavedBy( toSave, tableStream ); } } diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/SttbUtils.java b/src/scratchpad/src/org/apache/poi/hwpf/model/SttbUtils.java new file mode 100644 index 0000000000..1cc11e0cda --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/SttbUtils.java @@ -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 ); + } + +} diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/SttbfUtils.java b/src/scratchpad/src/org/apache/poi/hwpf/model/SttbfUtils.java deleted file mode 100644 index 8b5bd92e5e..0000000000 --- a/src/scratchpad/src/org/apache/poi/hwpf/model/SttbfUtils.java +++ /dev/null @@ -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; - } - -}