From: Josh Micich Date: Sun, 26 Oct 2008 07:18:17 +0000 (+0000) Subject: Merged revisions 707486,707519,707525,707534,707541-707542,707551,707585,707729,70777... X-Git-Tag: ooxml_20081107~16 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=7c0d1fc041746fb337e683c198ed5d618481004e;p=poi.git Merged revisions 707486,707519,707525,707534,707541-707542,707551,707585,707729,707778,707780,707802 via svnmerge from https://svn.apache.org/repos/asf/poi/trunk ........ r707486 | josh | 2008-10-23 15:28:05 -0700 (Thu, 23 Oct 2008) | 1 line Converted Ptgs to use LittleEndianOutput ........ r707519 | josh | 2008-10-23 17:58:49 -0700 (Thu, 23 Oct 2008) | 1 line Fix for unicode string bug in StyleRecord. Improvements to WriteAccessRecord. ........ r707525 | josh | 2008-10-23 19:08:47 -0700 (Thu, 23 Oct 2008) | 1 line Further conversion of Ptg classes to use LittleEndian input/output interfaces ........ r707534 | josh | 2008-10-23 20:47:42 -0700 (Thu, 23 Oct 2008) | 1 line added LittleEndianByteArrayInputStream ........ r707541 | josh | 2008-10-23 21:30:38 -0700 (Thu, 23 Oct 2008) | 1 line Removed String methods from LittleEndianInput ........ r707542 | josh | 2008-10-23 21:40:37 -0700 (Thu, 23 Oct 2008) | 1 line removing unused code ........ r707551 | josh | 2008-10-23 22:46:29 -0700 (Thu, 23 Oct 2008) | 1 line Simplification and code clean-up ........ r707585 | josh | 2008-10-24 01:58:00 -0700 (Fri, 24 Oct 2008) | 1 line General clean-up in LittleEndian util class. (Some optimization, some obsolete code removal) ........ r707729 | josh | 2008-10-24 12:25:11 -0700 (Fri, 24 Oct 2008) | 1 line Fixed test suite name ........ r707778 | josh | 2008-10-24 16:13:44 -0700 (Fri, 24 Oct 2008) | 1 line Optimisation of RecordInputStream - removed intermediate 8K byte buffer. Expected performance gain was not realised immediately, so LittleEndianInput stuff has been pushed down into DocumentInputStream to help. ........ r707780 | josh | 2008-10-24 16:19:26 -0700 (Fri, 24 Oct 2008) | 1 line should have been submitted with c707778 ........ r707802 | josh | 2008-10-24 18:02:37 -0700 (Fri, 24 Oct 2008) | 1 line Further simplification to RecordInputStream. Mostly regarding Strings, ContinueRecords and LittleEndianInput ........ git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@707945 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/contrib/src/org/apache/poi/contrib/poibrowser/TreeReaderListener.java b/src/contrib/src/org/apache/poi/contrib/poibrowser/TreeReaderListener.java index a98ec3906c..df9e996fbe 100644 --- a/src/contrib/src/org/apache/poi/contrib/poibrowser/TreeReaderListener.java +++ b/src/contrib/src/org/apache/poi/contrib/poibrowser/TreeReaderListener.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -15,11 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - package org.apache.poi.contrib.poibrowser; -import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -160,17 +157,7 @@ public class TreeReaderListener implements POIFSReaderListener throw new RuntimeException(t.getMessage()); } - try - { - is.close(); - } - catch (IOException ex) - { - System.err.println - ("Unexpected exception while closing " + - event.getName() + " in " + event.getPath().toString()); - ex.printStackTrace(System.err); - } + is.close(); final MutableTreeNode parentNode = getNode(d.path, filename, rootNode); final MutableTreeNode nameNode = new DefaultMutableTreeNode(d.name); diff --git a/src/java/org/apache/poi/hssf/model/Workbook.java b/src/java/org/apache/poi/hssf/model/Workbook.java index 54639adb18..222f0a6ab6 100644 --- a/src/java/org/apache/poi/hssf/model/Workbook.java +++ b/src/java/org/apache/poi/hssf/model/Workbook.java @@ -788,7 +788,7 @@ public final class Workbook implements Model { if(r instanceof ExtendedFormatRecord) { } else if(r instanceof StyleRecord) { StyleRecord sr = (StyleRecord)r; - if(sr.getIndex() == xfIndex) { + if(sr.getXFIndex() == xfIndex) { return sr; } } else { @@ -806,7 +806,7 @@ public final class Workbook implements Model { // Style records always follow after // the ExtendedFormat records StyleRecord newSR = new StyleRecord(); - newSR.setIndex((short)xfIndex); + newSR.setXFIndex(xfIndex); // Find the spot int addAt = -1; @@ -1782,45 +1782,44 @@ public final class Workbook implements Model { * @see org.apache.poi.hssf.record.StyleRecord * @see org.apache.poi.hssf.record.Record */ - protected Record createStyle(int id) { // we'll need multiple editions StyleRecord retval = new StyleRecord(); switch (id) { case 0 : - retval.setIndex(( short ) 0xffff8010); - retval.setBuiltin(( byte ) 3); + retval.setXFIndex(0x010); + retval.setBuiltinStyle(3); retval.setOutlineStyleLevel(( byte ) 0xffffffff); break; case 1 : - retval.setIndex(( short ) 0xffff8011); - retval.setBuiltin(( byte ) 6); + retval.setXFIndex(0x011); + retval.setBuiltinStyle(6); retval.setOutlineStyleLevel(( byte ) 0xffffffff); break; case 2 : - retval.setIndex(( short ) 0xffff8012); - retval.setBuiltin(( byte ) 4); + retval.setXFIndex(0x012); + retval.setBuiltinStyle(4); retval.setOutlineStyleLevel(( byte ) 0xffffffff); break; case 3 : - retval.setIndex(( short ) 0xffff8013); - retval.setBuiltin(( byte ) 7); + retval.setXFIndex(0x013); + retval.setBuiltinStyle(7); retval.setOutlineStyleLevel(( byte ) 0xffffffff); break; case 4 : - retval.setIndex(( short ) 0xffff8000); - retval.setBuiltin(( byte ) 0); + retval.setXFIndex(0x000); + retval.setBuiltinStyle(0); retval.setOutlineStyleLevel(( byte ) 0xffffffff); break; case 5 : - retval.setIndex(( short ) 0xffff8014); - retval.setBuiltin(( byte ) 5); + retval.setXFIndex(0x014); + retval.setBuiltinStyle(5); retval.setOutlineStyleLevel(( byte ) 0xffffffff); break; } diff --git a/src/java/org/apache/poi/hssf/record/CRNRecord.java b/src/java/org/apache/poi/hssf/record/CRNRecord.java index 417f2a559a..050076cca2 100755 --- a/src/java/org/apache/poi/hssf/record/CRNRecord.java +++ b/src/java/org/apache/poi/hssf/record/CRNRecord.java @@ -18,17 +18,18 @@ package org.apache.poi.hssf.record; import org.apache.poi.hssf.record.constant.ConstantValueParser; -import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.LittleEndianByteArrayOutputStream; +import org.apache.poi.util.LittleEndianOutput; /** - * Title: CRN

- * Description: This record stores the contents of an external cell or cell range

- * REFERENCE: 5.23

+ * Title: CRN(0x005A)

+ * Description: This record stores the contents of an external cell or cell range

+ * REFERENCE: OOO 5.23

* * @author josh micich */ public final class CRNRecord extends Record { - public final static short sid = 0x5A; + public final static short sid = 0x005A; private int field_1_last_column_index; private int field_2_first_column_index; @@ -45,8 +46,8 @@ public final class CRNRecord extends Record { public CRNRecord(RecordInputStream in) { - field_1_last_column_index = in.readByte() & 0x00FF; - field_2_first_column_index = in.readByte() & 0x00FF; + field_1_last_column_index = in.readUByte(); + field_2_first_column_index = in.readUByte(); field_3_row_index = in.readShort(); int nValues = field_1_last_column_index - field_2_first_column_index + 1; field_4_constant_values = ConstantValueParser.parse(in, nValues); @@ -68,13 +69,15 @@ public final class CRNRecord extends Record { public int serialize(int offset, byte [] data) { int dataSize = getDataSize(); - LittleEndian.putShort(data, 0 + offset, sid); - LittleEndian.putShort(data, 2 + offset, (short) dataSize); - LittleEndian.putByte(data, 4 + offset, field_1_last_column_index); - LittleEndian.putByte(data, 5 + offset, field_2_first_column_index); - LittleEndian.putShort(data, 6 + offset, (short) field_3_row_index); - ConstantValueParser.encode(data, 8 + offset, field_4_constant_values); - return getRecordSize(); + int recSize = 4 + dataSize; + LittleEndianOutput out = new LittleEndianByteArrayOutputStream(data, offset, recSize); + out.writeShort(sid); + out.writeShort(dataSize); + out.writeByte(field_1_last_column_index); + out.writeByte(field_2_first_column_index); + out.writeShort(field_3_row_index); + ConstantValueParser.encode(out, field_4_constant_values); + return recSize; } public int getRecordSize() { diff --git a/src/java/org/apache/poi/hssf/record/EmbeddedObjectRefSubRecord.java b/src/java/org/apache/poi/hssf/record/EmbeddedObjectRefSubRecord.java index db0dbf6771..f033149ee8 100644 --- a/src/java/org/apache/poi/hssf/record/EmbeddedObjectRefSubRecord.java +++ b/src/java/org/apache/poi/hssf/record/EmbeddedObjectRefSubRecord.java @@ -27,6 +27,7 @@ import org.apache.poi.hssf.record.formula.RefPtg; import org.apache.poi.util.HexDump; import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndianInput; +import org.apache.poi.util.LittleEndianInputStream; import org.apache.poi.util.LittleEndianOutput; import org.apache.poi.util.StringUtil; @@ -111,10 +112,10 @@ public final class EmbeddedObjectRefSubRecord extends SubRecord { field_3_unicode_flag = ( in.readByte() & 0x01 ) != 0; remaining -= LittleEndian.BYTE_SIZE; if (field_3_unicode_flag) { - field_4_ole_classname = in.readUnicodeLEString(nChars); + field_4_ole_classname = StringUtil.readUnicodeLE(in, nChars); stringByteCount = nChars * 2; } else { - field_4_ole_classname = in.readCompressedUnicode(nChars); + field_4_ole_classname = StringUtil.readCompressedUnicode(in, nChars); stringByteCount = nChars; } } else { @@ -156,12 +157,7 @@ public final class EmbeddedObjectRefSubRecord extends SubRecord { } private static Ptg readRefPtg(byte[] formulaRawBytes) { - byte[] data = new byte[formulaRawBytes.length + 4]; - LittleEndian.putUShort(data, 0, -5555); - LittleEndian.putUShort(data, 2, formulaRawBytes.length); - System.arraycopy(formulaRawBytes, 0, data, 4, formulaRawBytes.length); - RecordInputStream in = new RecordInputStream(new ByteArrayInputStream(data)); - in.nextRecord(); + LittleEndianInput in = new LittleEndianInputStream(new ByteArrayInputStream(formulaRawBytes)); byte ptgSid = in.readByte(); switch(ptgSid) { case AreaPtg.sid: return new AreaPtg(in); diff --git a/src/java/org/apache/poi/hssf/record/LinkedDataFormulaField.java b/src/java/org/apache/poi/hssf/record/LinkedDataFormulaField.java index 4a607c9e37..36aeb7b47c 100644 --- a/src/java/org/apache/poi/hssf/record/LinkedDataFormulaField.java +++ b/src/java/org/apache/poi/hssf/record/LinkedDataFormulaField.java @@ -51,7 +51,7 @@ public final class LinkedDataFormulaField { .append( "=" ) .append(ptg.toString() ) .append( "\n" ) - .append(ptg.toDebugString() ) + .append(ptg.toString()) .append( "\n" ); } } diff --git a/src/java/org/apache/poi/hssf/record/RecordInputStream.java b/src/java/org/apache/poi/hssf/record/RecordInputStream.java index cec9ddb191..f9dd2c76c7 100755 --- a/src/java/org/apache/poi/hssf/record/RecordInputStream.java +++ b/src/java/org/apache/poi/hssf/record/RecordInputStream.java @@ -17,12 +17,12 @@ package org.apache.poi.hssf.record; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; + import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndianInput; - -import java.io.IOException; -import java.io.InputStream; -import java.io.ByteArrayOutputStream; +import org.apache.poi.util.LittleEndianInputStream; /** * Title: Record Input Stream

@@ -34,115 +34,147 @@ public final class RecordInputStream extends InputStream implements LittleEndian /** Maximum size of a single record (minus the 4 byte header) without a continue*/ public final static short MAX_RECORD_DATA_SIZE = 8224; private static final int INVALID_SID_VALUE = -1; + /** + * When {@link #_currentDataLength} has this value, it means that the previous BIFF record is + * finished, the next sid has been properly read, but the data size field has not been read yet. + */ + private static final int DATA_LEN_NEEDS_TO_BE_READ = -1; + private static final byte[] EMPTY_BYTE_ARRAY = { }; + + /** {@link LittleEndianInput} facet of the wrapped {@link InputStream} */ + private final LittleEndianInput _le; + /** the record identifier of the BIFF record currently being read */ + private int _currentSid; + /** + * Length of the data section of the current BIFF record (always 4 less than the total record size). + * When uninitialised, this field is set to {@link #DATA_LEN_NEEDS_TO_BE_READ}. + */ + private int _currentDataLength; + /** + * The BIFF record identifier for the next record is read when just as the current record + * is finished. + * This field is only really valid during the time that ({@link #_currentDataLength} == + * {@link #DATA_LEN_NEEDS_TO_BE_READ}). At most other times its value is not really the + * 'sid of the next record'. Wwhile mid-record, this field coincidentally holds the sid + * of the current record. + */ + private int _nextSid; + /** + * index within the data section of the current BIFF record + */ + private int _currentDataOffset; + + public RecordInputStream(InputStream in) throws RecordFormatException { + if (in instanceof LittleEndianInput) { + // accessing directly is an optimisation + _le = (LittleEndianInput) in; + } else { + // less optimal, but should work OK just the same. Often occurs in junit tests. + _le = new LittleEndianInputStream(in); + } + _nextSid = readNextSid(); + } + + /** + * @returns the number of bytes available in the current BIFF record + * @see #remaining() + */ + public int available() { + return remaining(); + } - private InputStream in; - private short currentSid; - private short currentLength = -1; - private short nextSid; - - private final byte[] data = new byte[MAX_RECORD_DATA_SIZE]; - private short recordOffset; - private long pos; - - private boolean autoContinue = true; - - public RecordInputStream(InputStream in) throws RecordFormatException { - this.in = in; - try { - nextSid = LittleEndian.readShort(in); - //Don't increment the pos just yet (technically we are at the start of - //the record stream until nextRecord is called). - } catch (IOException ex) { - throw new RecordFormatException("Error reading bytes", ex); - } - } - - /** This method will read a byte from the current record*/ public int read() { checkRecordPosition(LittleEndian.BYTE_SIZE); - - byte result = data[recordOffset]; - recordOffset += LittleEndian.BYTE_SIZE; - pos += LittleEndian.BYTE_SIZE; - return result; + _currentDataOffset += LittleEndian.BYTE_SIZE; + return _le.readUByte(); + } + public int read(byte[] b, int off, int len) { + int limit = Math.min(len, remaining()); + if (limit == 0) { + return 0; + } + readFully(b, off,limit); + return limit; } - public short getSid() { - return currentSid; - } - - public short getLength() { - return currentLength; - } - - public short getRecordOffset() { - return recordOffset; - } - - public long getPos() { - return pos; - } - - public boolean hasNextRecord() { - return nextSid != INVALID_SID_VALUE; - } + public short getSid() { + return (short) _currentSid; + } - /** Moves to the next record in the stream. - * - * Note: The auto continue flag is reset to true - */ - public void nextRecord() throws RecordFormatException { - if ((currentLength != -1) && (currentLength != recordOffset)) { - System.out.println("WARN. Unread "+remaining()+" bytes of record 0x"+Integer.toHexString(currentSid)); - } - currentSid = nextSid; - pos += LittleEndian.SHORT_SIZE; - autoContinue = true; - try { - recordOffset = 0; - currentLength = LittleEndian.readShort(in); - if (currentLength > MAX_RECORD_DATA_SIZE) - throw new RecordFormatException("The content of an excel record cannot exceed "+MAX_RECORD_DATA_SIZE+" bytes"); - pos += LittleEndian.SHORT_SIZE; - in.read(data, 0, currentLength); - - //Read the Sid of the next record - if (in.available() < EOFRecord.ENCODED_SIZE) { - if (in.available() > 0) { - // some scrap left over? - // ex45582-22397.xls has one extra byte after the last record - // Excel reads that file OK - } - nextSid = INVALID_SID_VALUE; - } else { - nextSid = LittleEndian.readShort(in); - if (nextSid == INVALID_SID_VALUE) { - throw new RecordFormatException("Found sid " + nextSid + " after record with sid 0x" - + Integer.toHexString(currentSid).toUpperCase()); - } - } - } catch (IOException ex) { - throw new RecordFormatException("Error reading bytes", ex); - } - } + /** + * Note - this method is expected to be called only when completed reading the current BIFF record. + * Calling this before reaching the end of the current record will cause all remaining data to be + * discarded + */ + public boolean hasNextRecord() { + if (_currentDataLength != -1 && _currentDataLength != _currentDataOffset) { + System.out.println("WARN. Unread "+remaining()+" bytes of record 0x"+Integer.toHexString(_currentSid)); + // discard unread data + while (_currentDataOffset < _currentDataLength) { + readByte(); + } + } + if (_currentDataLength != DATA_LEN_NEEDS_TO_BE_READ) { + _nextSid = readNextSid(); + } + return _nextSid != INVALID_SID_VALUE; + } - public void setAutoContinue(boolean enable) { - this.autoContinue = enable; - } + /** + * + * @return the sid of the next record or {@link #INVALID_SID_VALUE} if at end of stream + */ + private int readNextSid() { + int nAvailable = _le.available(); + if (nAvailable < EOFRecord.ENCODED_SIZE) { + if (nAvailable > 0) { + // some scrap left over? + // ex45582-22397.xls has one extra byte after the last record + // Excel reads that file OK + } + return INVALID_SID_VALUE; + } + int result = _le.readUShort(); + if (result == INVALID_SID_VALUE) { + throw new RecordFormatException("Found invalid sid (" + result + ")"); + } + _currentDataLength = DATA_LEN_NEEDS_TO_BE_READ; + return result; + } - public boolean getAutoContinue() { - return autoContinue; - } + /** Moves to the next record in the stream. + * + * Note: The auto continue flag is reset to true + */ + public void nextRecord() throws RecordFormatException { + if (_nextSid == INVALID_SID_VALUE) { + throw new IllegalStateException("EOF - next record not available"); + } + if (_currentDataLength != DATA_LEN_NEEDS_TO_BE_READ) { + throw new IllegalStateException("Cannot call nextRecord() without checking hasNextRecord() first"); + } + _currentSid = _nextSid; + _currentDataOffset = 0; + _currentDataLength = _le.readUShort(); + if (_currentDataLength > MAX_RECORD_DATA_SIZE) { + throw new RecordFormatException("The content of an excel record cannot exceed " + + MAX_RECORD_DATA_SIZE + " bytes"); + } + } private void checkRecordPosition(int requiredByteCount) { - if (remaining() < requiredByteCount) { - if (isContinueNext() && autoContinue) { - nextRecord(); - } else { - throw new ArrayIndexOutOfBoundsException(); - } + int nAvailable = remaining(); + if (nAvailable >= requiredByteCount) { + // all OK + return; } + if (nAvailable == 0 && isContinueNext()) { + nextRecord(); + return; + } + throw new RecordFormatException("Not enough data (" + nAvailable + + ") to read requested (" + requiredByteCount +") bytes"); } /** @@ -150,11 +182,8 @@ public final class RecordInputStream extends InputStream implements LittleEndian */ public byte readByte() { checkRecordPosition(LittleEndian.BYTE_SIZE); - - byte result = data[recordOffset]; - recordOffset += LittleEndian.BYTE_SIZE; - pos += LittleEndian.BYTE_SIZE; - return result; + _currentDataOffset += LittleEndian.BYTE_SIZE; + return _le.readByte(); } /** @@ -162,29 +191,20 @@ public final class RecordInputStream extends InputStream implements LittleEndian */ public short readShort() { checkRecordPosition(LittleEndian.SHORT_SIZE); - - short result = LittleEndian.getShort(data, recordOffset); - recordOffset += LittleEndian.SHORT_SIZE; - pos += LittleEndian.SHORT_SIZE; - return result; + _currentDataOffset += LittleEndian.SHORT_SIZE; + return _le.readShort(); } public int readInt() { checkRecordPosition(LittleEndian.INT_SIZE); - - int result = LittleEndian.getInt(data, recordOffset); - recordOffset += LittleEndian.INT_SIZE; - pos += LittleEndian.INT_SIZE; - return result; + _currentDataOffset += LittleEndian.INT_SIZE; + return _le.readInt(); } public long readLong() { checkRecordPosition(LittleEndian.LONG_SIZE); - - long result = LittleEndian.getLong(data, recordOffset); - recordOffset += LittleEndian.LONG_SIZE; - pos += LittleEndian.LONG_SIZE; - return result; + _currentDataOffset += LittleEndian.LONG_SIZE; + return _le.readLong(); } /** @@ -200,22 +220,18 @@ public final class RecordInputStream extends InputStream implements LittleEndian */ public int readUShort() { checkRecordPosition(LittleEndian.SHORT_SIZE); - - int result = LittleEndian.getUShort(data, recordOffset); - recordOffset += LittleEndian.SHORT_SIZE; - pos += LittleEndian.SHORT_SIZE; - return result; + _currentDataOffset += LittleEndian.SHORT_SIZE; + return _le.readUShort(); } public double readDouble() { checkRecordPosition(LittleEndian.DOUBLE_SIZE); - long valueLongBits = LittleEndian.getLong(data, recordOffset); + _currentDataOffset += LittleEndian.DOUBLE_SIZE; + long valueLongBits = _le.readLong(); double result = Double.longBitsToDouble(valueLongBits); if (Double.isNaN(result)) { throw new RuntimeException("Did not expect to read NaN"); // (Because Excel typically doesn't write NaN } - recordOffset += LittleEndian.DOUBLE_SIZE; - pos += LittleEndian.DOUBLE_SIZE; return result; } public void readFully(byte[] buf) { @@ -224,9 +240,8 @@ public final class RecordInputStream extends InputStream implements LittleEndian public void readFully(byte[] buf, int off, int len) { checkRecordPosition(len); - System.arraycopy(data, recordOffset, buf, off, len); - recordOffset+=len; - pos+=len; + _le.readFully(buf, off, len); + _currentDataOffset+=len; } public String readString() { @@ -315,18 +330,19 @@ public final class RecordInputStream extends InputStream implements LittleEndian return new UnicodeString(this); } - /** Returns the remaining bytes for the current record. - * - * @return The remaining bytes of the current record. - */ - public byte[] readRemainder() { - int size = remaining(); - byte[] result = new byte[size]; - System.arraycopy(data, recordOffset, result, 0, size); - recordOffset += size; - pos += size; - return result; - } + /** Returns the remaining bytes for the current record. + * + * @return The remaining bytes of the current record. + */ + public byte[] readRemainder() { + int size = remaining(); + if (size ==0) { + return EMPTY_BYTE_ARRAY; + } + byte[] result = new byte[size]; + readFully(result); + return result; + } /** Reads all byte data for the current record, including any * that overlaps into any following continue records. @@ -350,19 +366,34 @@ public final class RecordInputStream extends InputStream implements LittleEndian return out.toByteArray(); } - /** The remaining number of bytes in the current record. - * - * @return The number of bytes remaining in the current record - */ - public int remaining() { - return (currentLength - recordOffset); - } + /** The remaining number of bytes in the current record. + * + * @return The number of bytes remaining in the current record + */ + public int remaining() { + if (_currentDataLength == DATA_LEN_NEEDS_TO_BE_READ) { + // already read sid of next record. so current one is finished + return 0; + } + return _currentDataLength - _currentDataOffset; + } - /** Returns true iif a Continue record is next in the excel stream - * - * @return True when a ContinueRecord is next. - */ - public boolean isContinueNext() { - return (nextSid == ContinueRecord.sid); - } + /** + * + * @return true when a {@link ContinueRecord} is next. + */ + private boolean isContinueNext() { + if (_currentDataLength != DATA_LEN_NEEDS_TO_BE_READ && _currentDataOffset != _currentDataLength) { + throw new IllegalStateException("Should never be called before end of current record"); + } + if (!hasNextRecord()) { + return false; + } + // At what point are records continued? + // - Often from within the char data of long strings (caller is within readStringCommon()). + // - From UnicodeString construction (many different points - call via checkRecordPosition) + // - During TextObjectRecord construction (just before the text, perhaps within the text, + // and before the formatting run data) + return _nextSid == ContinueRecord.sid; + } } diff --git a/src/java/org/apache/poi/hssf/record/StyleRecord.java b/src/java/org/apache/poi/hssf/record/StyleRecord.java index 5b746e8d4b..ad740e9b2e 100644 --- a/src/java/org/apache/poi/hssf/record/StyleRecord.java +++ b/src/java/org/apache/poi/hssf/record/StyleRecord.java @@ -19,332 +19,181 @@ package org.apache.poi.hssf.record; import org.apache.poi.util.BitField; import org.apache.poi.util.BitFieldFactory; +import org.apache.poi.util.HexDump; import org.apache.poi.util.LittleEndian; import org.apache.poi.util.StringUtil; /** - * Title: Style Record

+ * Title: Style Record (0x0293)

* Description: Describes a builtin to the gui or user defined style

* REFERENCE: PG 390 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)

* @author Andrew C. Oliver (acoliver at apache dot org) * @author aviks : string fixes for UserDefined Style - * @version 2.0-pre */ public final class StyleRecord extends Record { - public final static short sid = 0x0293; - - private static final BitField fHighByte = BitFieldFactory.getInstance(0x01); - - public final static short STYLE_USER_DEFINED = 0; - public final static short STYLE_BUILT_IN = 1; - - // shared by both user defined and builtin styles - private short field_1_xf_index; // TODO: bitfield candidate - - // only for built in styles - private byte field_2_builtin_style; - private byte field_3_outline_style_level; - - // only for user defined styles - private short field_2_name_length; //OO doc says 16 bit length, so we believe - private byte field_3_string_options; - private String field_4_name; - - public StyleRecord() - { - } - - public StyleRecord(RecordInputStream in) - { - field_1_xf_index = in.readShort(); - if (getType() == STYLE_BUILT_IN) - { - field_2_builtin_style = in.readByte(); - field_3_outline_style_level = in.readByte(); - } - else if (getType() == STYLE_USER_DEFINED) - { - field_2_name_length = in.readShort(); - - // Some files from Crystal Reports lack - // the remaining fields, which is naughty - if(in.remaining() > 0) { - field_3_string_options = in.readByte(); - - byte[] string = in.readRemainder(); - if (fHighByte.isSet(field_3_string_options)) { - field_4_name= StringUtil.getFromUnicodeBE(string, 0, field_2_name_length); - } else { - field_4_name=StringUtil.getFromCompressedUnicode(string, 0, field_2_name_length); - } - } - } - - // todo sanity check exception to make sure we're one or the other - } - - /** - * set the entire index field (including the type) (see bit setters that reference this method) - * @param index bitmask - */ - - public void setIndex(short index) - { - field_1_xf_index = index; - } - - // bitfields for field 1 - - /** - * set the type of the style (builtin or user-defined) - * @see #STYLE_USER_DEFINED - * @see #STYLE_BUILT_IN - * @param type of style (userdefined/builtin) - * @see #setIndex(short) - */ - - public void setType(short type) - { - field_1_xf_index = setField(field_1_xf_index, type, 0x8000, 15); - } - - /** - * set the actual index of the style extended format record - * @see #setIndex(short) - * @param index of the xf record - */ - - public void setXFIndex(short index) - { - field_1_xf_index = setField(field_1_xf_index, index, 0x1FFF, 0); - } - - // end bitfields - // only for user defined records - - /** - * if this is a user defined record set the length of the style name - * @param length of the style's name - * @see #setName(String) - */ - - public void setNameLength(byte length) - { - field_2_name_length = length; - } - - /** - * set the style's name - * @param name of the style - * @see #setNameLength(byte) - */ - - public void setName(String name) - { - field_4_name = name; - - // Fix up the length - field_2_name_length = (short)name.length(); - //TODO set name string options - } - - // end user defined - // only for buildin records - - /** - * if this is a builtin style set teh number of the built in style - * @param builtin style number (0-7) - * - */ - - public void setBuiltin(byte builtin) - { - field_2_builtin_style = builtin; - } - - /** - * set the row or column level of the style (if builtin 1||2) - */ - - public void setOutlineStyleLevel(byte level) - { - field_3_outline_style_level = level; - } - - // end builtin records - // field 1 - - /** - * get the entire index field (including the type) (see bit getters that reference this method) - * @return bitmask - */ - - public short getIndex() - { - return field_1_xf_index; - } - - // bitfields for field 1 - - /** - * get the type of the style (builtin or user-defined) - * @see #STYLE_USER_DEFINED - * @see #STYLE_BUILT_IN - * @return type of style (userdefined/builtin) - * @see #getIndex() - */ - - public short getType() - { - return ( short ) ((field_1_xf_index & 0x8000) >> 15); - } - - /** - * get the actual index of the style extended format record - * @see #getIndex() - * @return index of the xf record - */ - - public short getXFIndex() - { - return ( short ) (field_1_xf_index & 0x1FFF); - } - - // end bitfields - // only for user defined records - - /** - * if this is a user defined record get the length of the style name - * @return length of the style's name - * @see #getName() - */ - - public short getNameLength() - { - return field_2_name_length; - } - - /** - * get the style's name - * @return name of the style - * @see #getNameLength() - */ - - public String getName() - { - return field_4_name; - } - - // end user defined - // only for buildin records - - /** - * if this is a builtin style get the number of the built in style - * @return builtin style number (0-7) - * - */ - - public byte getBuiltin() - { - return field_2_builtin_style; - } - - /** - * get the row or column level of the style (if builtin 1||2) - */ - - public byte getOutlineStyleLevel() - { - return field_3_outline_style_level; - } - - // end builtin records - public String toString() - { - StringBuffer buffer = new StringBuffer(); - - buffer.append("[STYLE]\n"); - buffer.append(" .xf_index_raw = ") - .append(Integer.toHexString(getIndex())).append("\n"); - buffer.append(" .type = ") - .append(Integer.toHexString(getType())).append("\n"); - buffer.append(" .xf_index = ") - .append(Integer.toHexString(getXFIndex())).append("\n"); - if (getType() == STYLE_BUILT_IN) - { - buffer.append(" .builtin_style = ") - .append(Integer.toHexString(getBuiltin())).append("\n"); - buffer.append(" .outline_level = ") - .append(Integer.toHexString(getOutlineStyleLevel())) - .append("\n"); - } - else if (getType() == STYLE_USER_DEFINED) - { - buffer.append(" .name_length = ") - .append(Integer.toHexString(getNameLength())).append("\n"); - buffer.append(" .name = ").append(getName()) - .append("\n"); - } - buffer.append("[/STYLE]\n"); - return buffer.toString(); - } - - private short setField(int fieldValue, int new_value, int mask, - int shiftLeft) - { - return ( short ) ((fieldValue & ~mask) - | ((new_value << shiftLeft) & mask)); - } - - public int serialize(int offset, byte [] data) - { - LittleEndian.putShort(data, 0 + offset, sid); - if (getType() == STYLE_BUILT_IN) - { - LittleEndian.putShort(data, 2 + offset, - (( short ) 0x04)); // 4 bytes (8 total) - } - else - { - LittleEndian.putShort(data, 2 + offset, - (( short ) (getRecordSize()-4))); - } - LittleEndian.putShort(data, 4 + offset, getIndex()); - if (getType() == STYLE_BUILT_IN) - { - data[ 6 + offset ] = getBuiltin(); - data[ 7 + offset ] = getOutlineStyleLevel(); - } - else - { - LittleEndian.putShort(data, 6 + offset , getNameLength()); - data[8+offset]=this.field_3_string_options; - StringUtil.putCompressedUnicode(getName(), data, 9 + offset); - } - return getRecordSize(); - } - - public int getRecordSize() - { - int retval; - - if (getType() == STYLE_BUILT_IN) - { - retval = 8; - } - else - { - if (fHighByte.isSet(field_3_string_options)) { - retval= 9+2*getNameLength(); - }else { - retval = 9 + getNameLength(); - } - } - return retval; - } - - public short getSid() - { - return sid; - } + public final static short sid = 0x0293; + + private static final BitField is16BitUnicodeFlag = BitFieldFactory.getInstance(0x01); + + private static final BitField styleIndexMask = BitFieldFactory.getInstance(0x0FFF); + private static final BitField isBuiltinFlag = BitFieldFactory.getInstance(0x8000); + + /** shared by both user defined and built-in styles */ + private int field_1_xf_index; + + // only for built in styles + private int field_2_builtin_style; + private int field_3_outline_style_level; + + // only for user defined styles + private int field_3_string_options; + private String field_4_name; + + /** + * creates a new style record, initially set to 'built-in' + */ + public StyleRecord() { + field_1_xf_index = isBuiltinFlag.set(field_1_xf_index); + } + + public StyleRecord(RecordInputStream in) { + field_1_xf_index = in.readShort(); + if (isBuiltin()) { + field_2_builtin_style = in.readByte(); + field_3_outline_style_level = in.readByte(); + } else { + int field_2_name_length = in.readShort(); + + if(in.remaining() < 1) { + // Some files from Crystal Reports lack the is16BitUnicode byte + // the remaining fields, which is naughty + if (field_2_name_length != 0) { + throw new RecordFormatException("Ran out of data reading style record"); + } + // guess this is OK if the string length is zero + field_4_name = ""; + } else { + + int is16BitUnicode = in.readByte(); + if (is16BitUnicodeFlag.isSet(is16BitUnicode)) { + field_4_name = StringUtil.readUnicodeLE(in, field_2_name_length); + } else { + field_4_name = StringUtil.readCompressedUnicode(in, field_2_name_length); + } + } + } + } + + /** + * set the actual index of the style extended format record + * @param xfIndex of the xf record + */ + public void setXFIndex(int xfIndex) { + field_1_xf_index = styleIndexMask.setValue(field_1_xf_index, xfIndex); + } + + /** + * get the actual index of the style extended format record + * @see #getIndex() + * @return index of the xf record + */ + public int getXFIndex() { + return styleIndexMask.getValue(field_1_xf_index); + } + + /** + * set the style's name + * @param name of the style + */ + public void setName(String name) { + field_4_name = name; + field_3_string_options = StringUtil.hasMultibyte(name) ? 0x01 : 0x00; + field_1_xf_index = isBuiltinFlag.clear(field_1_xf_index); + } + + /** + * if this is a builtin style set the number of the built in style + * @param builtinStyleId style number (0-7) + * + */ + public void setBuiltinStyle(int builtinStyleId) { + field_1_xf_index = isBuiltinFlag.set(field_1_xf_index); + field_2_builtin_style = builtinStyleId; + } + + /** + * set the row or column level of the style (if builtin 1||2) + */ + public void setOutlineStyleLevel(int level) { + field_3_outline_style_level = level & 0x00FF; + } + + public boolean isBuiltin(){ + return isBuiltinFlag.isSet(field_1_xf_index); + } + + /** + * get the style's name + * @return name of the style + */ + public String getName() { + return field_4_name; + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + + sb.append("[STYLE]\n"); + sb.append(" .xf_index_raw =").append(HexDump.shortToHex(field_1_xf_index)).append("\n"); + sb.append(" .type =").append(isBuiltin() ? "built-in" : "user-defined").append("\n"); + sb.append(" .xf_index =").append(HexDump.shortToHex(getXFIndex())).append("\n"); + if (isBuiltin()){ + sb.append(" .builtin_style=").append(HexDump.byteToHex(field_2_builtin_style)).append("\n"); + sb.append(" .outline_level=").append(HexDump.byteToHex(field_3_outline_style_level)).append("\n"); + } else { + sb.append(" .name =").append(getName()).append("\n"); + } + sb.append("[/STYLE]\n"); + return sb.toString(); + } + + + private int getDataSize() { + if (isBuiltin()) { + return 4; // short, byte, byte + } + int size = 2 + 3; // short + if (is16BitUnicodeFlag.isSet(field_3_string_options)) { + size += 2 * field_4_name.length(); + } else { + size += field_4_name.length(); + } + return size; + } + + public int serialize(int offset, byte [] data) { + int dataSize = getDataSize(); + LittleEndian.putShort(data, 0 + offset, sid); + LittleEndian.putUShort(data, 2 + offset, dataSize); + + LittleEndian.putUShort(data, 4 + offset, field_1_xf_index); + if (isBuiltin()) { + LittleEndian.putByte(data, 6 + offset, field_2_builtin_style); + LittleEndian.putByte(data, 7 + offset, field_3_outline_style_level); + } else { + LittleEndian.putUShort(data, 6 + offset, field_4_name.length()); + LittleEndian.putByte(data, 8 + offset, field_3_string_options); + StringUtil.putCompressedUnicode(getName(), data, 9 + offset); + } + return 4+dataSize; + } + + public int getRecordSize() { + return 4 + getDataSize(); + } + + public short getSid() + { + return sid; + } } diff --git a/src/java/org/apache/poi/hssf/record/SubRecord.java b/src/java/org/apache/poi/hssf/record/SubRecord.java index a5263b1ba9..78bcb4eee2 100644 --- a/src/java/org/apache/poi/hssf/record/SubRecord.java +++ b/src/java/org/apache/poi/hssf/record/SubRecord.java @@ -27,6 +27,7 @@ import org.apache.poi.hssf.record.formula.Ref3DPtg; import org.apache.poi.hssf.record.formula.RefPtg; import org.apache.poi.util.HexDump; import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.LittleEndianByteArrayInputStream; import org.apache.poi.util.LittleEndianInput; import org.apache.poi.util.LittleEndianOutput; import org.apache.poi.util.LittleEndianOutputStream; @@ -209,17 +210,12 @@ public abstract class SubRecord { out.writeShort(_unknownShort13); } private static Ptg readRefPtg(byte[] formulaRawBytes) { - byte[] data = new byte[formulaRawBytes.length + 4]; - LittleEndian.putUShort(data, 0, -5555); - LittleEndian.putUShort(data, 2, formulaRawBytes.length); - System.arraycopy(formulaRawBytes, 0, data, 4, formulaRawBytes.length); - RecordInputStream in = new RecordInputStream(new ByteArrayInputStream(data)); - in.nextRecord(); - byte ptgSid = in.readByte(); + LittleEndianInput in = new LittleEndianByteArrayInputStream(formulaRawBytes); + byte ptgSid = in.readByte(); switch(ptgSid) { case AreaPtg.sid: return new AreaPtg(in); case Area3DPtg.sid: return new Area3DPtg(in); - case RefPtg.sid: return new RefPtg(in); + case RefPtg.sid: return new RefPtg(in); case Ref3DPtg.sid: return new Ref3DPtg(in); } return null; diff --git a/src/java/org/apache/poi/hssf/record/SupBookRecord.java b/src/java/org/apache/poi/hssf/record/SupBookRecord.java index 30e337e2d5..78d77c5cbf 100644 --- a/src/java/org/apache/poi/hssf/record/SupBookRecord.java +++ b/src/java/org/apache/poi/hssf/record/SupBookRecord.java @@ -84,9 +84,11 @@ public final class SupBookRecord extends Record { * @param offset of the record's data (provided a big array of the file) */ public SupBookRecord(RecordInputStream in) { + int recLen = in.remaining(); + field_1_number_of_sheets = in.readShort(); - if(in.getLength() > SMALL_RECORD_SIZE) { + if(recLen > SMALL_RECORD_SIZE) { // 5.38.1 External References _isAddInFunctions = false; diff --git a/src/java/org/apache/poi/hssf/record/TextObjectRecord.java b/src/java/org/apache/poi/hssf/record/TextObjectRecord.java index 6529dc93f8..4bb065e91e 100644 --- a/src/java/org/apache/poi/hssf/record/TextObjectRecord.java +++ b/src/java/org/apache/poi/hssf/record/TextObjectRecord.java @@ -25,6 +25,8 @@ import org.apache.poi.util.BitField; import org.apache.poi.util.BitFieldFactory; import org.apache.poi.util.HexDump; import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.LittleEndianByteArrayOutputStream; +import org.apache.poi.util.LittleEndianOutput; /** * The TXO record (0x01B6) is used to define the properties of a text box. It is @@ -129,13 +131,7 @@ public final class TextObjectRecord extends Record { _text = new HSSFRichTextString(text); if (field_7_formattingDataLength > 0) { - if (in.isContinueNext() && in.remaining() == 0) { - in.nextRecord(); - processFontRuns(in, _text, field_7_formattingDataLength); - } else { - throw new RecordFormatException( - "Expected Continue Record to hold font runs for TextObjectRecord"); - } + processFontRuns(in, _text, field_7_formattingDataLength); } } @@ -154,10 +150,6 @@ public final class TextObjectRecord extends Record { throw new RecordFormatException("Bad format run data length " + formattingRunDataLength + ")"); } - if (in.remaining() != formattingRunDataLength) { - throw new RecordFormatException("Expected " + formattingRunDataLength - + " bytes but got " + in.remaining()); - } int nRuns = formattingRunDataLength / FORMAT_RUN_ENCODED_SIZE; for (int i = 0; i < nRuns; i++) { short index = in.readShort(); @@ -190,36 +182,31 @@ public final class TextObjectRecord extends Record { private int serializeTXORecord(int offset, byte[] data) { int dataSize = getDataSize(); + int recSize = dataSize+4; + LittleEndianOutput out = new LittleEndianByteArrayOutputStream(data, offset, recSize); - LittleEndian.putUShort(data, 0 + offset, TextObjectRecord.sid); - LittleEndian.putUShort(data, 2 + offset, dataSize); - + out.writeShort(TextObjectRecord.sid); + out.writeShort(dataSize); - LittleEndian.putUShort(data, 4 + offset, field_1_options); - LittleEndian.putUShort(data, 6 + offset, field_2_textOrientation); - LittleEndian.putUShort(data, 8 + offset, field_3_reserved4); - LittleEndian.putUShort(data, 10 + offset, field_4_reserved5); - LittleEndian.putUShort(data, 12 + offset, field_5_reserved6); - LittleEndian.putUShort(data, 14 + offset, _text.length()); - LittleEndian.putUShort(data, 16 + offset, getFormattingDataLength()); - LittleEndian.putInt(data, 18 + offset, field_8_reserved7); + out.writeShort(field_1_options); + out.writeShort(field_2_textOrientation); + out.writeShort(field_3_reserved4); + out.writeShort(field_4_reserved5); + out.writeShort(field_5_reserved6); + out.writeShort(_text.length()); + out.writeShort(getFormattingDataLength()); + out.writeInt(field_8_reserved7); if (_linkRefPtg != null) { - int pos = offset+22; int formulaSize = _linkRefPtg.getSize(); - LittleEndian.putUShort(data, pos, formulaSize); - pos += LittleEndian.SHORT_SIZE; - LittleEndian.putInt(data, pos, _unknownPreFormulaInt); - pos += LittleEndian.INT_SIZE; - _linkRefPtg.writeBytes(data, pos); - pos += formulaSize; + out.writeShort(formulaSize); + out.writeInt(_unknownPreFormulaInt); + _linkRefPtg.write(out); if (_unknownPostFormulaByte != null) { - LittleEndian.putByte(data, pos, _unknownPostFormulaByte.byteValue()); - pos += LittleEndian.BYTE_SIZE; + out.writeByte(_unknownPostFormulaByte.byteValue()); } } - - return 4 + dataSize; + return recSize; } private int serializeTrailingRecords(int offset, byte[] data) { diff --git a/src/java/org/apache/poi/hssf/record/UnicodeString.java b/src/java/org/apache/poi/hssf/record/UnicodeString.java index 4258d0a6d5..0494aa98ab 100644 --- a/src/java/org/apache/poi/hssf/record/UnicodeString.java +++ b/src/java/org/apache/poi/hssf/record/UnicodeString.java @@ -36,13 +36,8 @@ import java.util.Collections; * @author Andrew C. Oliver * @author Marc Johnson (mjohnson at apache dot org) * @author Glen Stampoultzis (glens at apache.org) - * @version 2.0-pre */ - -public class UnicodeString - implements Comparable -{ - public final static short sid = 0xFFF; +public final class UnicodeString implements Comparable { private short field_1_charCount; // = 0; private byte field_2_optionflags; // = 0; private String field_3_string; // = null; @@ -53,8 +48,8 @@ public class UnicodeString private static final BitField richText = BitFieldFactory.getInstance(0x8); public static class FormatRun implements Comparable { - private short character; - private short fontIndex; + short character; + short fontIndex; public FormatRun(short character, short fontIndex) { this.character = character; @@ -102,15 +97,6 @@ public class UnicodeString setString(str); } - /** - * construct a unicode string record and fill its fields, ID is ignored - * @param in the RecordInputstream to read the record from - */ - - public UnicodeString(RecordInputStream in) - { - fillFields(in); // TODO - inline - } public int hashCode() @@ -142,9 +128,9 @@ public class UnicodeString && field_3_string.equals(other.field_3_string)); if (!eq) return false; - //Ok string appears to be equal but now lets compare formatting runs + //OK string appears to be equal but now lets compare formatting runs if ((field_4_format_runs == null) && (other.field_4_format_runs == null)) - //Strings are equal, and there are not formtting runs. + //Strings are equal, and there are not formatting runs. return true; if (((field_4_format_runs == null) && (other.field_4_format_runs != null)) || (field_4_format_runs != null) && (other.field_4_format_runs == null)) @@ -186,10 +172,10 @@ public class UnicodeString } /** + * construct a unicode string record and fill its fields, ID is ignored * @param in the RecordInputstream to read the record from */ - protected void fillFields(RecordInputStream in) - { + public UnicodeString(RecordInputStream in) { field_1_charCount = in.readShort(); field_2_optionflags = in.readByte(); @@ -206,35 +192,13 @@ public class UnicodeString extensionLength = in.readInt(); } - //Now need to get the string data. - //Turn off autocontinuation so that we can catch the continue boundary - in.setAutoContinue(false); - StringBuffer tmpString = new StringBuffer(field_1_charCount); - int stringCharCount = field_1_charCount; boolean isCompressed = ((field_2_optionflags & 1) == 0); - while (stringCharCount != 0) { - if (in.remaining() == 0) { - if (in.isContinueNext()) { - in.nextRecord(); - //Check if we are now reading, compressed or uncompressed unicode. - byte optionflags = in.readByte(); - isCompressed = ((optionflags & 1) == 0); - } else - throw new RecordFormatException("Expected continue record."); - } - if (isCompressed) { - char ch = (char)in.readUByte(); // avoid sex - tmpString.append(ch); - } else { - char ch = (char) in.readShort(); - tmpString.append(ch); - } - stringCharCount --; + if (isCompressed) { + field_3_string = in.readCompressedUnicode(field_1_charCount); + } else { + field_3_string = in.readUnicodeLEString(field_1_charCount); } - field_3_string = tmpString.toString(); - //Turn back on autocontinuation - in.setAutoContinue(true); - + if (isRichText() && (runCount > 0)) { field_4_format_runs = new ArrayList(runCount); @@ -305,13 +269,8 @@ public class UnicodeString } /** - * get the actual string this contains as a java String object - * - * - * @return String - * + * @return the actual string this contains as a java String object */ - public String getString() { return field_3_string; @@ -341,7 +300,7 @@ public class UnicodeString } } if (useUTF16) - //Set the uncomressed bit + //Set the uncompressed bit field_2_optionflags = highByte.setByte(field_2_optionflags); else field_2_optionflags = highByte.clearByte(field_2_optionflags); } @@ -392,7 +351,7 @@ public class UnicodeString //Make sure that we now say that we are a rich string field_2_optionflags = richText.setByte(field_2_optionflags); - } + } public Iterator formatIterator() { if (field_4_format_runs != null) @@ -497,8 +456,8 @@ public class UnicodeString LittleEndian.putShort(data, offset, ContinueRecord.sid); offset+=2; - //Record the location of the last continue legnth position, but dont write - //anything there yet (since we dont know what it will be!) + //Record the location of the last continue length position, but don't write + //anything there yet (since we don't know what it will be!) stats.lastLengthPos = offset; offset += 2; @@ -506,7 +465,7 @@ public class UnicodeString stats.remainingSize = SSTRecord.MAX_RECORD_SIZE-4; } return offset; - } + } public int serialize(UnicodeRecordStats stats, final int offset, byte [] data) { @@ -514,7 +473,6 @@ public class UnicodeString //Basic string overhead pos = writeContinueIfRequired(stats, 3, pos, data); - // byte[] retval = new byte[ 3 + (getString().length() * charsize)]; LittleEndian.putShort(data, pos, getCharCount()); pos += 2; data[ pos ] = getOptionFlags(); @@ -568,39 +526,39 @@ public class UnicodeString //Check to see if the offset occurs mid string, if so then we need to add //the byte to start with that represents the first byte of the continue record. if (strSize > stats.remainingSize) { - //Ok the offset occurs half way through the string, that means that + //OK the offset occurs half way through the string, that means that //we need an extra byte after the continue record ie we didnt finish //writing out the string the 1st time through //But hang on, how many continue records did we span? What if this is //a REALLY long string. We need to work this all out. - int ammountThatCantFit = strSize; + int amountThatCantFit = strSize; int strPos = 0; - while (ammountThatCantFit > 0) { - int ammountWritten = Math.min(stats.remainingSize, ammountThatCantFit); - //Make sure that the ammount that cant fit takes into account + while (amountThatCantFit > 0) { + int amountWritten = Math.min(stats.remainingSize, amountThatCantFit); + //Make sure that the amount that can't fit takes into account //whether we are writing double byte unicode if (isUncompressedUnicode()) { //We have the '-1' here because whether this is the first record or //subsequent continue records, there is always the case that the - //number of bytes in a string on doube byte boundaries is actually odd. - if ( ( (ammountWritten ) % 2) == 1) - ammountWritten--; + //number of bytes in a string on double byte boundaries is actually odd. + if ( ( (amountWritten ) % 2) == 1) + amountWritten--; } - System.arraycopy(strBytes, strPos, data, pos, ammountWritten); - pos += ammountWritten; - strPos += ammountWritten; - stats.recordSize += ammountWritten; - stats.remainingSize -= ammountWritten; + System.arraycopy(strBytes, strPos, data, pos, amountWritten); + pos += amountWritten; + strPos += amountWritten; + stats.recordSize += amountWritten; + stats.remainingSize -= amountWritten; //Ok lets subtract what we can write - ammountThatCantFit -= ammountWritten; + amountThatCantFit -= amountWritten; //Each iteration of this while loop is another continue record, unless //everything now fits. - if (ammountThatCantFit > 0) { + if (amountThatCantFit > 0) { //We know that a continue WILL be requied, but use this common method - pos = writeContinueIfRequired(stats, ammountThatCantFit, pos, data); + pos = writeContinueIfRequired(stats, amountThatCantFit, pos, data); //The first byte after a continue mid string is the extra byte to //indicate if this run is compressed or not. @@ -686,7 +644,7 @@ public class UnicodeString return highByte.isSet(getOptionFlags()); } - /** Returns the size of this record, given the ammount of record space + /** Returns the size of this record, given the amount of record space * remaining, it will also include the size of writing a continue record. */ @@ -833,13 +791,6 @@ public class UnicodeString } } - - - public short getSid() - { - return sid; - } - public int compareTo(Object obj) { UnicodeString str = ( UnicodeString ) obj; @@ -877,7 +828,7 @@ public class UnicodeString } //Well the format runs are equal as well!, better check the ExtRst data - //Which by the way we dont know how to decode! + //Which by the way we don't know how to decode! if ((field_5_ext_rst == null) && (str.field_5_ext_rst == null)) return 0; if ((field_5_ext_rst == null) && (str.field_5_ext_rst != null)) diff --git a/src/java/org/apache/poi/hssf/record/WriteAccessRecord.java b/src/java/org/apache/poi/hssf/record/WriteAccessRecord.java index d73687f186..6b005c7f44 100644 --- a/src/java/org/apache/poi/hssf/record/WriteAccessRecord.java +++ b/src/java/org/apache/poi/hssf/record/WriteAccessRecord.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -15,111 +14,130 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - package org.apache.poi.hssf.record; +import java.util.Arrays; + import org.apache.poi.util.LittleEndian; import org.apache.poi.util.StringUtil; /** - * Title: Write Access Record

- * Description: Stores the username of that who owns the spreadsheet generator - * (on unix the user's login, on Windoze its the name you typed when - * you installed the thing)

- * REFERENCE: PG 424 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)

+ * Title: Write Access Record (0x005C)

+ * + * Description: Stores the username of that who owns the spreadsheet generator (on unix the user's + * login, on Windoze its the name you typed when you installed the thing) + *

+ * REFERENCE: PG 424 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2) + *

+ * * @author Andrew C. Oliver (acoliver at apache dot org) - * @version 2.0-pre */ - -public class WriteAccessRecord - extends Record -{ - public final static short sid = 0x5c; - private String field_1_username; - - public WriteAccessRecord() - { - } - - public WriteAccessRecord(RecordInputStream in) - { - byte[] data = in.readRemainder(); - //The string is always 112 characters (padded with spaces), therefore - //this record can not be continued. - - //What a wierd record, it is not really a unicode string because the - //header doesnt provide a correct size indication.??? - //But the header is present, so we need to skip over it. - //Odd, Odd, Odd ;-) - field_1_username = StringUtil.getFromCompressedUnicode(data, 3, data.length - 3); - } - - /** - * set the username for the user that created the report. HSSF uses the logged in user. - * @param username of the user who is logged in (probably "tomcat" or "apache") - */ - - public void setUsername(String username) - { - field_1_username = username; - } - - /** - * get the username for the user that created the report. HSSF uses the logged in user. On - * natively created M$ Excel sheet this would be the name you typed in when you installed it - * in most cases. - * @return username of the user who is logged in (probably "tomcat" or "apache") - */ - - public String getUsername() - { - return field_1_username; - } - - public String toString() - { - StringBuffer buffer = new StringBuffer(); - - buffer.append("[WRITEACCESS]\n"); - buffer.append(" .name = ") - .append(field_1_username.toString()).append("\n"); - buffer.append("[/WRITEACCESS]\n"); - return buffer.toString(); - } - - public int serialize(int offset, byte [] data) - { - String username = getUsername(); - StringBuffer temp = new StringBuffer(0x70 - (0x3)); - - temp.append(username); - while (temp.length() < 0x70 - 0x3) - { - temp.append( - " "); // (70 = fixed lenght -3 = the overhead bits of unicode string) - } - username = temp.toString(); - UnicodeString str = new UnicodeString(username); - str.setOptionFlags(( byte ) 0x0); - - LittleEndian.putShort(data, 0 + offset, sid); - LittleEndian.putShort(data, 2 + offset, (short)112); // 112 bytes (115 total) - UnicodeString.UnicodeRecordStats stats = new UnicodeString.UnicodeRecordStats(); - stats.recordSize += 4; - stats.remainingSize-= 4; - str.serialize(stats, 4 + offset, data); - - return getRecordSize(); - } - - public int getRecordSize() - { - return 116; - } - - public short getSid() - { - return sid; - } +public final class WriteAccessRecord extends Record { + private static final byte PAD_CHAR = (byte) ' '; + public final static short sid = 0x005C; + private static final int DATA_SIZE = 112; + private String field_1_username; + /** this record is always padded to a constant length */ + private byte[] padding; + + public WriteAccessRecord() { + setUsername(""); + padding = new byte[DATA_SIZE - 3]; + } + + public WriteAccessRecord(RecordInputStream in) { + if (in.remaining() > DATA_SIZE) { + throw new RecordFormatException("Expected data size (" + DATA_SIZE + ") but got (" + + in.remaining() + ")"); + } + // The string is always 112 characters (padded with spaces), therefore + // this record can not be continued. + + int nChars = in.readUShort(); + int is16BitFlag = in.readUByte(); + int expectedPadSize = DATA_SIZE - 3; + if ((is16BitFlag & 0x01) == 0x00) { + field_1_username = StringUtil.readCompressedUnicode(in, nChars); + expectedPadSize -= nChars; + } else { + field_1_username = StringUtil.readUnicodeLE(in, nChars); + expectedPadSize -= nChars * 2; + } + padding = new byte[expectedPadSize]; + int padSize = in.remaining(); + in.readFully(padding, 0, padSize); + if (padSize < expectedPadSize) { + // this occurs in a couple of test examples: "42564.xls", + // "bug_42794.xls" + Arrays.fill(padding, padSize, expectedPadSize, PAD_CHAR); + } + } + + /** + * set the username for the user that created the report. HSSF uses the + * logged in user. + * + * @param username of the user who is logged in (probably "tomcat" or "apache") + */ + public void setUsername(String username) { + boolean is16bit = StringUtil.hasMultibyte(username); + int encodedByteCount = 3 + username.length() * (is16bit ? 2 : 1); + int paddingSize = DATA_SIZE - encodedByteCount; + if (paddingSize < 0) { + throw new IllegalArgumentException("Name is too long: " + username); + } + padding = new byte[paddingSize]; + Arrays.fill(padding, PAD_CHAR); + + field_1_username = username; + } + + /** + * get the username for the user that created the report. HSSF uses the + * logged in user. On natively created M$ Excel sheet this would be the name + * you typed in when you installed it in most cases. + * + * @return username of the user who is logged in (probably "tomcat" or "apache") + */ + public String getUsername() { + return field_1_username; + } + + public String toString() { + StringBuffer buffer = new StringBuffer(); + + buffer.append("[WRITEACCESS]\n"); + buffer.append(" .name = ").append(field_1_username.toString()).append("\n"); + buffer.append("[/WRITEACCESS]\n"); + return buffer.toString(); + } + + public int serialize(int offset, byte[] data) { + String username = getUsername(); + boolean is16bit = StringUtil.hasMultibyte(username); + + LittleEndian.putUShort(data, 0 + offset, sid); + LittleEndian.putUShort(data, 2 + offset, DATA_SIZE); + LittleEndian.putUShort(data, 4 + offset, username.length()); + LittleEndian.putByte(data, 6 + offset, is16bit ? 0x01 : 0x00); + int pos = offset + 7; + if (is16bit) { + StringUtil.putUnicodeLE(username, data, pos); + pos += username.length() * 2; + } else { + StringUtil.putCompressedUnicode(username, data, pos); + pos += username.length(); + } + System.arraycopy(padding, 0, data, pos, padding.length); + return 4 + DATA_SIZE; + } + + public int getRecordSize() { + return 4 + DATA_SIZE; + } + + public short getSid() { + return sid; + } } diff --git a/src/java/org/apache/poi/hssf/record/constant/ConstantValueParser.java b/src/java/org/apache/poi/hssf/record/constant/ConstantValueParser.java index 12f26bfdee..8304eb0ee3 100755 --- a/src/java/org/apache/poi/hssf/record/constant/ConstantValueParser.java +++ b/src/java/org/apache/poi/hssf/record/constant/ConstantValueParser.java @@ -17,10 +17,11 @@ package org.apache.poi.hssf.record.constant; -import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.hssf.record.UnicodeString; import org.apache.poi.hssf.record.UnicodeString.UnicodeRecordStats; -import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.LittleEndianInput; +import org.apache.poi.util.LittleEndianOutput; +import org.apache.poi.util.StringUtil; /** * To support Constant Values (2.5.7) as required by the CRN record. @@ -47,7 +48,7 @@ public final class ConstantValueParser { // no instances of this class } - public static Object[] parse(RecordInputStream in, int nValues) { + public static Object[] parse(LittleEndianInput in, int nValues) { Object[] result = new Object[nValues]; for (int i = 0; i < result.length; i++) { result[i] = readAConstantValue(in); @@ -55,7 +56,7 @@ public final class ConstantValueParser { return result; } - private static Object readAConstantValue(RecordInputStream in) { + private static Object readAConstantValue(LittleEndianInput in) { byte grbit = in.readByte(); switch(grbit) { case TYPE_EMPTY: @@ -64,7 +65,7 @@ public final class ConstantValueParser { case TYPE_NUMBER: return new Double(in.readDouble()); case TYPE_STRING: - return in.readUnicodeString(); + return new UnicodeString(StringUtil.readUnicodeString(in)); case TYPE_BOOLEAN: return readBoolean(in); case TYPE_ERROR_CODE: @@ -77,7 +78,7 @@ public final class ConstantValueParser { throw new RuntimeException("Unknown grbit value (" + grbit + ")"); } - private static Object readBoolean(RecordInputStream in) { + private static Object readBoolean(LittleEndianInput in) { byte val = (byte)in.readLong(); // 7 bytes 'not used' switch(val) { case FALSE_ENCODING: @@ -116,46 +117,43 @@ public final class ConstantValueParser { return urs.recordSize; } - public static void encode(byte[] data, int offset, Object[] values) { - int currentOffset = offset; + public static void encode(LittleEndianOutput out, Object[] values) { for (int i = 0; i < values.length; i++) { - currentOffset += encodeSingleValue(data, currentOffset, values[i]); + encodeSingleValue(out, values[i]); } } - private static int encodeSingleValue(byte[] data, int offset, Object value) { + private static void encodeSingleValue(LittleEndianOutput out, Object value) { if (value == EMPTY_REPRESENTATION) { - LittleEndian.putByte(data, offset, TYPE_EMPTY); - LittleEndian.putLong(data, offset+1, 0L); - return 9; + out.writeByte(TYPE_EMPTY); + out.writeLong(0L); + return; } if (value instanceof Boolean) { Boolean bVal = ((Boolean)value); - LittleEndian.putByte(data, offset, TYPE_BOOLEAN); + out.writeByte(TYPE_BOOLEAN); long longVal = bVal.booleanValue() ? 1L : 0L; - LittleEndian.putLong(data, offset+1, longVal); - return 9; + out.writeLong(longVal); + return; } if (value instanceof Double) { Double dVal = (Double) value; - LittleEndian.putByte(data, offset, TYPE_NUMBER); - LittleEndian.putDouble(data, offset+1, dVal.doubleValue()); - return 9; + out.writeByte(TYPE_NUMBER); + out.writeDouble(dVal.doubleValue()); + return; } if (value instanceof UnicodeString) { UnicodeString usVal = (UnicodeString) value; - LittleEndian.putByte(data, offset, TYPE_STRING); - UnicodeRecordStats urs = new UnicodeRecordStats(); - usVal.serialize(urs, offset +1, data); - return 1 + urs.recordSize; + out.writeByte(TYPE_STRING); + StringUtil.writeUnicodeString(out, usVal.getString()); + return; } if (value instanceof ErrorConstant) { ErrorConstant ecVal = (ErrorConstant) value; - LittleEndian.putByte(data, offset, TYPE_ERROR_CODE); - LittleEndian.putUShort(data, offset+1, ecVal.getErrorCode()); - LittleEndian.putUShort(data, offset+3, 0); - LittleEndian.putInt(data, offset+5, 0); - return 9; + out.writeByte(TYPE_ERROR_CODE); + long longVal = ecVal.getErrorCode(); + out.writeLong(longVal); + return; } throw new IllegalStateException("Unexpected value type (" + value.getClass().getName() + "'"); diff --git a/src/java/org/apache/poi/hssf/record/formula/AbstractFunctionPtg.java b/src/java/org/apache/poi/hssf/record/formula/AbstractFunctionPtg.java index 037ebb83ae..8e1f9ffe93 100644 --- a/src/java/org/apache/poi/hssf/record/formula/AbstractFunctionPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/AbstractFunctionPtg.java @@ -98,7 +98,6 @@ public abstract class AbstractFunctionPtg extends OperationPtg { buf.append(")"); } - public abstract void writeBytes(byte[] array, int offset); public abstract int getSize(); diff --git a/src/java/org/apache/poi/hssf/record/formula/Area2DPtgBase.java b/src/java/org/apache/poi/hssf/record/formula/Area2DPtgBase.java index f16a200779..7802fcd13f 100644 --- a/src/java/org/apache/poi/hssf/record/formula/Area2DPtgBase.java +++ b/src/java/org/apache/poi/hssf/record/formula/Area2DPtgBase.java @@ -17,42 +17,48 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.hssf.record.RecordInputStream; -import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.LittleEndianInput; +import org.apache.poi.util.LittleEndianOutput; /** - * Common superclass of 2-D area refs + * Common superclass of 2-D area refs */ public abstract class Area2DPtgBase extends AreaPtgBase { - private final static int SIZE = 9; + private final static int SIZE = 9; protected Area2DPtgBase(int firstRow, int lastRow, int firstColumn, int lastColumn, boolean firstRowRelative, boolean lastRowRelative, boolean firstColRelative, boolean lastColRelative) { super(firstRow, lastRow, firstColumn, lastColumn, firstRowRelative, lastRowRelative, firstColRelative, lastColRelative); } - protected Area2DPtgBase(RecordInputStream in) { + + protected Area2DPtgBase(LittleEndianInput in) { readCoordinates(in); } + protected abstract byte getSid(); - public final void writeBytes(byte [] array, int offset) { - LittleEndian.putByte(array, offset+0, getSid() + getPtgClass()); - writeCoordinates(array, offset+1); + public final void write(LittleEndianOutput out) { + out.writeByte(getSid() + getPtgClass()); + writeCoordinates(out); } + public Area2DPtgBase(String arearef) { - super(arearef); + super(arearef); } + public final int getSize() { return SIZE; } + public final String toFormulaString() { - return formatReferenceAsString(); + return formatReferenceAsString(); + } + + public final String toString() { + StringBuffer sb = new StringBuffer(); + sb.append(getClass().getName()); + sb.append(" ["); + sb.append(formatReferenceAsString()); + sb.append("]"); + return sb.toString(); } - public final String toString() { - StringBuffer sb = new StringBuffer(); - sb.append(getClass().getName()); - sb.append(" ["); - sb.append(formatReferenceAsString()); - sb.append("]"); - return sb.toString(); - } } diff --git a/src/java/org/apache/poi/hssf/record/formula/Area3DPtg.java b/src/java/org/apache/poi/hssf/record/formula/Area3DPtg.java index 58cdc5b275..675ee45551 100644 --- a/src/java/org/apache/poi/hssf/record/formula/Area3DPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/Area3DPtg.java @@ -17,11 +17,11 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.ss.formula.ExternSheetReferenceToken; import org.apache.poi.ss.formula.FormulaRenderingWorkbook; import org.apache.poi.ss.formula.WorkbookDependentFormula; -import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.LittleEndianInput; +import org.apache.poi.util.LittleEndianOutput; /** * Title: Area 3D Ptg - 3D reference (Sheet + Area)

@@ -44,7 +44,7 @@ public final class Area3DPtg extends AreaPtgBase implements WorkbookDependentFor setExternSheetIndex( externIdx ); } - public Area3DPtg(RecordInputStream in) { + public Area3DPtg(LittleEndianInput in) { field_1_index_extern_sheet = in.readShort(); readCoordinates(in); } @@ -67,10 +67,10 @@ public final class Area3DPtg extends AreaPtgBase implements WorkbookDependentFor return sb.toString(); } - public void writeBytes(byte[] array, int offset) { - LittleEndian.putByte(array, offset + 0, sid + getPtgClass()); - LittleEndian.putUShort(array, 1 + offset, field_1_index_extern_sheet); - writeCoordinates(array, offset+3); + public void write(LittleEndianOutput out) { + out.writeByte(sid + getPtgClass()); + out.writeShort(field_1_index_extern_sheet); + writeCoordinates(out); } public int getSize() { diff --git a/src/java/org/apache/poi/hssf/record/formula/AreaErrPtg.java b/src/java/org/apache/poi/hssf/record/formula/AreaErrPtg.java index bfe247897d..dfbfb7ab32 100644 --- a/src/java/org/apache/poi/hssf/record/formula/AreaErrPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/AreaErrPtg.java @@ -17,9 +17,9 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.hssf.usermodel.HSSFErrorConstants; -import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.LittleEndianInput; +import org.apache.poi.util.LittleEndianOutput; /** * AreaErr - handles deleted cell area references. @@ -36,16 +36,16 @@ public final class AreaErrPtg extends OperandPtg { unused2 = 0; } - public AreaErrPtg(RecordInputStream in) { + public AreaErrPtg(LittleEndianInput in) { // 8 bytes unused: unused1 = in.readInt(); unused2 = in.readInt(); } - public void writeBytes(byte[] array, int offset) { - LittleEndian.putByte(array, offset + 0, sid + getPtgClass()); - LittleEndian.putInt(array, offset + 1, unused1); - LittleEndian.putInt(array, offset + 5, unused2); + public void write(LittleEndianOutput out) { + out.writeByte(sid + getPtgClass()); + out.writeInt(unused1); + out.writeInt(unused2); } public String toFormulaString() { diff --git a/src/java/org/apache/poi/hssf/record/formula/AreaNPtg.java b/src/java/org/apache/poi/hssf/record/formula/AreaNPtg.java index fc391b801d..f8b761f315 100644 --- a/src/java/org/apache/poi/hssf/record/formula/AreaNPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/AreaNPtg.java @@ -17,7 +17,7 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.hssf.record.RecordInputStream; +import org.apache.poi.util.LittleEndianInput; /** * Specifies a rectangular area of cells A1:A4 for instance. @@ -26,7 +26,7 @@ import org.apache.poi.hssf.record.RecordInputStream; public final class AreaNPtg extends Area2DPtgBase { public final static short sid = 0x2D; - public AreaNPtg(RecordInputStream in) { + public AreaNPtg(LittleEndianInput in) { super(in); } diff --git a/src/java/org/apache/poi/hssf/record/formula/AreaPtg.java b/src/java/org/apache/poi/hssf/record/formula/AreaPtg.java index 77e9fa0b5e..1da9b784c8 100755 --- a/src/java/org/apache/poi/hssf/record/formula/AreaPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/AreaPtg.java @@ -17,7 +17,7 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.hssf.record.RecordInputStream; +import org.apache.poi.util.LittleEndianInput; /** * Specifies a rectangular area of cells A1:A4 for instance. @@ -29,7 +29,7 @@ public final class AreaPtg extends Area2DPtgBase { public AreaPtg(int firstRow, int lastRow, int firstColumn, int lastColumn, boolean firstRowRelative, boolean lastRowRelative, boolean firstColRelative, boolean lastColRelative) { super(firstRow, lastRow, firstColumn, lastColumn, firstRowRelative, lastRowRelative, firstColRelative, lastColRelative); } - public AreaPtg(RecordInputStream in) { + public AreaPtg(LittleEndianInput in) { super(in); } public AreaPtg(String arearef) { diff --git a/src/java/org/apache/poi/hssf/record/formula/AreaPtgBase.java b/src/java/org/apache/poi/hssf/record/formula/AreaPtgBase.java index a97ecd4f03..d6f2b44aaf 100644 --- a/src/java/org/apache/poi/hssf/record/formula/AreaPtgBase.java +++ b/src/java/org/apache/poi/hssf/record/formula/AreaPtgBase.java @@ -17,12 +17,12 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.ss.util.AreaReference; import org.apache.poi.ss.util.CellReference; import org.apache.poi.util.BitField; import org.apache.poi.util.BitFieldFactory; -import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.LittleEndianInput; +import org.apache.poi.util.LittleEndianOutput; /** * Specifies a rectangular area of cells A1:A4 for instance. @@ -46,7 +46,7 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { private int field_3_first_column; /** zero based, unsigned 8 bit */ private int field_4_last_column; - + private final static BitField rowRelative = BitFieldFactory.getInstance(0x8000); private final static BitField colRelative = BitFieldFactory.getInstance(0x4000); private final static BitField columnMask = BitFieldFactory.getInstance(0x3FFF); @@ -54,7 +54,7 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { protected AreaPtgBase() { // do nothing } - + protected AreaPtgBase(String arearef) { AreaReference ar = new AreaReference(arearef); CellReference firstCell = ar.getFirstCell(); @@ -66,17 +66,17 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { setFirstColRelative(!firstCell.isColAbsolute()); setLastColRelative(!lastCell.isColAbsolute()); setFirstRowRelative(!firstCell.isRowAbsolute()); - setLastRowRelative(!lastCell.isRowAbsolute()); + setLastRowRelative(!lastCell.isRowAbsolute()); } - + protected AreaPtgBase(int firstRow, int lastRow, int firstColumn, int lastColumn, boolean firstRowRelative, boolean lastRowRelative, boolean firstColRelative, boolean lastColRelative) { - + checkColumnBounds(firstColumn); checkColumnBounds(lastColumn); checkRowBounds(firstRow); checkRowBounds(lastRow); - + if (lastRow > firstRow) { setFirstRow(firstRow); setLastRow(lastRow); @@ -88,7 +88,7 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { setFirstRowRelative(lastRowRelative); setLastRowRelative(firstRowRelative); } - + if (lastColumn > firstColumn) { setFirstColumn(firstColumn); setLastColumn(lastColumn); @@ -100,7 +100,7 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { setFirstColRelative(lastColRelative); setLastColRelative(firstColRelative); } - } + } private static void checkColumnBounds(int colIx) { if((colIx & 0x0FF) != colIx) { @@ -113,17 +113,17 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { } } - protected final void readCoordinates(RecordInputStream in) { + protected final void readCoordinates(LittleEndianInput in) { field_1_first_row = in.readUShort(); field_2_last_row = in.readUShort(); field_3_first_column = in.readUShort(); field_4_last_column = in.readUShort(); } - protected final void writeCoordinates(byte[] array, int offset) { - LittleEndian.putUShort(array, offset + 0, field_1_first_row); - LittleEndian.putUShort(array, offset + 2, field_2_last_row); - LittleEndian.putUShort(array, offset + 4, field_3_first_column); - LittleEndian.putUShort(array, offset + 6, field_4_last_column); + protected final void writeCoordinates(LittleEndianOutput out) { + out.writeShort(field_1_first_row); + out.writeShort(field_2_last_row); + out.writeShort(field_3_first_column); + out.writeShort(field_4_last_column); } /** @@ -150,7 +150,7 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { } /** - * @param rowIx last row number in the area + * @param rowIx last row number in the area */ public final void setLastRow(int rowIx) { checkRowBounds(rowIx); @@ -177,7 +177,7 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { public final boolean isFirstRowRelative() { return rowRelative.isSet(field_3_first_column); } - + /** * sets the first row to relative or not * @param rel is relative or not. @@ -192,9 +192,9 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { public final boolean isFirstColRelative() { return colRelative.isSet(field_3_first_column); } - + /** - * set whether the first column is relative + * set whether the first column is relative */ public final void setFirstColRelative(boolean rel) { field_3_first_column=colRelative.setBoolean(field_3_first_column,rel); @@ -235,7 +235,7 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { public final boolean isLastRowRelative() { return rowRelative.isSet(field_4_last_column); } - + /** * set whether the last row is relative or not * @param rel true if the last row relative, else @@ -251,14 +251,14 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { public final boolean isLastColRelative() { return colRelative.isSet(field_4_last_column); } - + /** * set whether the last column should be relative or not */ public final void setLastColRelative(boolean rel) { field_4_last_column=colRelative.setBoolean(field_4_last_column,rel); } - + /** * set the last column in the area */ @@ -276,13 +276,13 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { protected final String formatReferenceAsString() { CellReference topLeft = new CellReference(getFirstRow(),getFirstColumn(),!isFirstRowRelative(),!isFirstColRelative()); CellReference botRight = new CellReference(getLastRow(),getLastColumn(),!isLastRowRelative(),!isLastColRelative()); - + if(AreaReference.isWholeColumnReference(topLeft, botRight)) { return (new AreaReference(topLeft, botRight)).formatAsString(); } - return topLeft.formatAsString() + ":" + botRight.formatAsString(); + return topLeft.formatAsString() + ":" + botRight.formatAsString(); } - + public String toFormulaString() { return formatReferenceAsString(); } diff --git a/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java b/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java index 1b025872d4..3fcc23eda0 100644 --- a/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java @@ -17,11 +17,11 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.hssf.record.UnicodeString; import org.apache.poi.hssf.record.constant.ConstantValueParser; import org.apache.poi.hssf.record.constant.ErrorConstant; -import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.LittleEndianInput; +import org.apache.poi.util.LittleEndianOutput; /** * ArrayPtg - handles arrays @@ -54,7 +54,7 @@ public final class ArrayPtg extends Ptg { private short token_2_rows; private Object[] token_3_arrayValues; - public ArrayPtg(RecordInputStream in) { + public ArrayPtg(LittleEndianInput in) { field_1_reserved = new byte[RESERVED_FIELD_LEN]; // TODO - add readFully method to RecordInputStream for(int i=0; i< RESERVED_FIELD_LEN; i++) { @@ -108,7 +108,7 @@ public final class ArrayPtg extends Ptg { * AFTER the last Ptg in the expression. * See page 304-305 of Excel97-2007BinaryFileFormat(xls)Specification.pdf */ - public void readTokenValues(RecordInputStream in) { + public void readTokenValues(LittleEndianInput in) { int nColumns = in.readUByte(); short nRows = in.readShort(); //The token_1_columns and token_2_rows do not follow the documentation. @@ -132,7 +132,7 @@ public final class ArrayPtg extends Ptg { if (token_3_arrayValues == null) { sb.append(" #values#uninitialised#\n"); } else { - sb.append(" ").append(formatAsString()); + sb.append(" ").append(toFormulaString()); } return sb.toString(); } @@ -153,17 +153,16 @@ public final class ArrayPtg extends Ptg { return rowIx * token_1_columns + colIx; } - public void writeBytes(byte[] data, int offset) { - - LittleEndian.putByte(data, offset + 0, sid + getPtgClass()); - System.arraycopy(field_1_reserved, 0, data, offset+1, RESERVED_FIELD_LEN); + public void write(LittleEndianOutput out) { + out.writeByte(sid + getPtgClass()); + out.write(field_1_reserved); } - public int writeTokenValueBytes(byte[] data, int offset) { + public int writeTokenValueBytes(LittleEndianOutput out) { - LittleEndian.putByte(data, offset + 0, token_1_columns-1); - LittleEndian.putUShort(data, offset + 1, token_2_rows-1); - ConstantValueParser.encode(data, offset + 3, token_3_arrayValues); + out.writeByte(token_1_columns-1); + out.writeShort(token_2_rows-1); + ConstantValueParser.encode(out, token_3_arrayValues); return 3 + ConstantValueParser.getEncodedSize(token_3_arrayValues); } @@ -183,7 +182,7 @@ public final class ArrayPtg extends Ptg { + ConstantValueParser.getEncodedSize(token_3_arrayValues); } - public String formatAsString() { // TODO - fold into toFormulaString + public String toFormulaString() { StringBuffer b = new StringBuffer(); b.append("{"); for (int y=0;y @@ -42,7 +42,7 @@ public final class DeletedArea3DPtg extends OperandPtg implements WorkbookDepend unused2 = 0; } - public DeletedArea3DPtg(RecordInputStream in) { + public DeletedArea3DPtg(LittleEndianInput in) { field_1_index_extern_sheet = in.readUShort(); unused1 = in.readInt(); unused2 = in.readInt(); @@ -60,10 +60,10 @@ public final class DeletedArea3DPtg extends OperandPtg implements WorkbookDepend public int getSize() { return 11; } - public void writeBytes(byte[] data, int offset) { - LittleEndian.putByte(data, 0 + offset, sid + getPtgClass()); - LittleEndian.putUShort(data, 1 + offset, field_1_index_extern_sheet); - LittleEndian.putInt(data, 3 + offset, unused1); - LittleEndian.putInt(data, 7 + offset, unused2); + public void write(LittleEndianOutput out) { + out.writeByte(sid + getPtgClass()); + out.writeShort(field_1_index_extern_sheet); + out.writeInt(unused1); + out.writeInt(unused2); } } diff --git a/src/java/org/apache/poi/hssf/record/formula/DeletedRef3DPtg.java b/src/java/org/apache/poi/hssf/record/formula/DeletedRef3DPtg.java index 4dbf09b7ff..dcc07ab6d9 100644 --- a/src/java/org/apache/poi/hssf/record/formula/DeletedRef3DPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/DeletedRef3DPtg.java @@ -18,11 +18,11 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.hssf.usermodel.HSSFErrorConstants; -import org.apache.poi.ss.formula.WorkbookDependentFormula; import org.apache.poi.ss.formula.FormulaRenderingWorkbook; -import org.apache.poi.util.LittleEndian; +import org.apache.poi.ss.formula.WorkbookDependentFormula; +import org.apache.poi.util.LittleEndianInput; +import org.apache.poi.util.LittleEndianOutput; /** * Title: Deleted Reference 3D Ptg

@@ -37,7 +37,7 @@ public final class DeletedRef3DPtg extends OperandPtg implements WorkbookDepende private final int unused1; /** Creates new DeletedRef3DPtg */ - public DeletedRef3DPtg(RecordInputStream in) { + public DeletedRef3DPtg(LittleEndianInput in) { field_1_index_extern_sheet = in.readUShort(); unused1 = in.readInt(); } @@ -60,9 +60,9 @@ public final class DeletedRef3DPtg extends OperandPtg implements WorkbookDepende public int getSize() { return 7; } - public void writeBytes(byte[] data, int offset) { - LittleEndian.putByte(data, 0 + offset, sid + getPtgClass()); - LittleEndian.putUShort(data, 1 + offset, field_1_index_extern_sheet); - LittleEndian.putInt(data, 3 + offset, unused1); + public void write(LittleEndianOutput out) { + out.writeByte(sid + getPtgClass()); + out.writeShort(field_1_index_extern_sheet); + out.writeInt(unused1); } } diff --git a/src/java/org/apache/poi/hssf/record/formula/ErrPtg.java b/src/java/org/apache/poi/hssf/record/formula/ErrPtg.java index 65d40dc51e..361e4b5cf9 100644 --- a/src/java/org/apache/poi/hssf/record/formula/ErrPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/ErrPtg.java @@ -17,8 +17,9 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.hssf.usermodel.HSSFErrorConstants; +import org.apache.poi.util.LittleEndianInput; +import org.apache.poi.util.LittleEndianOutput; /** * @author Daniel Noll (daniel at nuix dot com dot au) @@ -57,14 +58,13 @@ public final class ErrPtg extends ScalarConstantPtg { field_1_error_code = errorCode; } - public static ErrPtg read(RecordInputStream in) { + public static ErrPtg read(LittleEndianInput in) { return valueOf(in.readByte()); } - public void writeBytes(byte [] array, int offset) - { - array[offset] = (byte) (sid + getPtgClass()); - array[offset + 1] = (byte)field_1_error_code; + public void write(LittleEndianOutput out) { + out.writeByte(sid + getPtgClass()); + out.writeByte(field_1_error_code); } public String toFormulaString() { diff --git a/src/java/org/apache/poi/hssf/record/formula/ExpPtg.java b/src/java/org/apache/poi/hssf/record/formula/ExpPtg.java index 5c48aeb6b0..665fa97c83 100644 --- a/src/java/org/apache/poi/hssf/record/formula/ExpPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/ExpPtg.java @@ -18,8 +18,8 @@ package org.apache.poi.hssf.record.formula; import org.apache.poi.hssf.record.RecordFormatException; -import org.apache.poi.hssf.record.RecordInputStream; -import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.LittleEndianInput; +import org.apache.poi.util.LittleEndianOutput; /** * @@ -33,42 +33,41 @@ public final class ExpPtg extends ControlPtg { private final short field_1_first_row; private final short field_2_first_col; - public ExpPtg(RecordInputStream in) + public ExpPtg(LittleEndianInput in) { field_1_first_row = in.readShort(); field_2_first_col = in.readShort(); } - - public void writeBytes(byte [] array, int offset) - { - array[offset+0]= (byte) (sid); - LittleEndian.putShort(array,offset+1,field_1_first_row); - LittleEndian.putShort(array,offset+3,field_2_first_col); + + public void write(LittleEndianOutput out) { + out.writeByte(sid + getPtgClass()); + out.writeShort(field_1_first_row); + out.writeShort(field_2_first_col); } public int getSize() { return SIZE; } - + public short getRow() { return field_1_first_row; } public short getColumn() { return field_2_first_col; - } + } public String toFormulaString() { throw new RecordFormatException("Coding Error: Expected ExpPtg to be converted from Shared to Non-Shared Formula by ValueRecordsAggregate, but it wasn't"); } - + public String toString() { StringBuffer buffer = new StringBuffer("[Array Formula or Shared Formula]\n"); buffer.append("row = ").append(getRow()).append("\n"); buffer.append("col = ").append(getColumn()).append("\n"); return buffer.toString(); - } + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/FuncPtg.java b/src/java/org/apache/poi/hssf/record/formula/FuncPtg.java index c0034b24fb..133072203b 100644 --- a/src/java/org/apache/poi/hssf/record/formula/FuncPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/FuncPtg.java @@ -16,10 +16,10 @@ ==================================================================== */ package org.apache.poi.hssf.record.formula; -import org.apache.poi.util.LittleEndian; -import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.hssf.record.formula.function.FunctionMetadata; import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry; +import org.apache.poi.util.LittleEndianInput; +import org.apache.poi.util.LittleEndianOutput; /** * @author aviks @@ -35,7 +35,7 @@ public final class FuncPtg extends AbstractFunctionPtg { /**Creates new function pointer from a byte array * usually called while reading an excel file. */ - public FuncPtg(RecordInputStream in) { + public FuncPtg(LittleEndianInput in) { //field_1_num_args = data[ offset + 0 ]; field_2_fnc_index = in.readShort(); @@ -55,9 +55,9 @@ public final class FuncPtg extends AbstractFunctionPtg { paramClass = fm.getParameterClassCodes(); } - public void writeBytes(byte[] array, int offset) { - array[offset+0]= (byte) (sid + getPtgClass()); - LittleEndian.putShort(array,offset+1,field_2_fnc_index); + public void write(LittleEndianOutput out) { + out.writeByte(sid + getPtgClass()); + out.writeShort(field_2_fnc_index); } public int getNumberOfOperands() { diff --git a/src/java/org/apache/poi/hssf/record/formula/FuncVarPtg.java b/src/java/org/apache/poi/hssf/record/formula/FuncVarPtg.java index fd3546250a..1deb630d50 100644 --- a/src/java/org/apache/poi/hssf/record/formula/FuncVarPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/FuncVarPtg.java @@ -16,10 +16,10 @@ ==================================================================== */ package org.apache.poi.hssf.record.formula; -import org.apache.poi.util.LittleEndian; -import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.hssf.record.formula.function.FunctionMetadata; import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry; +import org.apache.poi.util.LittleEndianInput; +import org.apache.poi.util.LittleEndianOutput; /** * @@ -33,7 +33,7 @@ public final class FuncVarPtg extends AbstractFunctionPtg{ /**Creates new function pointer from a byte array * usually called while reading an excel file. */ - public FuncVarPtg(RecordInputStream in) { + public FuncVarPtg(LittleEndianInput in) { field_1_num_args = in.readByte(); field_2_fnc_index = in.readShort(); FunctionMetadata fm = FunctionMetadataRegistry.getFunctionByIndex(field_2_fnc_index); @@ -64,10 +64,10 @@ public final class FuncVarPtg extends AbstractFunctionPtg{ } } - public void writeBytes(byte[] array, int offset) { - array[offset+0]=(byte) (sid + getPtgClass()); - array[offset+1]=field_1_num_args; - LittleEndian.putShort(array,offset+2,field_2_fnc_index); + public void write(LittleEndianOutput out) { + out.writeByte(sid + getPtgClass()); + out.writeByte(field_1_num_args); + out.writeShort(field_2_fnc_index); } public int getNumberOfOperands() { diff --git a/src/java/org/apache/poi/hssf/record/formula/IntPtg.java b/src/java/org/apache/poi/hssf/record/formula/IntPtg.java index ea3fc80bd6..bb5d36f007 100644 --- a/src/java/org/apache/poi/hssf/record/formula/IntPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/IntPtg.java @@ -17,58 +17,60 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.hssf.record.RecordInputStream; -import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.LittleEndianInput; +import org.apache.poi.util.LittleEndianOutput; /** - * Integer (unsigned short integer) - * Stores an unsigned short value (java int) in a formula - * @author Andrew C. Oliver (acoliver at apache dot org) + * Integer (unsigned short integer) Stores an unsigned short value (java int) in + * a formula + * + * @author Andrew C. Oliver (acoliver at apache dot org) * @author Jason Height (jheight at chariot dot net dot au) */ public final class IntPtg extends ScalarConstantPtg { - // 16 bit unsigned integer - private static final int MIN_VALUE = 0x0000; - private static final int MAX_VALUE = 0xFFFF; - - /** - * Excel represents integers 0..65535 with the tInt token. - * @return true if the specified value is within the range of values - * IntPtg can represent. - */ - public static boolean isInRange(int i) { - return i>=MIN_VALUE && i <=MAX_VALUE; - } + // 16 bit unsigned integer + private static final int MIN_VALUE = 0x0000; + private static final int MAX_VALUE = 0xFFFF; - public final static int SIZE = 3; - public final static byte sid = 0x1e; - private final int field_1_value; - - public IntPtg(RecordInputStream in) { - this(in.readUShort()); - } + /** + * Excel represents integers 0..65535 with the tInt token. + * + * @return true if the specified value is within the range of values + * IntPtg can represent. + */ + public static boolean isInRange(int i) { + return i >= MIN_VALUE && i <= MAX_VALUE; + } - public IntPtg(int value) { - if(!isInRange(value)) { - throw new IllegalArgumentException("value is out of range: " + value); - } - field_1_value = value; - } + public final static int SIZE = 3; + public final static byte sid = 0x1e; + private final int field_1_value; - public int getValue() { - return field_1_value; - } + public IntPtg(LittleEndianInput in) { + this(in.readUShort()); + } - public void writeBytes(byte [] array, int offset) { - array[ offset + 0 ] = sid; - LittleEndian.putUShort(array, offset + 1, getValue()); - } + public IntPtg(int value) { + if (!isInRange(value)) { + throw new IllegalArgumentException("value is out of range: " + value); + } + field_1_value = value; + } - public int getSize() { - return SIZE; - } + public int getValue() { + return field_1_value; + } - public String toFormulaString() { - return String.valueOf(getValue()); - } + public void write(LittleEndianOutput out) { + out.writeByte(sid + getPtgClass()); + out.writeShort(getValue()); + } + + public int getSize() { + return SIZE; + } + + public String toFormulaString() { + return String.valueOf(getValue()); + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/IntersectionPtg.java b/src/java/org/apache/poi/hssf/record/formula/IntersectionPtg.java index f96c5ecb1a..0e6771de35 100644 --- a/src/java/org/apache/poi/hssf/record/formula/IntersectionPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/IntersectionPtg.java @@ -17,54 +17,46 @@ package org.apache.poi.hssf.record.formula; +import org.apache.poi.util.LittleEndianOutput; /** * @author Daniel Noll (daniel at nuix dot com dot au) */ public final class IntersectionPtg extends OperationPtg { - public final static byte sid = 0x0f; + public final static byte sid = 0x0f; - public static final OperationPtg instance = new IntersectionPtg(); + public static final OperationPtg instance = new IntersectionPtg(); - private IntersectionPtg() { - // enforce singleton - } + private IntersectionPtg() { + // enforce singleton + } - public final boolean isBaseToken() { - return true; - } + public final boolean isBaseToken() { + return true; + } - public int getSize() - { - return 1; - } + public int getSize() { + return 1; + } - public void writeBytes( byte[] array, int offset ) - { - array[ offset + 0 ] = sid; - } + public void write(LittleEndianOutput out) { + out.writeByte(sid + getPtgClass()); + } - /** Implementation of method from Ptg */ - public String toFormulaString() - { - return " "; - } + public String toFormulaString() { + return " "; + } + public String toFormulaString(String[] operands) { + StringBuffer buffer = new StringBuffer(); - /** implementation of method from OperationsPtg*/ - public String toFormulaString(String[] operands) - { - StringBuffer buffer = new StringBuffer(); - - buffer.append(operands[ 0 ]); - buffer.append(" "); - buffer.append(operands[ 1 ]); - return buffer.toString(); - } - - public int getNumberOfOperands() - { - return 2; - } + buffer.append(operands[0]); + buffer.append(" "); + buffer.append(operands[1]); + return buffer.toString(); + } + public int getNumberOfOperands() { + return 2; + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/MemAreaPtg.java b/src/java/org/apache/poi/hssf/record/formula/MemAreaPtg.java index 951fd7c436..58a836e28f 100644 --- a/src/java/org/apache/poi/hssf/record/formula/MemAreaPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/MemAreaPtg.java @@ -17,68 +17,45 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.hssf.record.RecordInputStream; -import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.LittleEndianInput; +import org.apache.poi.util.LittleEndianOutput; /** * @author Daniel Noll (daniel at nuix dot com dot au) */ public class MemAreaPtg extends OperandPtg { - public final static short sid = 0x26; - private final static int SIZE = 7; - private int field_1_reserved; - private short field_2_subex_len; - - /** Creates new MemAreaPtg */ - - public MemAreaPtg() - { - } - - public MemAreaPtg(RecordInputStream in) - { - field_1_reserved = in.readInt(); - field_2_subex_len = in.readShort(); - } - - public void setReserved(int res) - { - field_1_reserved = res; - } - - public int getReserved() - { - return field_1_reserved; - } - - public void setSubexpressionLength(short subexlen) - { - field_2_subex_len = subexlen; - } - - public short getSubexpressionLength() - { - return field_2_subex_len; - } - - public void writeBytes(byte [] array, int offset) - { - array[offset] = (byte) (sid + getPtgClass()); - LittleEndian.putInt(array, offset + 1, field_1_reserved); - LittleEndian.putShort(array, offset + 5, field_2_subex_len); - } - - public int getSize() - { - return SIZE; - } - - public String toFormulaString() - { - return ""; // TODO: Not sure how to format this. -- DN - } - - public byte getDefaultOperandClass() { - return Ptg.CLASS_VALUE; - } + public final static short sid = 0x26; + private final static int SIZE = 7; + private final int field_1_reserved; + private final int field_2_subex_len; + + /** Creates new MemAreaPtg */ + + public MemAreaPtg(int subexLen) { + field_1_reserved = 0; + field_2_subex_len = subexLen; + } + + public MemAreaPtg(LittleEndianInput in) { + field_1_reserved = in.readInt(); + field_2_subex_len = in.readShort(); + } + + public void write(LittleEndianOutput out) { + out.writeByte(sid + getPtgClass()); + out.writeInt(field_1_reserved); + out.writeShort(field_2_subex_len); + } + + public int getSize() { + return SIZE; + } + + public String toFormulaString() { + return ""; // TODO: Not sure how to format this. -- DN + } + + public byte getDefaultOperandClass() { + return Ptg.CLASS_VALUE; + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/MemErrPtg.java b/src/java/org/apache/poi/hssf/record/formula/MemErrPtg.java index d3de53fe13..91027b0ab0 100644 --- a/src/java/org/apache/poi/hssf/record/formula/MemErrPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/MemErrPtg.java @@ -17,36 +17,41 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.hssf.record.RecordInputStream; -import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.util.LittleEndianInput; +import org.apache.poi.util.LittleEndianOutput; /** - * - * @author andy + * + * @author andy * @author Jason Height (jheight at chariot dot net dot au) * @author Daniel Noll (daniel at nuix dot com dot au) */ - -public final class MemErrPtg extends MemAreaPtg { - public final static short sid = 0x27; - - /** Creates new MemErrPtg */ - - public MemErrPtg() - { - } - - public MemErrPtg(RecordInputStream in) { - super(in); - } - - public void writeBytes(byte [] array, int offset) { - super.writeBytes(array, offset); - array[offset] = (byte) (sid + getPtgClass()); - } - - public String toFormulaString() - { - return "ERR#"; - } +public final class MemErrPtg extends OperandPtg { + public final static short sid = 0x27; + private final static int SIZE = 7; + private int field_1_reserved; + private short field_2_subex_len; + + public MemErrPtg(LittleEndianInput in) { + field_1_reserved = in.readInt(); + field_2_subex_len = in.readShort(); + } + + public void write(LittleEndianOutput out) { + out.writeByte(sid + getPtgClass()); + out.writeInt(field_1_reserved); + out.writeShort(field_2_subex_len); + } + + public int getSize() { + return SIZE; + } + + public String toFormulaString() { + return "ERR#"; + } + + public byte getDefaultOperandClass() { + return Ptg.CLASS_VALUE; + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/MemFuncPtg.java b/src/java/org/apache/poi/hssf/record/formula/MemFuncPtg.java index 991f1742c0..3ee893bdd7 100644 --- a/src/java/org/apache/poi/hssf/record/formula/MemFuncPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/MemFuncPtg.java @@ -17,56 +17,51 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.hssf.record.RecordInputStream; -import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.LittleEndianInput; +import org.apache.poi.util.LittleEndianOutput; /** * @author Glen Stampoultzis (glens at apache.org) */ public final class MemFuncPtg extends OperandPtg { - public final static byte sid = 0x29; - private final int field_1_len_ref_subexpression; + public final static byte sid = 0x29; + private final int field_1_len_ref_subexpression; - /**Creates new function pointer from a byte array - * usually called while reading an excel file. - */ - public MemFuncPtg(RecordInputStream in) { - this(in.readUShort()); - } + /** + * Creates new function pointer from a byte array usually called while + * reading an excel file. + */ + public MemFuncPtg(LittleEndianInput in) { + this(in.readUShort()); + } - public MemFuncPtg(int subExprLen) { - field_1_len_ref_subexpression = subExprLen; + public MemFuncPtg(int subExprLen) { + field_1_len_ref_subexpression = subExprLen; } - public int getSize() - { - return 3; - } + public int getSize() { + return 3; + } - public void writeBytes( byte[] array, int offset ) - { - array[offset + 0] = sid ; - LittleEndian.putUShort( array, offset + 1, field_1_len_ref_subexpression ); - } + public void write(LittleEndianOutput out) { + out.writeByte(sid + getPtgClass()); + out.writeShort(field_1_len_ref_subexpression); + } - public String toFormulaString() - { - return ""; - } + public String toFormulaString() { + return ""; + } - public byte getDefaultOperandClass() - { - return Ptg.CLASS_REF; - } + public byte getDefaultOperandClass() { + return Ptg.CLASS_REF; + } - public int getNumberOfOperands() - { - return field_1_len_ref_subexpression; - } + public int getNumberOfOperands() { + return field_1_len_ref_subexpression; + } - public int getLenRefSubexpression() - { - return field_1_len_ref_subexpression; - } + public int getLenRefSubexpression() { + return field_1_len_ref_subexpression; + } } \ No newline at end of file diff --git a/src/java/org/apache/poi/hssf/record/formula/MissingArgPtg.java b/src/java/org/apache/poi/hssf/record/formula/MissingArgPtg.java index 075105f50c..99b7708a4b 100644 --- a/src/java/org/apache/poi/hssf/record/formula/MissingArgPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/MissingArgPtg.java @@ -17,32 +17,35 @@ package org.apache.poi.hssf.record.formula; +import org.apache.poi.util.LittleEndianOutput; + /** * Missing Function Arguments - * + * * Avik Sengupta <avik at apache.org> + * * @author Jason Height (jheight at chariot dot net dot au) */ public final class MissingArgPtg extends ScalarConstantPtg { - - private final static int SIZE = 1; - public final static byte sid = 0x16; - - public static final Ptg instance = new MissingArgPtg(); - - private MissingArgPtg() { - // enforce singleton - } - - public void writeBytes(byte [] array, int offset) { - array[ offset + 0 ] = sid; - } - - public int getSize() { - return SIZE; - } - - public String toFormulaString() { - return " "; - } + + private final static int SIZE = 1; + public final static byte sid = 0x16; + + public static final Ptg instance = new MissingArgPtg(); + + private MissingArgPtg() { + // enforce singleton + } + + public void write(LittleEndianOutput out) { + out.writeByte(sid + getPtgClass()); + } + + public int getSize() { + return SIZE; + } + + public String toFormulaString() { + return " "; + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/NamePtg.java b/src/java/org/apache/poi/hssf/record/formula/NamePtg.java index 59670f8758..e5cd0fc9de 100644 --- a/src/java/org/apache/poi/hssf/record/formula/NamePtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/NamePtg.java @@ -17,63 +17,63 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.hssf.record.RecordInputStream; -import org.apache.poi.ss.formula.WorkbookDependentFormula; import org.apache.poi.ss.formula.FormulaRenderingWorkbook; -import org.apache.poi.util.LittleEndian; +import org.apache.poi.ss.formula.WorkbookDependentFormula; +import org.apache.poi.util.LittleEndianInput; +import org.apache.poi.util.LittleEndianOutput; /** - * - * @author andy + * + * @author andy * @author Jason Height (jheight at chariot dot net dot au) */ public final class NamePtg extends OperandPtg implements WorkbookDependentFormula { - public final static short sid = 0x23; - private final static int SIZE = 5; - /** one-based index to defined name record */ - private int field_1_label_index; - private short field_2_zero; // reserved must be 0 + public final static short sid = 0x23; + private final static int SIZE = 5; + /** one-based index to defined name record */ + private int field_1_label_index; + private short field_2_zero; // reserved must be 0 - /** - * @param nameIndex zero-based index to name within workbook - */ - public NamePtg(int nameIndex) { - field_1_label_index = 1+nameIndex; // convert to 1-based - } + /** + * @param nameIndex zero-based index to name within workbook + */ + public NamePtg(int nameIndex) { + field_1_label_index = 1 + nameIndex; // convert to 1-based + } - /** Creates new NamePtg */ + /** Creates new NamePtg */ - public NamePtg(RecordInputStream in) { - field_1_label_index = in.readShort(); - field_2_zero = in.readShort(); - } - - /** - * @return zero based index to a defined name record in the LinkTable - */ - public int getIndex() { - return field_1_label_index-1; // convert to zero based - } + public NamePtg(LittleEndianInput in) { + field_1_label_index = in.readShort(); + field_2_zero = in.readShort(); + } - public void writeBytes(byte [] array, int offset) { - LittleEndian.putByte(array, offset + 0, sid + getPtgClass()); - LittleEndian.putUShort(array, offset + 1, field_1_label_index); - LittleEndian.putUShort(array, offset + 3, field_2_zero); - } + /** + * @return zero based index to a defined name record in the LinkTable + */ + public int getIndex() { + return field_1_label_index - 1; // convert to zero based + } - public int getSize() { - return SIZE; - } + public void write(LittleEndianOutput out) { + out.writeByte(sid + getPtgClass()); + out.writeShort(field_1_label_index); + out.writeShort(field_2_zero); + } + + public int getSize() { + return SIZE; + } + + public String toFormulaString(FormulaRenderingWorkbook book) { + return book.getNameText(this); + } - public String toFormulaString(FormulaRenderingWorkbook book) - { - return book.getNameText(this); - } public String toFormulaString() { throw new RuntimeException("3D references need a workbook to determine formula text"); } - - public byte getDefaultOperandClass() { + + public byte getDefaultOperandClass() { return Ptg.CLASS_REF; } } diff --git a/src/java/org/apache/poi/hssf/record/formula/NameXPtg.java b/src/java/org/apache/poi/hssf/record/formula/NameXPtg.java index 7f1614347f..de08763c73 100644 --- a/src/java/org/apache/poi/hssf/record/formula/NameXPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/NameXPtg.java @@ -17,10 +17,10 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.hssf.record.RecordInputStream; -import org.apache.poi.ss.formula.WorkbookDependentFormula; import org.apache.poi.ss.formula.FormulaRenderingWorkbook; -import org.apache.poi.util.LittleEndian; +import org.apache.poi.ss.formula.WorkbookDependentFormula; +import org.apache.poi.util.LittleEndianInput; +import org.apache.poi.util.LittleEndianOutput; /** * @@ -51,15 +51,15 @@ public final class NameXPtg extends OperandPtg implements WorkbookDependentFormu this(sheetRefIndex, nameIndex + 1, 0); } - public NameXPtg(RecordInputStream in) { + public NameXPtg(LittleEndianInput in) { this(in.readUShort(), in.readUShort(), in.readUShort()); } - public void writeBytes(byte[] array, int offset) { - LittleEndian.putByte(array, offset + 0, sid + getPtgClass()); - LittleEndian.putUShort(array, offset + 1, _sheetRefIndex); - LittleEndian.putUShort(array, offset + 3, _nameNumber); - LittleEndian.putUShort(array, offset + 5, _reserved); + public void write(LittleEndianOutput out) { + out.writeByte(sid + getPtgClass()); + out.writeShort(_sheetRefIndex); + out.writeShort(_nameNumber); + out.writeShort(_reserved); } public int getSize() { diff --git a/src/java/org/apache/poi/hssf/record/formula/NumberPtg.java b/src/java/org/apache/poi/hssf/record/formula/NumberPtg.java index cddb674475..5bd8b08453 100644 --- a/src/java/org/apache/poi/hssf/record/formula/NumberPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/NumberPtg.java @@ -17,55 +17,55 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.hssf.record.RecordInputStream; -import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.LittleEndianInput; +import org.apache.poi.util.LittleEndianOutput; /** - * Number - * Stores a floating point value in a formula - * value stored in a 8 byte field using IEEE notation - * @author Avik Sengupta + * Number Stores a floating point value in a formula value stored in a 8 byte + * field using IEEE notation + * + * @author Avik Sengupta * @author Jason Height (jheight at chariot dot net dot au) */ public final class NumberPtg extends ScalarConstantPtg { - public final static int SIZE = 9; - public final static byte sid = 0x1f; - private final double field_1_value; - - /** Create a NumberPtg from a byte array read from disk */ - public NumberPtg(RecordInputStream in) { - this(in.readDouble()); - } - - /** Create a NumberPtg from a string representation of the number - * Number format is not checked, it is expected to be validated in the parser - * that calls this method. - * @param value : String representation of a floating point number - */ - public NumberPtg(String value) { - this(Double.parseDouble(value)); - } - - public NumberPtg(double value) { - field_1_value = value; - } - - public double getValue() { - return field_1_value; - } + public final static int SIZE = 9; + public final static byte sid = 0x1f; + private final double field_1_value; - public void writeBytes(byte [] array, int offset) { - array[ offset + 0 ] = sid; - LittleEndian.putDouble(array, offset + 1, getValue()); - } + public NumberPtg(LittleEndianInput in) { + this(in.readDouble()); + } - public int getSize() { - return SIZE; - } + /** + * Create a NumberPtg from a string representation of the number Number + * format is not checked, it is expected to be validated in the parser that + * calls this method. + * + * @param value String representation of a floating point number + */ + public NumberPtg(String value) { + this(Double.parseDouble(value)); + } - public String toFormulaString() { - // TODO - java's rendering of double values is not quite same as excel's - // Maybe use HSSFDataFormatter? - return String.valueOf(field_1_value); - } + public NumberPtg(double value) { + field_1_value = value; + } + + public double getValue() { + return field_1_value; + } + + public void write(LittleEndianOutput out) { + out.writeByte(sid + getPtgClass()); + out.writeDouble(getValue()); + } + + public int getSize() { + return SIZE; + } + + public String toFormulaString() { + // TODO - java's rendering of double values is not quite same as excel's + return String.valueOf(field_1_value); + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/ParenthesisPtg.java b/src/java/org/apache/poi/hssf/record/formula/ParenthesisPtg.java index 5365b939d8..1b94c90383 100644 --- a/src/java/org/apache/poi/hssf/record/formula/ParenthesisPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/ParenthesisPtg.java @@ -15,46 +15,44 @@ limitations under the License. ==================================================================== */ - package org.apache.poi.hssf.record.formula; +import org.apache.poi.util.LittleEndianOutput; /** - * While formula tokens are stored in RPN order and thus do not need parenthesis for - * precedence reasons, Parenthesis tokens ARE written to ensure that user entered - * parenthesis are displayed as-is on reading back - * - * Avik Sengupta <lists@aviksengupta.com> - * Andrew C. Oliver (acoliver at apache dot org) + * While formula tokens are stored in RPN order and thus do not need parenthesis + * for precedence reasons, Parenthesis tokens ARE written to ensure that user + * entered parenthesis are displayed as-is on reading back + * + * Avik Sengupta <lists@aviksengupta.com> Andrew C. Oliver (acoliver at + * apache dot org) + * * @author Jason Height (jheight at chariot dot net dot au) */ public final class ParenthesisPtg extends ControlPtg { - - private final static int SIZE = 1; - public final static byte sid = 0x15; - - public static final ControlPtg instance = new ParenthesisPtg(); - private ParenthesisPtg() { - // enforce singleton - } - - public void writeBytes(byte [] array, int offset) - { - array[ offset + 0 ] = sid; - } - - public int getSize() - { - return SIZE; - } - - public String toFormulaString() - { - return "()"; - } - - - public String toFormulaString(String[] operands) { - return "("+operands[0]+")"; - } + + private final static int SIZE = 1; + public final static byte sid = 0x15; + + public static final ControlPtg instance = new ParenthesisPtg(); + + private ParenthesisPtg() { + // enforce singleton + } + + public void write(LittleEndianOutput out) { + out.writeByte(sid + getPtgClass()); + } + + public int getSize() { + return SIZE; + } + + public String toFormulaString() { + return "()"; + } + + public String toFormulaString(String[] operands) { + return "(" + operands[0] + ")"; + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/Ptg.java b/src/java/org/apache/poi/hssf/record/formula/Ptg.java index a264481d43..771cccf582 100644 --- a/src/java/org/apache/poi/hssf/record/formula/Ptg.java +++ b/src/java/org/apache/poi/hssf/record/formula/Ptg.java @@ -20,8 +20,8 @@ package org.apache.poi.hssf.record.formula; import java.util.ArrayList; import java.util.List; -import org.apache.poi.hssf.record.RecordInputStream; -import org.apache.poi.util.HexDump; +import org.apache.poi.util.LittleEndianByteArrayOutputStream; +import org.apache.poi.util.LittleEndianInput; import org.apache.poi.util.LittleEndianOutput; /** @@ -48,12 +48,12 @@ public abstract class Ptg implements Cloneable { * Reads size bytes of the input stream, to create an array of Ptgs. * Extra data (beyond size) may be read if and ArrayPtgs are present. */ - public static Ptg[] readTokens(int size, RecordInputStream in) { + public static Ptg[] readTokens(int size, LittleEndianInput in) { List temp = new ArrayList(4 + size / 2); int pos = 0; List arrayPtgs = null; while (pos < size) { - Ptg ptg = Ptg.createPtg( in ); + Ptg ptg = Ptg.createPtg(in); if (ptg instanceof ArrayPtg) { if (arrayPtgs == null) { arrayPtgs = new ArrayList(5); @@ -77,7 +77,7 @@ public abstract class Ptg implements Cloneable { return toPtgArray(temp); } - public static Ptg createPtg(RecordInputStream in) { + public static Ptg createPtg(LittleEndianInput in) { byte id = in.readByte(); if (id < 0x20) { @@ -97,7 +97,7 @@ public abstract class Ptg implements Cloneable { return retval; } - private static Ptg createClassifiedPtg(byte id, RecordInputStream in) { + private static Ptg createClassifiedPtg(byte id, LittleEndianInput in) { int baseId = id & 0x1F | 0x20; @@ -126,9 +126,9 @@ public abstract class Ptg implements Cloneable { Integer.toHexString(id) + " (" + ( int ) id + ")"); } - private static Ptg createBasePtg(byte id, RecordInputStream in) { + private static Ptg createBasePtg(byte id, LittleEndianInput in) { switch(id) { - case 0x00: return new UnknownPtg(); // TODO - not a real Ptg + case 0x00: return new UnknownPtg(id); // TODO - not a real Ptg case ExpPtg.sid: return new ExpPtg(in); // 0x01 case TblPtg.sid: return new TblPtg(in); // 0x02 case AddPtg.sid: return AddPtg.instance; // 0x03 @@ -228,32 +228,30 @@ public abstract class Ptg implements Cloneable { * @return number of bytes written */ public static int serializePtgs(Ptg[] ptgs, byte[] array, int offset) { - int pos = 0; - int size = ptgs.length; + int nTokens = ptgs.length; + + LittleEndianByteArrayOutputStream out = new LittleEndianByteArrayOutputStream(array, offset); List arrayPtgs = null; - for (int k = 0; k < size; k++) { + for (int k = 0; k < nTokens; k++) { Ptg ptg = ptgs[k]; - ptg.writeBytes(array, pos + offset); + ptg.write(out); if (ptg instanceof ArrayPtg) { if (arrayPtgs == null) { arrayPtgs = new ArrayList(5); } arrayPtgs.add(ptg); - pos += ArrayPtg.PLAIN_TOKEN_SIZE; - } else { - pos += ptg.getSize(); } } if (arrayPtgs != null) { for (int i=0;i @@ -41,7 +41,7 @@ public final class Ref3DPtg extends RefPtgBase implements WorkbookDependentFormu /** Creates new AreaPtg */ public Ref3DPtg() {} - public Ref3DPtg(RecordInputStream in) { + public Ref3DPtg(LittleEndianInput in) { field_1_index_extern_sheet = in.readShort(); readCoordinates(in); } @@ -66,10 +66,10 @@ public final class Ref3DPtg extends RefPtgBase implements WorkbookDependentFormu return sb.toString(); } - public void writeBytes(byte [] array, int offset) { - LittleEndian.putByte(array, 0 + offset, sid + getPtgClass()); - LittleEndian.putUShort(array, 1 + offset, getExternSheetIndex()); - writeCoordinates(array, offset + 3); + public void write(LittleEndianOutput out) { + out.writeByte(sid + getPtgClass()); + out.writeShort(getExternSheetIndex()); + writeCoordinates(out); } public int getSize() { diff --git a/src/java/org/apache/poi/hssf/record/formula/RefErrorPtg.java b/src/java/org/apache/poi/hssf/record/formula/RefErrorPtg.java index 699f6344a3..4cbd501053 100755 --- a/src/java/org/apache/poi/hssf/record/formula/RefErrorPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/RefErrorPtg.java @@ -17,10 +17,9 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.hssf.usermodel.HSSFErrorConstants; -import org.apache.poi.hssf.usermodel.HSSFWorkbook; -import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.LittleEndianInput; +import org.apache.poi.util.LittleEndianOutput; /** * RefError - handles deleted cell reference @@ -35,7 +34,7 @@ public final class RefErrorPtg extends OperandPtg { public RefErrorPtg() { field_1_reserved = 0; } - public RefErrorPtg(RecordInputStream in) { + public RefErrorPtg(LittleEndianInput in) { field_1_reserved = in.readInt(); } @@ -43,9 +42,9 @@ public final class RefErrorPtg extends OperandPtg { return getClass().getName(); } - public void writeBytes(byte [] array, int offset) { - LittleEndian.putByte(array, offset+0, sid + getPtgClass()); - LittleEndian.putInt(array,offset+1,field_1_reserved); + public void write(LittleEndianOutput out) { + out.writeByte(sid + getPtgClass()); + out.writeInt(field_1_reserved); } public int getSize() diff --git a/src/java/org/apache/poi/hssf/record/formula/RefNPtg.java b/src/java/org/apache/poi/hssf/record/formula/RefNPtg.java index 07114265d0..3a8148c46c 100644 --- a/src/java/org/apache/poi/hssf/record/formula/RefNPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/RefNPtg.java @@ -17,7 +17,7 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.hssf.record.RecordInputStream; +import org.apache.poi.util.LittleEndianInput; /** * RefNPtg @@ -26,7 +26,7 @@ import org.apache.poi.hssf.record.RecordInputStream; public final class RefNPtg extends Ref2DPtgBase { public final static byte sid = 0x2C; - public RefNPtg(RecordInputStream in) { + public RefNPtg(LittleEndianInput in) { super(in); } diff --git a/src/java/org/apache/poi/hssf/record/formula/RefPtg.java b/src/java/org/apache/poi/hssf/record/formula/RefPtg.java index 94a1b3301c..69508fd7d2 100644 --- a/src/java/org/apache/poi/hssf/record/formula/RefPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/RefPtg.java @@ -17,7 +17,7 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.hssf.record.RecordInputStream; +import org.apache.poi.util.LittleEndianInput; /** * ReferencePtg - handles references (such as A1, A2, IA4) @@ -39,7 +39,7 @@ public final class RefPtg extends Ref2DPtgBase { super(row, column, isRowRelative, isColumnRelative); } - public RefPtg(RecordInputStream in) { + public RefPtg(LittleEndianInput in) { super(in); } diff --git a/src/java/org/apache/poi/hssf/record/formula/RefPtgBase.java b/src/java/org/apache/poi/hssf/record/formula/RefPtgBase.java index f04ec0ac8f..8357e158c5 100644 --- a/src/java/org/apache/poi/hssf/record/formula/RefPtgBase.java +++ b/src/java/org/apache/poi/hssf/record/formula/RefPtgBase.java @@ -17,112 +17,114 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.hssf.util.CellReference; import org.apache.poi.util.BitField; import org.apache.poi.util.BitFieldFactory; -import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.LittleEndianInput; +import org.apache.poi.util.LittleEndianOutput; /** * ReferencePtgBase - handles references (such as A1, A2, IA4) - * @author Andrew C. Oliver (acoliver@apache.org) + * + * @author Andrew C. Oliver (acoliver@apache.org) * @author Jason Height (jheight at chariot dot net dot au) */ public abstract class RefPtgBase extends OperandPtg { - private final static int MAX_ROW_NUMBER = 65536; - - /** The row index - zero based unsigned 16 bit value */ - private int field_1_row; - /** Field 2 - * - lower 8 bits is the zero based unsigned byte column index - * - bit 16 - isRowRelative - * - bit 15 - isColumnRelative - */ - private int field_2_col; - private static final BitField rowRelative = BitFieldFactory.getInstance(0x8000); - private static final BitField colRelative = BitFieldFactory.getInstance(0x4000); - private static final BitField column = BitFieldFactory.getInstance(0x00FF); - - protected RefPtgBase() { - //Required for clone methods - } - - /** - * Takes in a String representation of a cell reference and fills out the - * numeric fields. - */ - protected RefPtgBase(String cellref) { - CellReference c= new CellReference(cellref); - setRow(c.getRow()); - setColumn(c.getCol()); - setColRelative(!c.isColAbsolute()); - setRowRelative(!c.isRowAbsolute()); - } - - protected RefPtgBase(int row, int column, boolean isRowRelative, boolean isColumnRelative) { - setRow(row); - setColumn(column); - setRowRelative(isRowRelative); - setColRelative(isColumnRelative); - } - - protected final void readCoordinates(RecordInputStream in) { - field_1_row = in.readUShort(); - field_2_col = in.readUShort(); - } - protected final void writeCoordinates(byte[] array, int offset) { - LittleEndian.putUShort(array, offset + 0, field_1_row); - LittleEndian.putUShort(array, offset + 2, field_2_col); - } - - public final void setRow(int row) { - if(row < 0 || row >= MAX_ROW_NUMBER) { - throw new IllegalArgumentException("The row number, when specified as an integer, must be between 0 and " + MAX_ROW_NUMBER); - } - field_1_row = row; - } - - /** - * @return the row number as an int, between 0 and 65535 - */ - public final int getRow(){ - return field_1_row; - } - - public final boolean isRowRelative() { - return rowRelative.isSet(field_2_col); - } - - public final void setRowRelative(boolean rel) { - field_2_col=rowRelative.setBoolean(field_2_col,rel); - } - - public final boolean isColRelative() { - return colRelative.isSet(field_2_col); - } - - public final void setColRelative(boolean rel) { - field_2_col=colRelative.setBoolean(field_2_col,rel); - } - - public final void setColumn(int col) { - if(col < 0 || col >= 0x100) { - throw new IllegalArgumentException("Specified colIx (" + col + ") is out of range"); - } - field_2_col = column.setValue(field_2_col, col); - } - - public final int getColumn() { - return column.getValue(field_2_col); - } - protected final String formatReferenceAsString() { - // Only make cell references as needed. Memory is an issue - CellReference cr = new CellReference(getRow(), getColumn(), !isRowRelative(), !isColRelative()); - return cr.formatAsString(); - } - - public final byte getDefaultOperandClass() { - return Ptg.CLASS_REF; - } + private final static int MAX_ROW_NUMBER = 65536; + + /** The row index - zero based unsigned 16 bit value */ + private int field_1_row; + /** + * Field 2 - lower 8 bits is the zero based unsigned byte column index - bit + * 16 - isRowRelative - bit 15 - isColumnRelative + */ + private int field_2_col; + private static final BitField rowRelative = BitFieldFactory.getInstance(0x8000); + private static final BitField colRelative = BitFieldFactory.getInstance(0x4000); + private static final BitField column = BitFieldFactory.getInstance(0x00FF); + + protected RefPtgBase() { + // Required for clone methods + } + + /** + * Takes in a String representation of a cell reference and fills out the + * numeric fields. + */ + protected RefPtgBase(String cellref) { + CellReference c = new CellReference(cellref); + setRow(c.getRow()); + setColumn(c.getCol()); + setColRelative(!c.isColAbsolute()); + setRowRelative(!c.isRowAbsolute()); + } + + protected RefPtgBase(int row, int column, boolean isRowRelative, boolean isColumnRelative) { + setRow(row); + setColumn(column); + setRowRelative(isRowRelative); + setColRelative(isColumnRelative); + } + + protected final void readCoordinates(LittleEndianInput in) { + field_1_row = in.readUShort(); + field_2_col = in.readUShort(); + } + + protected final void writeCoordinates(LittleEndianOutput out) { + out.writeShort(field_1_row); + out.writeShort(field_2_col); + } + + public final void setRow(int rowIndex) { + if (rowIndex < 0 || rowIndex >= MAX_ROW_NUMBER) { + throw new IllegalArgumentException("rowIndex must be between 0 and " + MAX_ROW_NUMBER); + } + field_1_row = rowIndex; + } + + /** + * @return the row number as an int, between 0 and 65535 + */ + public final int getRow() { + return field_1_row; + } + + public final boolean isRowRelative() { + return rowRelative.isSet(field_2_col); + } + + public final void setRowRelative(boolean rel) { + field_2_col = rowRelative.setBoolean(field_2_col, rel); + } + + public final boolean isColRelative() { + return colRelative.isSet(field_2_col); + } + + public final void setColRelative(boolean rel) { + field_2_col = colRelative.setBoolean(field_2_col, rel); + } + + public final void setColumn(int col) { + if (col < 0 || col >= 0x100) { + throw new IllegalArgumentException("Specified colIx (" + col + ") is out of range"); + } + field_2_col = column.setValue(field_2_col, col); + } + + public final int getColumn() { + return column.getValue(field_2_col); + } + + protected final String formatReferenceAsString() { + // Only make cell references as needed. Memory is an issue + CellReference cr = new CellReference(getRow(), getColumn(), !isRowRelative(), !isColRelative()); + return cr.formatAsString(); + } + + public final byte getDefaultOperandClass() { + return Ptg.CLASS_REF; + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/StringPtg.java b/src/java/org/apache/poi/hssf/record/formula/StringPtg.java index 2f34e2e360..be6a20d6e4 100644 --- a/src/java/org/apache/poi/hssf/record/formula/StringPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/StringPtg.java @@ -17,9 +17,8 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.hssf.record.RecordInputStream; -import org.apache.poi.util.BitField; -import org.apache.poi.util.BitFieldFactory; +import org.apache.poi.util.LittleEndianInput; +import org.apache.poi.util.LittleEndianOutput; import org.apache.poi.util.StringUtil; /** @@ -31,29 +30,26 @@ import org.apache.poi.util.StringUtil; * @author Bernard Chesnoy */ public final class StringPtg extends ScalarConstantPtg { - public final static int SIZE = 9; - public final static byte sid = 0x17; - private static final BitField fHighByte = BitFieldFactory.getInstance(0x01); - /** the character (")used in formulas to delimit string literals */ + public final static byte sid = 0x17; + /** the character (") used in formulas to delimit string literals */ private static final char FORMULA_DELIMITER = '"'; + private final boolean _is16bitUnicode; /** * NOTE: OO doc says 16bit length, but BiffViewer says 8 Book says something * totally different, so don't look there! */ - private final int field_1_length; - private final byte field_2_options; private final String field_3_string; /** Create a StringPtg from a stream */ - public StringPtg(RecordInputStream in) { - field_1_length = in.readUByte(); - field_2_options = in.readByte(); - if (fHighByte.isSet(field_2_options)) { - field_3_string = in.readUnicodeLEString(field_1_length); - } else { - field_3_string = in.readCompressedUnicode(field_1_length); - } + public StringPtg(LittleEndianInput in) { + int nChars = in.readUByte(); // Note - nChars is 8-bit + _is16bitUnicode = (in.readByte() & 0x01) != 0; + if (_is16bitUnicode) { + field_3_string = StringUtil.readUnicodeLE(in, nChars); + } else { + field_3_string = StringUtil.readCompressedUnicode(in, nChars); + } } /** @@ -69,32 +65,27 @@ public final class StringPtg extends ScalarConstantPtg { throw new IllegalArgumentException( "String literals in formulas can't be bigger than 255 characters ASCII"); } - field_2_options = (byte) fHighByte.setBoolean(0, StringUtil.hasMultibyte(value)); + _is16bitUnicode = StringUtil.hasMultibyte(value); field_3_string = value; - field_1_length = value.length(); // for the moment, we support only ASCII strings in formulas we create } public String getValue() { return field_3_string; } - public void writeBytes(byte[] array, int offset) { - array[offset + 0] = sid; - array[offset + 1] = (byte) field_1_length; - array[offset + 2] = field_2_options; - if (fHighByte.isSet(field_2_options)) { - StringUtil.putUnicodeLE(getValue(), array, offset + 3); + public void write(LittleEndianOutput out) { + out.writeByte(sid + getPtgClass()); + out.writeByte(field_3_string.length()); // Note - nChars is 8-bit + out.writeByte(_is16bitUnicode ? 0x01 : 0x00); + if (_is16bitUnicode) { + StringUtil.putUnicodeLE(field_3_string, out); } else { - StringUtil.putCompressedUnicode(getValue(), array, offset + 3); + StringUtil.putCompressedUnicode(field_3_string, out); } } public int getSize() { - if (fHighByte.isSet(field_2_options)) { - return 2 * field_1_length + 3; - } else { - return field_1_length + 3; - } + return 3 + field_3_string.length() * (_is16bitUnicode ? 2 : 1); } public String toFormulaString() { diff --git a/src/java/org/apache/poi/hssf/record/formula/TblPtg.java b/src/java/org/apache/poi/hssf/record/formula/TblPtg.java index 0b17e268e2..280bd9c010 100644 --- a/src/java/org/apache/poi/hssf/record/formula/TblPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/TblPtg.java @@ -18,8 +18,8 @@ package org.apache.poi.hssf.record.formula; import org.apache.poi.hssf.record.RecordFormatException; -import org.apache.poi.hssf.record.RecordInputStream; -import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.LittleEndianInput; +import org.apache.poi.util.LittleEndianOutput; /** * This ptg indicates a data table. @@ -43,15 +43,15 @@ public final class TblPtg extends ControlPtg { /** The column number of the upper left corner */ private final int field_2_first_col; - public TblPtg(RecordInputStream in) { + public TblPtg(LittleEndianInput in) { field_1_first_row = in.readUShort(); field_2_first_col = in.readUShort(); } - public void writeBytes(byte [] array, int offset) { - LittleEndian.putByte(array, offset+0, sid); - LittleEndian.putUShort(array, offset+1, field_1_first_row); - LittleEndian.putUShort(array, offset+3, field_2_first_col); + public void write(LittleEndianOutput out) { + out.writeByte(sid + getPtgClass()); + out.writeShort(field_1_first_row); + out.writeShort(field_2_first_col); } public int getSize() { diff --git a/src/java/org/apache/poi/hssf/record/formula/UnionPtg.java b/src/java/org/apache/poi/hssf/record/formula/UnionPtg.java index fdd82ce549..2c80ab6fe6 100644 --- a/src/java/org/apache/poi/hssf/record/formula/UnionPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/UnionPtg.java @@ -17,6 +17,8 @@ package org.apache.poi.hssf.record.formula; +import org.apache.poi.util.LittleEndianOutput; + /** * @author Glen Stampoultzis (glens at apache.org) @@ -39,9 +41,8 @@ public final class UnionPtg extends OperationPtg { return 1; } - public void writeBytes( byte[] array, int offset ) - { - array[ offset + 0 ] = sid; + public void write(LittleEndianOutput out) { + out.writeByte(sid + getPtgClass()); } public String toFormulaString() diff --git a/src/java/org/apache/poi/hssf/record/formula/UnknownPtg.java b/src/java/org/apache/poi/hssf/record/formula/UnknownPtg.java index d073878707..d441a0098d 100644 --- a/src/java/org/apache/poi/hssf/record/formula/UnknownPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/UnknownPtg.java @@ -16,7 +16,7 @@ ==================================================================== */ package org.apache.poi.hssf.record.formula; -import org.apache.poi.hssf.record.RecordInputStream; +import org.apache.poi.util.LittleEndianOutput; /** * @@ -25,22 +25,17 @@ import org.apache.poi.hssf.record.RecordInputStream; */ public class UnknownPtg extends Ptg { private short size = 1; + private final int _sid; - /** Creates new UnknownPtg */ - - public UnknownPtg() - { - } - - public UnknownPtg(RecordInputStream in) { - // doesn't need anything + public UnknownPtg(int sid) { + _sid = sid; } public boolean isBaseToken() { - return true; + return true; } - public void writeBytes(byte [] array, int offset) - { + public void write(LittleEndianOutput out) { + out.writeByte(_sid); } public int getSize() @@ -55,8 +50,6 @@ public class UnknownPtg extends Ptg { public byte getDefaultOperandClass() {return Ptg.CLASS_VALUE;} public Object clone() { - return new UnknownPtg(); + return this; } - - } diff --git a/src/java/org/apache/poi/hssf/record/formula/ValueOperatorPtg.java b/src/java/org/apache/poi/hssf/record/formula/ValueOperatorPtg.java index 7bb632b595..4f9d4be305 100644 --- a/src/java/org/apache/poi/hssf/record/formula/ValueOperatorPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/ValueOperatorPtg.java @@ -17,18 +17,19 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.util.LittleEndianOutput; /** - * Common superclass of all value operators. - * Subclasses include all unary and binary operators except for the reference operators (IntersectionPtg, RangePtg, UnionPtg) + * Common superclass of all value operators. Subclasses include all unary and + * binary operators except for the reference operators (IntersectionPtg, + * RangePtg, UnionPtg) * * @author Josh Micich */ public abstract class ValueOperatorPtg extends OperationPtg { /** - * All Operator Ptgs are base tokens (i.e. are not RVA classified) + * All Operator Ptgs are base tokens (i.e. are not RVA classified) */ public final boolean isBaseToken() { return true; @@ -38,8 +39,8 @@ public abstract class ValueOperatorPtg extends OperationPtg { return Ptg.CLASS_VALUE; } - public final void writeBytes(byte[] array, int offset) { - array[offset + 0] = getSid(); + public void write(LittleEndianOutput out) { + out.writeByte(getSid()); } protected abstract byte getSid(); @@ -47,8 +48,9 @@ public abstract class ValueOperatorPtg extends OperationPtg { public final int getSize() { return 1; } - public final String toFormulaString() { - // TODO - prune this method out of the hierarchy - throw new RuntimeException("toFormulaString(String[] operands) should be used for subclasses of OperationPtgs"); + + public final String toFormulaString() { + // TODO - prune this method out of the hierarchy + throw new RuntimeException("toFormulaString(String[] operands) should be used for subclasses of OperationPtgs"); } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFCellStyle.java b/src/java/org/apache/poi/hssf/usermodel/HSSFCellStyle.java index 84525881a7..5baf8219cc 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFCellStyle.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFCellStyle.java @@ -975,7 +975,7 @@ public class HSSFCellStyle implements CellStyle if(sr == null) { return null; } - if(sr.getType() == StyleRecord.STYLE_BUILT_IN) { + if(sr.isBuiltin()) { return null; } return sr.getName(); @@ -990,7 +990,7 @@ public class HSSFCellStyle implements CellStyle if(sr == null) { sr = workbook.createStyleRecord(index); } - if(sr.getType() == StyleRecord.STYLE_BUILT_IN) { + if(sr.isBuiltin()) { throw new IllegalArgumentException("Unable to set user specified style names for built in styles!"); } sr.setName(styleName); diff --git a/src/java/org/apache/poi/poifs/filesystem/DocumentInputStream.java b/src/java/org/apache/poi/poifs/filesystem/DocumentInputStream.java index 786a8d24e2..ecd110a329 100644 --- a/src/java/org/apache/poi/poifs/filesystem/DocumentInputStream.java +++ b/src/java/org/apache/poi/poifs/filesystem/DocumentInputStream.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -15,437 +14,314 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - package org.apache.poi.poifs.filesystem; -import java.io.*; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.poi.poifs.storage.DataInputBlock; +import org.apache.poi.util.LittleEndianInput; /** * This class provides methods to read a DocumentEntry managed by a - * Filesystem instance. + * {@link POIFSFileSystem} instance. * * @author Marc Johnson (mjohnson at apache dot org) */ - -public class DocumentInputStream - extends InputStream -{ - - // current offset into the Document - private int _current_offset; - - // current marked offset into the Document (used by mark and - // reset) - private int _marked_offset; - - // the Document's size - private int _document_size; - - // have we been closed? - private boolean _closed; - - // the actual Document - private POIFSDocument _document; - - // buffer used to read one byte at a time - private byte[] _tiny_buffer; - - // returned by read operations if we're at end of document - static private final int EOD = -1; - - /** - * Create an InputStream from the specified DocumentEntry - * - * @param document the DocumentEntry to be read - * - * @exception IOException if the DocumentEntry cannot be opened - * (like, maybe it has been deleted?) - */ - - public DocumentInputStream(final DocumentEntry document) - throws IOException - { - _current_offset = 0; - _marked_offset = 0; - _document_size = document.getSize(); - _closed = false; - _tiny_buffer = null; - if (document instanceof DocumentNode) - { - _document = (( DocumentNode ) document).getDocument(); - } - else - { - throw new IOException("Cannot open internal document storage"); - } - } - - /** - * Create an InputStream from the specified Document - * - * @param document the Document to be read - * - * @exception IOException if the DocumentEntry cannot be opened - * (like, maybe it has been deleted?) - */ - - public DocumentInputStream(final POIFSDocument document) - throws IOException - { - _current_offset = 0; - _marked_offset = 0; - _document_size = document.getSize(); - _closed = false; - _tiny_buffer = null; - _document = document; - } - - /** - * Returns the number of bytes that can be read (or skipped over) - * from this input stream without blocking by the next caller of a - * method for this input stream. The next caller might be the same - * thread or or another thread. - * - * @return the number of bytes that can be read from this input - * stream without blocking. - * - * @exception IOException on error (such as the stream has been - * closed) - */ - - public int available() - throws IOException - { - dieIfClosed(); - return _document_size - _current_offset; - } - - /** - * Closes this input stream and releases any system resources - * associated with the stream. - * - * @exception IOException - */ - - public void close() - throws IOException - { - _closed = true; - } - - /** - * Marks the current position in this input stream. A subsequent - * call to the reset method repositions this stream at the last - * marked position so that subsequent reads re-read the same - * bytes. - *

- * The readlimit arguments tells this input stream to allow that - * many bytes to be read before the mark position gets - * invalidated. This implementation, however, does not care. - *

- * The general contract of mark is that, if the method - * markSupported returns true, the stream somehow remembers all - * the bytes read after the call to mark and stands ready to - * supply those same bytes again if and whenever the method reset - * is called. However, the stream is not required to remember any - * data at all if more than readlimit bytes are read from the - * stream before reset is called. But this stream will. - * - * @param ignoredReadlimit the maximum limit of bytes that can be - * read before the mark position becomes - * invalid. Ignored by this - * implementation. - */ - - public void mark(int ignoredReadlimit) - { - _marked_offset = _current_offset; - } - - /** - * Tests if this input stream supports the mark and reset methods. - * - * @return true - */ - - public boolean markSupported() - { - return true; - } - - /** - * Reads the next byte of data from the input stream. The value - * byte is returned as an int in the range 0 to 255. If no byte is - * available because the end of the stream has been reached, the - * value -1 is returned. The definition of this method in - * java.io.InputStream allows this method to block, but it won't. - * - * @return the next byte of data, or -1 if the end of the stream - * is reached. - * - * @exception IOException - */ - - public int read() - throws IOException - { - dieIfClosed(); - if (atEOD()) - { - return EOD; - } - if (_tiny_buffer == null) - { - _tiny_buffer = new byte[ 1 ]; - } - _document.read(_tiny_buffer, _current_offset++); - return ((int)_tiny_buffer[ 0 ]) & 0x000000FF; - } - - /** - * Reads some number of bytes from the input stream and stores - * them into the buffer array b. The number of bytes actually read - * is returned as an integer. The definition of this method in - * java.io.InputStream allows this method to block, but it won't. - *

- * If b is null, a NullPointerException is thrown. If the length - * of b is zero, then no bytes are read and 0 is returned; - * otherwise, there is an attempt to read at least one byte. If no - * byte is available because the stream is at end of file, the - * value -1 is returned; otherwise, at least one byte is read and - * stored into b. - *

- * The first byte read is stored into element b[0], the next one - * into b[1], and so on. The number of bytes read is, at most, - * equal to the length of b. Let k be the number of bytes actually - * read; these bytes will be stored in elements b[0] through - * b[k-1], leaving elements b[k] through b[b.length-1] unaffected. - *

- * If the first byte cannot be read for any reason other than end - * of file, then an IOException is thrown. In particular, an - * IOException is thrown if the input stream has been closed. - *

- * The read(b) method for class InputStream has the same effect as: - *

- * read(b, 0, b.length) - * - * @param b the buffer into which the data is read. - * - * @return the total number of bytes read into the buffer, or -1 - * if there is no more data because the end of the stream - * has been reached. - * - * @exception IOException - * @exception NullPointerException - */ - - public int read(final byte [] b) - throws IOException, NullPointerException - { - return read(b, 0, b.length); - } - - /** - * Reads up to len bytes of data from the input stream into an - * array of bytes. An attempt is made to read as many as len - * bytes, but a smaller number may be read, possibly zero. The - * number of bytes actually read is returned as an integer. - *

- * The definition of this method in java.io.InputStream allows it - * to block, but it won't. - *

- * If b is null, a NullPointerException is thrown. - *

- * If off is negative, or len is negative, or off+len is greater - * than the length of the array b, then an - * IndexOutOfBoundsException is thrown. - *

- * If len is zero, then no bytes are read and 0 is returned; - * otherwise, there is an attempt to read at least one byte. If no - * byte is available because the stream is at end of file, the - * value -1 is returned; otherwise, at least one byte is read and - * stored into b. - *

- * The first byte read is stored into element b[off], the next one - * into b[off+1], and so on. The number of bytes read is, at most, - * equal to len. Let k be the number of bytes actually read; these - * bytes will be stored in elements b[off] through b[off+k-1], - * leaving elements b[off+k] through b[off+len-1] unaffected. - *

- * In every case, elements b[0] through b[off] and elements - * b[off+len] through b[b.length-1] are unaffected. - *

- * If the first byte cannot be read for any reason other than end - * of file, then an IOException is thrown. In particular, an - * IOException is thrown if the input stream has been closed. - * - * @param b the buffer into which the data is read. - * @param off the start offset in array b at which the data is - * written. - * @param len the maximum number of bytes to read. - * - * @return the total number of bytes read into the buffer, or -1 - * if there is no more data because the end of the stream - * has been reached. - * - * @exception IOException - * @exception NullPointerException - * @exception IndexOutOfBoundsException - */ - - public int read(final byte [] b, final int off, final int len) - throws IOException, NullPointerException, IndexOutOfBoundsException - { - dieIfClosed(); - if (b == null) - { - throw new NullPointerException("buffer is null"); - } - if ((off < 0) || (len < 0) || (b.length < (off + len))) - { - throw new IndexOutOfBoundsException( - "can't read past buffer boundaries"); - } - if (len == 0) - { - return 0; - } - if (atEOD()) - { - return EOD; - } - int limit = Math.min(available(), len); - - if ((off == 0) && (limit == b.length)) - { - _document.read(b, _current_offset); - } - else - { - byte[] buffer = new byte[ limit ]; - - _document.read(buffer, _current_offset); - System.arraycopy(buffer, 0, b, off, limit); - } - _current_offset += limit; - return limit; - } - - /** - * Repositions this stream to the position at the time the mark - * method was last called on this input stream. - *

- * The general contract of reset is: - *

- *

- *

- * All well and good ... this class's markSupported method returns - * true and this method does not care whether you've called mark - * at all, or whether you've exceeded the number of bytes - * specified in the last call to mark. We're basically walking a - * byte array ... mark and reset to your heart's content. - */ - - public void reset() - { - _current_offset = _marked_offset; - } - - /** - * Skips over and discards n bytes of data from this input - * stream. The skip method may, for a variety of reasons, end up - * skipping over some smaller number of bytes, possibly 0. This - * may result from any of a number of conditions; reaching end of - * file before n bytes have been skipped is only one - * possibility. The actual number of bytes skipped is returned. If - * n is negative, no bytes are skipped. - * - * @param n the number of bytes to be skipped. - * - * @return the actual number of bytes skipped. - * - * @exception IOException - */ - - public long skip(final long n) - throws IOException - { - dieIfClosed(); - if (n < 0) - { - return 0; - } - int new_offset = _current_offset + ( int ) n; - - if (new_offset < _current_offset) - { - - // wrap around in converting a VERY large long to an int - new_offset = _document_size; - } - else if (new_offset > _document_size) - { - new_offset = _document_size; - } - long rval = new_offset - _current_offset; - - _current_offset = new_offset; - return rval; - } - - private void dieIfClosed() - throws IOException - { - if (_closed) - { - throw new IOException( - "cannot perform requested operation on a closed stream"); - } - } - - private boolean atEOD() - { - return _current_offset == _document_size; - } -} // end public class DocumentInputStream - +public final class DocumentInputStream extends InputStream implements LittleEndianInput { + /** returned by read operations if we're at end of document */ + private static final int EOF = -1; + + private static final int SIZE_SHORT = 2; + private static final int SIZE_INT = 4; + private static final int SIZE_LONG = 8; + + /** current offset into the Document */ + private int _current_offset; + + /** current marked offset into the Document (used by mark and reset) */ + private int _marked_offset; + + /** the Document's size */ + private int _document_size; + + /** have we been closed? */ + private boolean _closed; + + /** the actual Document */ + private POIFSDocument _document; + + /** the data block containing the current stream pointer */ + private DataInputBlock _currentBlock; + + /** + * Create an InputStream from the specified DocumentEntry + * + * @param document the DocumentEntry to be read + * + * @exception IOException if the DocumentEntry cannot be opened (like, maybe it has + * been deleted?) + */ + public DocumentInputStream(DocumentEntry document) throws IOException { + if (!(document instanceof DocumentNode)) { + throw new IOException("Cannot open internal document storage"); + } + _current_offset = 0; + _marked_offset = 0; + _document_size = document.getSize(); + _closed = false; + _document = ((DocumentNode) document).getDocument(); + _currentBlock = getDataInputBlock(0); + } + + /** + * Create an InputStream from the specified Document + * + * @param document the Document to be read + */ + public DocumentInputStream(POIFSDocument document) { + _current_offset = 0; + _marked_offset = 0; + _document_size = document.getSize(); + _closed = false; + _document = document; + _currentBlock = getDataInputBlock(0); + } + + public int available() { + if (_closed) { + throw new IllegalStateException("cannot perform requested operation on a closed stream"); + } + return _document_size - _current_offset; + } + + public void close() { + _closed = true; + } + + public void mark(int ignoredReadlimit) { + _marked_offset = _current_offset; + } + + /** + * Tests if this input stream supports the mark and reset methods. + * + * @return true always + */ + public boolean markSupported() { + return true; + } + + private DataInputBlock getDataInputBlock(int offset) { + return _document.getDataInputBlock(offset); + } + + public int read() throws IOException { + dieIfClosed(); + if (atEOD()) { + return EOF; + } + int result = _currentBlock.readUByte(); + _current_offset++; + if (_currentBlock.available() < 1) { + _currentBlock = getDataInputBlock(_current_offset); + } + return result; + } + + public int read(byte[] b) throws IOException { + return read(b, 0, b.length); + } + + public int read(byte[] b, int off, int len) throws IOException { + dieIfClosed(); + if (b == null) { + throw new IllegalArgumentException("buffer must not be null"); + } + if (off < 0 || len < 0 || b.length < off + len) { + throw new IndexOutOfBoundsException("can't read past buffer boundaries"); + } + if (len == 0) { + return 0; + } + if (atEOD()) { + return EOF; + } + int limit = Math.min(available(), len); + readFully(b, off, limit); + return limit; + } + + /** + * Repositions this stream to the position at the time the mark() method was + * last called on this input stream. If mark() has not been called this + * method repositions the stream to its beginning. + */ + public void reset() { + _current_offset = _marked_offset; + _currentBlock = getDataInputBlock(_current_offset); + } + + public long skip(long n) throws IOException { + dieIfClosed(); + if (n < 0) { + return 0; + } + int new_offset = _current_offset + (int) n; + + if (new_offset < _current_offset) { + + // wrap around in converting a VERY large long to an int + new_offset = _document_size; + } else if (new_offset > _document_size) { + new_offset = _document_size; + } + long rval = new_offset - _current_offset; + + _current_offset = new_offset; + _currentBlock = getDataInputBlock(_current_offset); + return rval; + } + + private void dieIfClosed() throws IOException { + if (_closed) { + throw new IOException("cannot perform requested operation on a closed stream"); + } + } + + private boolean atEOD() { + return _current_offset == _document_size; + } + + private void checkAvaliable(int requestedSize) { + if (_closed) { + throw new IllegalStateException("cannot perform requested operation on a closed stream"); + } + if (requestedSize > _document_size - _current_offset) { + throw new RuntimeException("Buffer underrun - requested " + requestedSize + + " bytes but " + (_document_size - _current_offset) + " was available"); + } + } + + public byte readByte() { + return (byte) readUByte(); + } + + public double readDouble() { + return Double.longBitsToDouble(readLong()); + } + + public void readFully(byte[] buf) { + readFully(buf, 0, buf.length); + } + + public short readShort() { + return (short) readUShort(); + } + + public void readFully(byte[] buf, int off, int len) { + checkAvaliable(len); + int blockAvailable = _currentBlock.available(); + if (blockAvailable > len) { + _currentBlock.readFully(buf, off, len); + _current_offset += len; + return; + } + // else read big amount in chunks + int remaining = len; + int writePos = off; + while (remaining > 0) { + boolean blockIsExpiring = remaining >= blockAvailable; + int reqSize; + if (blockIsExpiring) { + reqSize = blockAvailable; + } else { + reqSize = remaining; + } + _currentBlock.readFully(buf, writePos, reqSize); + remaining -= reqSize; + writePos += reqSize; + _current_offset += reqSize; + if (blockIsExpiring) { + if (_current_offset == _document_size) { + if (remaining > 0) { + throw new IllegalStateException( + "reached end of document stream unexpectedly"); + } + _currentBlock = null; + break; + } + _currentBlock = getDataInputBlock(_current_offset); + blockAvailable = _currentBlock.available(); + } + } + } + + public long readLong() { + checkAvaliable(SIZE_LONG); + int blockAvailable = _currentBlock.available(); + long result; + if (blockAvailable > SIZE_LONG) { + result = _currentBlock.readLongLE(); + } else { + DataInputBlock nextBlock = getDataInputBlock(_current_offset + blockAvailable); + if (blockAvailable == SIZE_LONG) { + result = _currentBlock.readLongLE(); + } else { + result = nextBlock.readLongLE(_currentBlock, blockAvailable); + } + _currentBlock = nextBlock; + } + _current_offset += SIZE_LONG; + return result; + } + + public int readInt() { + checkAvaliable(SIZE_INT); + int blockAvailable = _currentBlock.available(); + int result; + if (blockAvailable > SIZE_INT) { + result = _currentBlock.readIntLE(); + } else { + DataInputBlock nextBlock = getDataInputBlock(_current_offset + blockAvailable); + if (blockAvailable == SIZE_INT) { + result = _currentBlock.readIntLE(); + } else { + result = nextBlock.readIntLE(_currentBlock, blockAvailable); + } + _currentBlock = nextBlock; + } + _current_offset += SIZE_INT; + return result; + } + + public int readUShort() { + checkAvaliable(SIZE_SHORT); + int blockAvailable = _currentBlock.available(); + int result; + if (blockAvailable > SIZE_SHORT) { + result = _currentBlock.readUShortLE(); + } else { + DataInputBlock nextBlock = getDataInputBlock(_current_offset + blockAvailable); + if (blockAvailable == SIZE_SHORT) { + result = _currentBlock.readUShortLE(); + } else { + result = nextBlock.readUShortLE(_currentBlock); + } + _currentBlock = nextBlock; + } + _current_offset += SIZE_SHORT; + return result; + } + + public int readUByte() { + checkAvaliable(1); + int result = _currentBlock.readUByte(); + _current_offset++; + if (_currentBlock.available() < 1) { + _currentBlock = getDataInputBlock(_current_offset); + } + return result; + } +} diff --git a/src/java/org/apache/poi/poifs/filesystem/POIFSDocument.java b/src/java/org/apache/poi/poifs/filesystem/POIFSDocument.java index c313baf489..b783035556 100644 --- a/src/java/org/apache/poi/poifs/filesystem/POIFSDocument.java +++ b/src/java/org/apache/poi/poifs/filesystem/POIFSDocument.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -15,21 +14,26 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - package org.apache.poi.poifs.filesystem; -import java.io.*; - -import java.util.*; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; import org.apache.poi.poifs.common.POIFSConstants; import org.apache.poi.poifs.dev.POIFSViewable; import org.apache.poi.poifs.property.DocumentProperty; import org.apache.poi.poifs.property.Property; import org.apache.poi.poifs.storage.BlockWritable; -import org.apache.poi.poifs.storage.ListManagedBlock; +import org.apache.poi.poifs.storage.DataInputBlock; import org.apache.poi.poifs.storage.DocumentBlock; +import org.apache.poi.poifs.storage.ListManagedBlock; import org.apache.poi.poifs.storage.RawDataBlock; import org.apache.poi.poifs.storage.SmallDocumentBlock; import org.apache.poi.util.HexDump; @@ -39,595 +43,488 @@ import org.apache.poi.util.HexDump; * * @author Marc Johnson (mjohnson at apache dot org) */ - -public class POIFSDocument - implements BATManaged, BlockWritable, POIFSViewable -{ - private DocumentProperty _property; - private int _size; - - // one of these stores will be valid - private SmallBlockStore _small_store; - private BigBlockStore _big_store; - - /** - * Constructor from large blocks - * - * @param name the name of the POIFSDocument - * @param blocks the big blocks making up the POIFSDocument - * @param length the actual length of the POIFSDocument - * - * @exception IOException - */ - - public POIFSDocument(final String name, final RawDataBlock [] blocks, - final int length) - throws IOException - { - _size = length; - _big_store = new BigBlockStore(blocks); - _property = new DocumentProperty(name, _size); - _small_store = new SmallBlockStore(new BlockWritable[ 0 ]); - _property.setDocument(this); - } - - /** - * Constructor from small blocks - * - * @param name the name of the POIFSDocument - * @param blocks the small blocks making up the POIFSDocument - * @param length the actual length of the POIFSDocument - */ - - public POIFSDocument(final String name, - final SmallDocumentBlock [] blocks, final int length) - { - _size = length; - try - { - _big_store = new BigBlockStore(new RawDataBlock[ 0 ]); - } - catch (IOException ignored) - { - - // can't happen with that constructor - } - _property = new DocumentProperty(name, _size); - _small_store = new SmallBlockStore(blocks); - _property.setDocument(this); - } - - /** - * Constructor from small blocks - * - * @param name the name of the POIFSDocument - * @param blocks the small blocks making up the POIFSDocument - * @param length the actual length of the POIFSDocument - * - * @exception IOException - */ - - public POIFSDocument(final String name, final ListManagedBlock [] blocks, - final int length) - throws IOException - { - _size = length; - _property = new DocumentProperty(name, _size); - _property.setDocument(this); - if (Property.isSmall(_size)) - { - _big_store = new BigBlockStore(new RawDataBlock[ 0 ]); - _small_store = new SmallBlockStore(blocks); - } - else - { - _big_store = new BigBlockStore(blocks); - _small_store = new SmallBlockStore(new BlockWritable[ 0 ]); - } - } - - /** - * Constructor - * - * @param name the name of the POIFSDocument - * @param stream the InputStream we read data from - * - * @exception IOException thrown on read errors - */ - - public POIFSDocument(final String name, final InputStream stream) - throws IOException - { - List blocks = new ArrayList(); - - _size = 0; - while (true) - { - DocumentBlock block = new DocumentBlock(stream); - int blockSize = block.size(); - - if (blockSize > 0) - { - blocks.add(block); - _size += blockSize; - } - if (block.partiallyRead()) - { - break; - } - } - DocumentBlock[] bigBlocks = - ( DocumentBlock [] ) blocks.toArray(new DocumentBlock[ 0 ]); - - _big_store = new BigBlockStore(bigBlocks); - _property = new DocumentProperty(name, _size); - _property.setDocument(this); - if (_property.shouldUseSmallBlocks()) - { - _small_store = - new SmallBlockStore(SmallDocumentBlock.convert(bigBlocks, - _size)); - _big_store = new BigBlockStore(new DocumentBlock[ 0 ]); - } - else - { - _small_store = new SmallBlockStore(new BlockWritable[ 0 ]); - } - } - - /** - * Constructor - * - * @param name the name of the POIFSDocument - * @param size the length of the POIFSDocument - * @param path the path of the POIFSDocument - * @param writer the writer who will eventually write the document - * contents - * - * @exception IOException thrown on read errors - */ - - public POIFSDocument(final String name, final int size, - final POIFSDocumentPath path, - final POIFSWriterListener writer) - throws IOException - { - _size = size; - _property = new DocumentProperty(name, _size); - _property.setDocument(this); - if (_property.shouldUseSmallBlocks()) - { - _small_store = new SmallBlockStore(path, name, size, writer); - _big_store = new BigBlockStore(new Object[ 0 ]); - } - else - { - _small_store = new SmallBlockStore(new BlockWritable[ 0 ]); - _big_store = new BigBlockStore(path, name, size, writer); - } - } - - /** - * return the array of SmallDocumentBlocks used - * - * @return array of SmallDocumentBlocks; may be empty, cannot be null - */ - - public BlockWritable [] getSmallBlocks() - { - return _small_store.getBlocks(); - } - - /** - * @return size of the document - */ - - public int getSize() - { - return _size; - } - - /** - * read data from the internal stores - * - * @param buffer the buffer to write to - * @param offset the offset into our storage to read from - */ - - void read(final byte [] buffer, final int offset) - { - if (_property.shouldUseSmallBlocks()) - { - SmallDocumentBlock.read(_small_store.getBlocks(), buffer, offset); - } - else - { - DocumentBlock.read(_big_store.getBlocks(), buffer, offset); - } - } - - /** - * Get the DocumentProperty - * - * @return the instance's DocumentProperty - */ - - DocumentProperty getDocumentProperty() - { - return _property; - } - - /* ********** START implementation of BlockWritable ********** */ - - /** - * Write the storage to an OutputStream - * - * @param stream the OutputStream to which the stored data should - * be written - * - * @exception IOException on problems writing to the specified - * stream - */ - - public void writeBlocks(final OutputStream stream) - throws IOException - { - _big_store.writeBlocks(stream); - } - - /* ********** END implementation of BlockWritable ********** */ - /* ********** START implementation of BATManaged ********** */ - - /** - * Return the number of BigBlock's this instance uses - * - * @return count of BigBlock instances - */ - - public int countBlocks() - { - return _big_store.countBlocks(); - } - - /** - * Set the start block for this instance - * - * @param index index into the array of blocks making up the - * filesystem - */ - - public void setStartBlock(final int index) - { - _property.setStartBlock(index); - } - - /* ********** END implementation of BATManaged ********** */ - /* ********** START begin implementation of POIFSViewable ********** */ - - /** - * Get an array of objects, some of which may implement - * POIFSViewable - * - * @return an array of Object; may not be null, but may be empty - */ - - public Object [] getViewableArray() - { - Object[] results = new Object[ 1 ]; - String result; - - try - { - ByteArrayOutputStream output = new ByteArrayOutputStream(); - BlockWritable[] blocks = null; - - if (_big_store.isValid()) - { - blocks = _big_store.getBlocks(); - } - else if (_small_store.isValid()) - { - blocks = _small_store.getBlocks(); - } - if (blocks != null) - { - for (int k = 0; k < blocks.length; k++) - { - blocks[ k ].writeBlocks(output); - } - byte[] data = output.toByteArray(); - - if (data.length > _property.getSize()) - { - byte[] tmp = new byte[ _property.getSize() ]; - - System.arraycopy(data, 0, tmp, 0, tmp.length); - data = tmp; - } - output = new ByteArrayOutputStream(); - HexDump.dump(data, 0, output, 0); - result = output.toString(); - } - else - { - result = ""; - } - } - catch (IOException e) - { - result = e.getMessage(); - } - results[ 0 ] = result; - return results; - } - - /** - * Get an Iterator of objects, some of which may implement - * POIFSViewable - * - * @return an Iterator; may not be null, but may have an empty - * back end store - */ - - public Iterator getViewableIterator() - { - return Collections.EMPTY_LIST.iterator(); - } - - /** - * Give viewers a hint as to whether to call getViewableArray or - * getViewableIterator - * - * @return true if a viewer should call getViewableArray, false if - * a viewer should call getViewableIterator - */ - - public boolean preferArray() - { - return true; - } - - /** - * Provides a short description of the object, to be used when a - * POIFSViewable object has not provided its contents. - * - * @return short description - */ - - public String getShortDescription() - { - StringBuffer buffer = new StringBuffer(); - - buffer.append("Document: \"").append(_property.getName()) - .append("\""); - buffer.append(" size = ").append(getSize()); - return buffer.toString(); - } - - /* ********** END begin implementation of POIFSViewable ********** */ - private class SmallBlockStore - { - private SmallDocumentBlock[] smallBlocks; - private POIFSDocumentPath path; - private String name; - private int size; - private POIFSWriterListener writer; - - /** - * Constructor - * - * @param blocks blocks to construct the store from - */ - - SmallBlockStore(final Object [] blocks) - { - smallBlocks = new SmallDocumentBlock[ blocks.length ]; - for (int j = 0; j < blocks.length; j++) - { - smallBlocks[ j ] = ( SmallDocumentBlock ) blocks[ j ]; - } - this.path = null; - this.name = null; - this.size = -1; - this.writer = null; - } - - /** - * Constructor for a small block store that will be written - * later - * - * @param path path of the document - * @param name name of the document - * @param size length of the document - * @param writer the object that will eventually write the document - */ - - SmallBlockStore(final POIFSDocumentPath path, final String name, - final int size, final POIFSWriterListener writer) - { - smallBlocks = new SmallDocumentBlock[ 0 ]; - this.path = path; - this.name = name; - this.size = size; - this.writer = writer; - } - - /** - * @return true if this store is a valid source of data - */ - - boolean isValid() - { - return ((smallBlocks.length > 0) || (writer != null)); - } - - /** - * @return the SmallDocumentBlocks - */ - - BlockWritable [] getBlocks() - { - if (isValid() && (writer != null)) - { - ByteArrayOutputStream stream = - new ByteArrayOutputStream(size); - DocumentOutputStream dstream = - new DocumentOutputStream(stream, size); - - writer.processPOIFSWriterEvent(new POIFSWriterEvent(dstream, - path, name, size)); - smallBlocks = SmallDocumentBlock.convert(stream.toByteArray(), - size); - } - return smallBlocks; - } - } // end private class SmallBlockStore - - private class BigBlockStore - { - private DocumentBlock[] bigBlocks; - private POIFSDocumentPath path; - private String name; - private int size; - private POIFSWriterListener writer; - - /** - * Constructor - * - * @param blocks the blocks making up the store - * - * @exception IOException on I/O error - */ - - BigBlockStore(final Object [] blocks) - throws IOException - { - bigBlocks = new DocumentBlock[ blocks.length ]; - for (int j = 0; j < blocks.length; j++) - { - if (blocks[ j ] instanceof DocumentBlock) - { - bigBlocks[ j ] = ( DocumentBlock ) blocks[ j ]; - } - else - { - bigBlocks[ j ] = - new DocumentBlock(( RawDataBlock ) blocks[ j ]); - } - } - this.path = null; - this.name = null; - this.size = -1; - this.writer = null; - } - - /** - * Constructor for a big block store that will be written - * later - * - * @param path path of the document - * @param name name of the document - * @param size length of the document - * @param writer the object that will eventually write the - * document - */ - - BigBlockStore(final POIFSDocumentPath path, final String name, - final int size, final POIFSWriterListener writer) - { - bigBlocks = new DocumentBlock[ 0 ]; - this.path = path; - this.name = name; - this.size = size; - this.writer = writer; - } - - /** - * @return true if this store is a valid source of data - */ - - boolean isValid() - { - return ((bigBlocks.length > 0) || (writer != null)); - } - - /** - * @return the DocumentBlocks - */ - - DocumentBlock [] getBlocks() - { - if (isValid() && (writer != null)) - { - ByteArrayOutputStream stream = - new ByteArrayOutputStream(size); - DocumentOutputStream dstream = - new DocumentOutputStream(stream, size); - - writer.processPOIFSWriterEvent(new POIFSWriterEvent(dstream, - path, name, size)); - bigBlocks = DocumentBlock.convert(stream.toByteArray(), size); - } - return bigBlocks; - } - - /** - * write the blocks to a stream - * - * @param stream the stream to which the data is to be written - * - * @exception IOException on error - */ - - void writeBlocks(OutputStream stream) - throws IOException - { - if (isValid()) - { - if (writer != null) - { - DocumentOutputStream dstream = - new DocumentOutputStream(stream, size); - - writer.processPOIFSWriterEvent( - new POIFSWriterEvent(dstream, path, name, size)); - dstream.writeFiller(countBlocks() - * POIFSConstants - .BIG_BLOCK_SIZE, DocumentBlock - .getFillByte()); - } - else - { - for (int k = 0; k < bigBlocks.length; k++) - { - bigBlocks[ k ].writeBlocks(stream); - } - } - } - } - - /** - * @return number of big blocks making up this document - */ - - int countBlocks() - { - int rval = 0; - - if (isValid()) - { - if (writer != null) - { - rval = (size + POIFSConstants.BIG_BLOCK_SIZE - 1) - / POIFSConstants.BIG_BLOCK_SIZE; - } - else - { - rval = bigBlocks.length; - } - } - return rval; - } - } // end private class BigBlockStore -} // end class POIFSDocument - +public final class POIFSDocument implements BATManaged, BlockWritable, POIFSViewable { + private static final DocumentBlock[] EMPTY_BIG_BLOCK_ARRAY = { }; + private static final SmallDocumentBlock[] EMPTY_SMALL_BLOCK_ARRAY = { }; + private DocumentProperty _property; + private int _size; + + // one of these stores will be valid + private SmallBlockStore _small_store; + private BigBlockStore _big_store; + + /** + * Constructor from large blocks + * + * @param name the name of the POIFSDocument + * @param blocks the big blocks making up the POIFSDocument + * @param length the actual length of the POIFSDocument + */ + public POIFSDocument(String name, RawDataBlock[] blocks, int length) throws IOException { + _size = length; + _big_store = new BigBlockStore(convertRawBlocksToBigBlocks(blocks)); + _property = new DocumentProperty(name, _size); + _small_store = new SmallBlockStore(EMPTY_SMALL_BLOCK_ARRAY); + _property.setDocument(this); + } + + // TODO - awkward typing going on here + private static DocumentBlock[] convertRawBlocksToBigBlocks(ListManagedBlock[] blocks) throws IOException { + DocumentBlock[] result = new DocumentBlock[blocks.length]; + for (int i = 0; i < result.length; i++) { + result[i] = new DocumentBlock((RawDataBlock)blocks[i]); + } + return result; + } + private static SmallDocumentBlock[] convertRawBlocksToSmallBlocks(ListManagedBlock[] blocks) { + if (blocks instanceof SmallDocumentBlock[]) { + return (SmallDocumentBlock[]) blocks; + } + SmallDocumentBlock[] result = new SmallDocumentBlock[blocks.length]; + System.arraycopy(blocks, 0, result, 0, blocks.length); + return result; + } + + /** + * Constructor from small blocks + * + * @param name the name of the POIFSDocument + * @param blocks the small blocks making up the POIFSDocument + * @param length the actual length of the POIFSDocument + */ + public POIFSDocument(String name, SmallDocumentBlock[] blocks, int length) { + _size = length; + _big_store = new BigBlockStore(EMPTY_BIG_BLOCK_ARRAY); + _property = new DocumentProperty(name, _size); + _small_store = new SmallBlockStore(blocks); + _property.setDocument(this); + } + + /** + * Constructor from small blocks + * + * @param name the name of the POIFSDocument + * @param blocks the small blocks making up the POIFSDocument + * @param length the actual length of the POIFSDocument + */ + public POIFSDocument(String name, ListManagedBlock[] blocks, int length) throws IOException { + _size = length; + _property = new DocumentProperty(name, _size); + _property.setDocument(this); + if (Property.isSmall(_size)) { + _big_store = new BigBlockStore(EMPTY_BIG_BLOCK_ARRAY); + _small_store = new SmallBlockStore(convertRawBlocksToSmallBlocks(blocks)); + } else { + _big_store = new BigBlockStore(convertRawBlocksToBigBlocks(blocks)); + _small_store = new SmallBlockStore(EMPTY_SMALL_BLOCK_ARRAY); + } + } + + /** + * Constructor + * + * @param name the name of the POIFSDocument + * @param stream the InputStream we read data from + */ + public POIFSDocument(String name, InputStream stream) throws IOException { + List blocks = new ArrayList(); + + _size = 0; + while (true) { + DocumentBlock block = new DocumentBlock(stream); + int blockSize = block.size(); + + if (blockSize > 0) { + blocks.add(block); + _size += blockSize; + } + if (block.partiallyRead()) { + break; + } + } + DocumentBlock[] bigBlocks = (DocumentBlock[]) blocks.toArray(new DocumentBlock[blocks.size()]); + + _big_store = new BigBlockStore(bigBlocks); + _property = new DocumentProperty(name, _size); + _property.setDocument(this); + if (_property.shouldUseSmallBlocks()) { + _small_store = new SmallBlockStore(SmallDocumentBlock.convert(bigBlocks, _size)); + _big_store = new BigBlockStore(new DocumentBlock[0]); + } else { + _small_store = new SmallBlockStore(EMPTY_SMALL_BLOCK_ARRAY); + } + } + + /** + * Constructor + * + * @param name the name of the POIFSDocument + * @param size the length of the POIFSDocument + * @param path the path of the POIFSDocument + * @param writer the writer who will eventually write the document contents + */ + public POIFSDocument(String name, int size, POIFSDocumentPath path, POIFSWriterListener writer) { + _size = size; + _property = new DocumentProperty(name, _size); + _property.setDocument(this); + if (_property.shouldUseSmallBlocks()) { + _small_store = new SmallBlockStore(path, name, size, writer); + _big_store = new BigBlockStore(EMPTY_BIG_BLOCK_ARRAY); + } else { + _small_store = new SmallBlockStore(EMPTY_SMALL_BLOCK_ARRAY); + _big_store = new BigBlockStore(path, name, size, writer); + } + } + + /** + * @return array of SmallDocumentBlocks; may be empty, cannot be null + */ + public BlockWritable[] getSmallBlocks() { + return _small_store.getBlocks(); + } + + /** + * @return size of the document + */ + public int getSize() { + return _size; + } + + /** + * read data from the internal stores + * + * @param buffer the buffer to write to + * @param offset the offset into our storage to read from + * This method is currently (Oct 2008) only used by test code. Perhaps it can be deleted + */ + void read(byte[] buffer, int offset) { + int len = buffer.length; + + DataInputBlock currentBlock = getDataInputBlock(offset); + + int blockAvailable = currentBlock.available(); + if (blockAvailable > len) { + currentBlock.readFully(buffer, 0, len); + return; + } + // else read big amount in chunks + int remaining = len; + int writePos = 0; + int currentOffset = offset; + while (remaining > 0) { + boolean blockIsExpiring = remaining >= blockAvailable; + int reqSize; + if (blockIsExpiring) { + reqSize = blockAvailable; + } else { + reqSize = remaining; + } + currentBlock.readFully(buffer, writePos, reqSize); + remaining-=reqSize; + writePos+=reqSize; + currentOffset += reqSize; + if (blockIsExpiring) { + if (currentOffset == _size) { + if (remaining > 0) { + throw new IllegalStateException("reached end of document stream unexpectedly"); + } + currentBlock = null; + break; + } + currentBlock = getDataInputBlock(currentOffset); + blockAvailable = currentBlock.available(); + } + } + } + + /** + * @return null if offset points to the end of the document stream + */ + DataInputBlock getDataInputBlock(int offset) { + if (offset >= _size) { + if (offset > _size) { + throw new RuntimeException("Request for Offset " + offset + " doc size is " + _size); + } + return null; + } + if (_property.shouldUseSmallBlocks()) { + return SmallDocumentBlock.getDataInputBlock(_small_store.getBlocks(), offset); + } else { + return DocumentBlock.getDataInputBlock(_big_store.getBlocks(), offset); + } + } + + /** + * @return the instance's DocumentProperty + */ + + DocumentProperty getDocumentProperty() { + return _property; + } + + /* ********** START implementation of BlockWritable ********** */ + + /** + * Write the storage to an OutputStream + * + * @param stream the OutputStream to which the stored data should be written + */ + public void writeBlocks(OutputStream stream) throws IOException { + _big_store.writeBlocks(stream); + } + + /* ********** END implementation of BlockWritable ********** */ + /* ********** START implementation of BATManaged ********** */ + + /** + * Return the number of BigBlock's this instance uses + * + * @return count of BigBlock instances + */ + public int countBlocks() { + return _big_store.countBlocks(); + } + + /** + * Set the start block for this instance + * + * @param index index into the array of blocks making up the filesystem + */ + public void setStartBlock(int index) { + _property.setStartBlock(index); + } + + /* ********** END implementation of BATManaged ********** */ + /* ********** START begin implementation of POIFSViewable ********** */ + + /** + * Get an array of objects, some of which may implement POIFSViewable + * + * @return an array of Object; may not be null, but may be empty + */ + public Object[] getViewableArray() { + Object[] results = new Object[1]; + String result; + + try { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + BlockWritable[] blocks = null; + + if (_big_store.isValid()) { + blocks = _big_store.getBlocks(); + } else if (_small_store.isValid()) { + blocks = _small_store.getBlocks(); + } + if (blocks != null) { + for (int k = 0; k < blocks.length; k++) { + blocks[k].writeBlocks(output); + } + byte[] data = output.toByteArray(); + + if (data.length > _property.getSize()) { + byte[] tmp = new byte[_property.getSize()]; + + System.arraycopy(data, 0, tmp, 0, tmp.length); + data = tmp; + } + output = new ByteArrayOutputStream(); + HexDump.dump(data, 0, output, 0); + result = output.toString(); + } else { + result = ""; + } + } catch (IOException e) { + result = e.getMessage(); + } + results[0] = result; + return results; + } + + /** + * Get an Iterator of objects, some of which may implement POIFSViewable + * + * @return an Iterator; may not be null, but may have an empty back end + * store + */ + public Iterator getViewableIterator() { + return Collections.EMPTY_LIST.iterator(); + } + + /** + * Give viewers a hint as to whether to call getViewableArray or + * getViewableIterator + * + * @return true if a viewer should call getViewableArray, + * false if a viewer should call getViewableIterator + */ + public boolean preferArray() { + return true; + } + + /** + * Provides a short description of the object, to be used when a + * POIFSViewable object has not provided its contents. + * + * @return short description + */ + public String getShortDescription() { + StringBuffer buffer = new StringBuffer(); + + buffer.append("Document: \"").append(_property.getName()).append("\""); + buffer.append(" size = ").append(getSize()); + return buffer.toString(); + } + + /* ********** END begin implementation of POIFSViewable ********** */ + private static final class SmallBlockStore { + private SmallDocumentBlock[] smallBlocks; + private final POIFSDocumentPath path; + private final String name; + private final int size; + private final POIFSWriterListener writer; + + /** + * Constructor + * + * @param blocks blocks to construct the store from + */ + SmallBlockStore(SmallDocumentBlock[] blocks) { + smallBlocks = (SmallDocumentBlock[]) blocks.clone(); + this.path = null; + this.name = null; + this.size = -1; + this.writer = null; + } + + /** + * Constructor for a small block store that will be written later + * + * @param path path of the document + * @param name name of the document + * @param size length of the document + * @param writer the object that will eventually write the document + */ + SmallBlockStore(POIFSDocumentPath path, String name, int size, POIFSWriterListener writer) { + smallBlocks = new SmallDocumentBlock[0]; + this.path = path; + this.name = name; + this.size = size; + this.writer = writer; + } + + /** + * @return true if this store is a valid source of data + */ + boolean isValid() { + return smallBlocks.length > 0 || writer != null; + } + + /** + * @return the SmallDocumentBlocks + */ + SmallDocumentBlock[] getBlocks() { + if (isValid() && writer != null) { + ByteArrayOutputStream stream = new ByteArrayOutputStream(size); + DocumentOutputStream dstream = new DocumentOutputStream(stream, size); + + writer.processPOIFSWriterEvent(new POIFSWriterEvent(dstream, path, name, size)); + smallBlocks = SmallDocumentBlock.convert(stream.toByteArray(), size); + } + return smallBlocks; + } + } // end private class SmallBlockStore + + private static final class BigBlockStore { + private DocumentBlock[] bigBlocks; + private final POIFSDocumentPath path; + private final String name; + private final int size; + private final POIFSWriterListener writer; + + /** + * Constructor + * + * @param blocks the blocks making up the store + */ + BigBlockStore(DocumentBlock[] blocks) { + bigBlocks = (DocumentBlock[]) blocks.clone(); + this.path = null; + this.name = null; + this.size = -1; + this.writer = null; + } + + /** + * Constructor for a big block store that will be written later + * + * @param path path of the document + * @param name name of the document + * @param size length of the document + * @param writer the object that will eventually write the document + */ + BigBlockStore(POIFSDocumentPath path, String name, int size, POIFSWriterListener writer) { + bigBlocks = new DocumentBlock[0]; + this.path = path; + this.name = name; + this.size = size; + this.writer = writer; + } + + /** + * @return true if this store is a valid source of data + */ + boolean isValid() { + return bigBlocks.length > 0 || writer != null; + } + + /** + * @return the DocumentBlocks + */ + DocumentBlock[] getBlocks() { + if (isValid() && writer != null) { + ByteArrayOutputStream stream = new ByteArrayOutputStream(size); + DocumentOutputStream dstream = new DocumentOutputStream(stream, size); + + writer.processPOIFSWriterEvent(new POIFSWriterEvent(dstream, path, name, size)); + bigBlocks = DocumentBlock.convert(stream.toByteArray(), size); + } + return bigBlocks; + } + + /** + * write the blocks to a stream + * + * @param stream the stream to which the data is to be written + */ + void writeBlocks(OutputStream stream) throws IOException { + if (isValid()) { + if (writer != null) { + DocumentOutputStream dstream = new DocumentOutputStream(stream, size); + + writer.processPOIFSWriterEvent(new POIFSWriterEvent(dstream, path, name, size)); + dstream.writeFiller(countBlocks() * POIFSConstants.BIG_BLOCK_SIZE, + DocumentBlock.getFillByte()); + } else { + for (int k = 0; k < bigBlocks.length; k++) { + bigBlocks[k].writeBlocks(stream); + } + } + } + } + + /** + * @return number of big blocks making up this document + */ + int countBlocks() { + + if (isValid()) { + if (writer == null) { + return bigBlocks.length; + } + return (size + POIFSConstants.BIG_BLOCK_SIZE - 1) + / POIFSConstants.BIG_BLOCK_SIZE; + } + return 0; + } + } // end private class BigBlockStore +} diff --git a/src/java/org/apache/poi/poifs/storage/DataInputBlock.java b/src/java/org/apache/poi/poifs/storage/DataInputBlock.java new file mode 100644 index 0000000000..a571a4f22e --- /dev/null +++ b/src/java/org/apache/poi/poifs/storage/DataInputBlock.java @@ -0,0 +1,186 @@ +/* ==================================================================== + 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.poifs.storage; + +/** + * Wraps a byte array and provides simple data input access. + * Internally, this class maintains a buffer read index, so that for the most part, primitive + * data can be read in a data-input-stream-like manner.

+ * + * Note - the calling class should call the {@link #available()} method to detect end-of-buffer + * and move to the next data block when the current is exhausted. + * For optimisation reasons, no error handling is performed in this class. Thus, mistakes in + * calling code ran may raise ugly exceptions here, like {@link ArrayIndexOutOfBoundsException}, + * etc .

+ * + * The multi-byte primitive input methods ({@link #readUShortLE()}, {@link #readIntLE()} and + * {@link #readLongLE()}) have corresponding 'spanning read' methods which (when required) perform + * a read across the block boundary. These spanning read methods take the previous + * {@link DataInputBlock} as a parameter. + * Reads of larger amounts of data (into byte array buffers) must be managed by the caller + * since these could conceivably involve more than two blocks. + * + * @author Josh Micich + */ +public final class DataInputBlock { + + /** + * Possibly any size (usually 512K or 64K). Assumed to be at least 8 bytes for all blocks + * before the end of the stream. The last block in the stream can be any size except zero. + */ + private final byte[] _buf; + private int _readIndex; + private int _maxIndex; + + DataInputBlock(byte[] data, int startOffset) { + _buf = data; + _readIndex = startOffset; + _maxIndex = _buf.length; + } + public int available() { + return _maxIndex-_readIndex; + } + + public int readUByte() { + return _buf[_readIndex++] & 0xFF; + } + + /** + * Reads a short which was encoded in little endian format. + */ + public int readUShortLE() { + int i = _readIndex; + + int b0 = _buf[i++] & 0xFF; + int b1 = _buf[i++] & 0xFF; + _readIndex = i; + return (b1 << 8) + (b0 << 0); + } + + /** + * Reads a short which spans the end of prevBlock and the start of this block. + */ + public int readUShortLE(DataInputBlock prevBlock) { + // simple case - will always be one byte in each block + int i = prevBlock._buf.length-1; + + int b0 = prevBlock._buf[i++] & 0xFF; + int b1 = _buf[_readIndex++] & 0xFF; + return (b1 << 8) + (b0 << 0); + } + + /** + * Reads an int which was encoded in little endian format. + */ + public int readIntLE() { + int i = _readIndex; + + int b0 = _buf[i++] & 0xFF; + int b1 = _buf[i++] & 0xFF; + int b2 = _buf[i++] & 0xFF; + int b3 = _buf[i++] & 0xFF; + _readIndex = i; + return (b3 << 24) + (b2 << 16) + (b1 << 8) + (b0 << 0); + } + + /** + * Reads an int which spans the end of prevBlock and the start of this block. + */ + public int readIntLE(DataInputBlock prevBlock, int prevBlockAvailable) { + byte[] buf = new byte[4]; + + readSpanning(prevBlock, prevBlockAvailable, buf); + int b0 = buf[0] & 0xFF; + int b1 = buf[1] & 0xFF; + int b2 = buf[2] & 0xFF; + int b3 = buf[3] & 0xFF; + return (b3 << 24) + (b2 << 16) + (b1 << 8) + (b0 << 0); + } + + /** + * Reads a long which was encoded in little endian format. + */ + public long readLongLE() { + int i = _readIndex; + + int b0 = _buf[i++] & 0xFF; + int b1 = _buf[i++] & 0xFF; + int b2 = _buf[i++] & 0xFF; + int b3 = _buf[i++] & 0xFF; + int b4 = _buf[i++] & 0xFF; + int b5 = _buf[i++] & 0xFF; + int b6 = _buf[i++] & 0xFF; + int b7 = _buf[i++] & 0xFF; + _readIndex = i; + return (((long)b7 << 56) + + ((long)b6 << 48) + + ((long)b5 << 40) + + ((long)b4 << 32) + + ((long)b3 << 24) + + (b2 << 16) + + (b1 << 8) + + (b0 << 0)); + } + + /** + * Reads a long which spans the end of prevBlock and the start of this block. + */ + public long readLongLE(DataInputBlock prevBlock, int prevBlockAvailable) { + byte[] buf = new byte[8]; + + readSpanning(prevBlock, prevBlockAvailable, buf); + + int b0 = buf[0] & 0xFF; + int b1 = buf[1] & 0xFF; + int b2 = buf[2] & 0xFF; + int b3 = buf[3] & 0xFF; + int b4 = buf[4] & 0xFF; + int b5 = buf[5] & 0xFF; + int b6 = buf[6] & 0xFF; + int b7 = buf[7] & 0xFF; + return (((long)b7 << 56) + + ((long)b6 << 48) + + ((long)b5 << 40) + + ((long)b4 << 32) + + ((long)b3 << 24) + + (b2 << 16) + + (b1 << 8) + + (b0 << 0)); + } + + /** + * Reads a small amount of data from across the boundary between two blocks. + * The {@link #_readIndex} of this (the second) block is updated accordingly. + * Note- this method (and other code) assumes that the second {@link DataInputBlock} + * always is big enough to complete the read without being exhausted. + */ + private void readSpanning(DataInputBlock prevBlock, int prevBlockAvailable, byte[] buf) { + System.arraycopy(prevBlock._buf, prevBlock._readIndex, buf, 0, prevBlockAvailable); + int secondReadLen = buf.length-prevBlockAvailable; + System.arraycopy(_buf, 0, buf, prevBlockAvailable, secondReadLen); + _readIndex = secondReadLen; + } + + /** + * Reads len bytes from this block into the supplied buffer. + */ + public void readFully(byte[] buf, int off, int len) { + System.arraycopy(_buf, _readIndex, buf, off, len); + _readIndex += len; + } +} diff --git a/src/java/org/apache/poi/poifs/storage/DocumentBlock.java b/src/java/org/apache/poi/poifs/storage/DocumentBlock.java index ddaf5b3da0..7c1f028eda 100644 --- a/src/java/org/apache/poi/poifs/storage/DocumentBlock.java +++ b/src/java/org/apache/poi/poifs/storage/DocumentBlock.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -15,31 +14,27 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - package org.apache.poi.poifs.storage; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; - import java.util.Arrays; import org.apache.poi.poifs.common.POIFSConstants; import org.apache.poi.util.IOUtils; -import org.apache.poi.util.IntegerField; -import org.apache.poi.util.LittleEndian; -import org.apache.poi.util.LittleEndianConsts; /** * A block of document data. * * @author Marc Johnson (mjohnson at apache dot org) */ +public final class DocumentBlock extends BigBlock { + private static final int BLOCK_SHIFT = 9; + private static final int BLOCK_SIZE = 1 << BLOCK_SHIFT; + private static final int BLOCK_MASK = BLOCK_SIZE-1; -public class DocumentBlock - extends BigBlock -{ private static final byte _default_value = ( byte ) 0xFF; private byte[] _data; private int _bytes_read; @@ -161,45 +156,10 @@ public class DocumentBlock return rval; } - /** - * read data from an array of DocumentBlocks - * - * @param blocks the blocks to read from - * @param buffer the buffer to write the data into - * @param offset the offset into the array of blocks to read from - */ - - public static void read(final DocumentBlock [] blocks, - final byte [] buffer, final int offset) - { - int firstBlockIndex = offset / POIFSConstants.BIG_BLOCK_SIZE; - int firstBlockOffset = offset % POIFSConstants.BIG_BLOCK_SIZE; - int lastBlockIndex = (offset + buffer.length - 1) - / POIFSConstants.BIG_BLOCK_SIZE; - - if (firstBlockIndex == lastBlockIndex) - { - System.arraycopy(blocks[ firstBlockIndex ]._data, - firstBlockOffset, buffer, 0, buffer.length); - } - else - { - int buffer_offset = 0; - - System.arraycopy(blocks[ firstBlockIndex ]._data, - firstBlockOffset, buffer, buffer_offset, - POIFSConstants.BIG_BLOCK_SIZE - - firstBlockOffset); - buffer_offset += POIFSConstants.BIG_BLOCK_SIZE - firstBlockOffset; - for (int j = firstBlockIndex + 1; j < lastBlockIndex; j++) - { - System.arraycopy(blocks[ j ]._data, 0, buffer, buffer_offset, - POIFSConstants.BIG_BLOCK_SIZE); - buffer_offset += POIFSConstants.BIG_BLOCK_SIZE; - } - System.arraycopy(blocks[ lastBlockIndex ]._data, 0, buffer, - buffer_offset, buffer.length - buffer_offset); - } + public static DataInputBlock getDataInputBlock(DocumentBlock[] blocks, int offset) { + int firstBlockIndex = offset >> BLOCK_SHIFT; + int firstBlockOffset= offset & BLOCK_MASK; + return new DataInputBlock(blocks[firstBlockIndex]._data, firstBlockOffset); } /* ********** START extension of BigBlock ********** */ diff --git a/src/java/org/apache/poi/poifs/storage/SmallDocumentBlock.java b/src/java/org/apache/poi/poifs/storage/SmallDocumentBlock.java index dabf280e70..5ae4f1ff03 100644 --- a/src/java/org/apache/poi/poifs/storage/SmallDocumentBlock.java +++ b/src/java/org/apache/poi/poifs/storage/SmallDocumentBlock.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -15,13 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - package org.apache.poi.poifs.storage; -import java.io.*; - -import java.util.*; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import org.apache.poi.poifs.common.POIFSConstants; @@ -31,13 +32,14 @@ import org.apache.poi.poifs.common.POIFSConstants; * * @author Marc Johnson (mjohnson at apache dot org) */ +public final class SmallDocumentBlock implements BlockWritable, ListManagedBlock { + private static final int BLOCK_SHIFT = 6; -public class SmallDocumentBlock - implements BlockWritable, ListManagedBlock -{ private byte[] _data; private static final byte _default_fill = ( byte ) 0xff; - private static final int _block_size = 64; + private static final int _block_size = 1 << BLOCK_SHIFT; + private static final int BLOCK_MASK = _block_size-1; + private static final int _blocks_per_big_block = POIFSConstants.BIG_BLOCK_SIZE / _block_size; @@ -178,46 +180,10 @@ public class SmallDocumentBlock return sdbs; } - /** - * read data from an array of SmallDocumentBlocks - * - * @param blocks the blocks to read from - * @param buffer the buffer to write the data into - * @param offset the offset into the array of blocks to read from - */ - - public static void read(final BlockWritable [] blocks, - final byte [] buffer, final int offset) - { - int firstBlockIndex = offset / _block_size; - int firstBlockOffset = offset % _block_size; - int lastBlockIndex = (offset + buffer.length - 1) / _block_size; - - if (firstBlockIndex == lastBlockIndex) - { - System.arraycopy( - (( SmallDocumentBlock ) blocks[ firstBlockIndex ])._data, - firstBlockOffset, buffer, 0, buffer.length); - } - else - { - int buffer_offset = 0; - - System.arraycopy( - (( SmallDocumentBlock ) blocks[ firstBlockIndex ])._data, - firstBlockOffset, buffer, buffer_offset, - _block_size - firstBlockOffset); - buffer_offset += _block_size - firstBlockOffset; - for (int j = firstBlockIndex + 1; j < lastBlockIndex; j++) - { - System.arraycopy((( SmallDocumentBlock ) blocks[ j ])._data, - 0, buffer, buffer_offset, _block_size); - buffer_offset += _block_size; - } - System.arraycopy( - (( SmallDocumentBlock ) blocks[ lastBlockIndex ])._data, 0, - buffer, buffer_offset, buffer.length - buffer_offset); - } + public static DataInputBlock getDataInputBlock(SmallDocumentBlock[] blocks, int offset) { + int firstBlockIndex = offset >> BLOCK_SHIFT; + int firstBlockOffset= offset & BLOCK_MASK; + return new DataInputBlock(blocks[firstBlockIndex]._data, firstBlockOffset); } /** diff --git a/src/java/org/apache/poi/util/DoubleList.java b/src/java/org/apache/poi/util/DoubleList.java deleted file mode 100644 index d2c669ca8d..0000000000 --- a/src/java/org/apache/poi/util/DoubleList.java +++ /dev/null @@ -1,642 +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.util; - -import java.util.*; - -/** - * A List of double's; as full an implementation of the java.util.List - * interface as possible, with an eye toward minimal creation of - * objects - * - * the mimicry of List is as follows: - *

- * - * the mimicry is not perfect, however: - * - * - * @author Marc Johnson - */ - -public class DoubleList -{ - private double[] _array; - private int _limit; - private static final int _default_size = 128; - - /** - * create an DoubleList of default size - */ - - public DoubleList() - { - this(_default_size); - } - - /** - * create a copy of an existing DoubleList - * - * @param list the existing DoubleList - */ - - public DoubleList(final DoubleList list) - { - this(list._array.length); - System.arraycopy(list._array, 0, _array, 0, _array.length); - _limit = list._limit; - } - - /** - * create an DoubleList with a predefined initial size - * - * @param initialCapacity the size for the internal array - */ - - public DoubleList(final int initialCapacity) - { - _array = new double[ initialCapacity ]; - _limit = 0; - } - - /** - * add the specfied value at the specified index - * - * @param index the index where the new value is to be added - * @param value the new value - * - * @exception IndexOutOfBoundsException if the index is out of - * range (index < 0 || index > size()). - */ - - public void add(final int index, final double value) - { - if (index > _limit) - { - throw new IndexOutOfBoundsException(); - } - else if (index == _limit) - { - add(value); - } - else - { - - // index < limit -- insert into the middle - if (_limit == _array.length) - { - growArray(_limit * 2); - } - System.arraycopy(_array, index, _array, index + 1, - _limit - index); - _array[ index ] = value; - _limit++; - } - } - - /** - * Appends the specified element to the end of this list - * - * @param value element to be appended to this list. - * - * @return true (as per the general contract of the Collection.add - * method). - */ - - public boolean add(final double value) - { - if (_limit == _array.length) - { - growArray(_limit * 2); - } - _array[ _limit++ ] = value; - return true; - } - - /** - * Appends all of the elements in the specified collection to the - * end of this list, in the order that they are returned by the - * specified collection's iterator. The behavior of this - * operation is unspecified if the specified collection is - * modified while the operation is in progress. (Note that this - * will occur if the specified collection is this list, and it's - * nonempty.) - * - * @param c collection whose elements are to be added to this - * list. - * - * @return true if this list changed as a result of the call. - */ - - public boolean addAll(final DoubleList c) - { - if (c._limit != 0) - { - if ((_limit + c._limit) > _array.length) - { - growArray(_limit + c._limit); - } - System.arraycopy(c._array, 0, _array, _limit, c._limit); - _limit += c._limit; - } - return true; - } - - /** - * Inserts all of the elements in the specified collection into - * this list at the specified position. Shifts the element - * currently at that position (if any) and any subsequent elements - * to the right (increases their indices). The new elements will - * appear in this list in the order that they are returned by the - * specified collection's iterator. The behavior of this - * operation is unspecified if the specified collection is - * modified while the operation is in progress. (Note that this - * will occur if the specified collection is this list, and it's - * nonempty.) - * - * @param index index at which to insert first element from the - * specified collection. - * @param c elements to be inserted into this list. - * - * @return true if this list changed as a result of the call. - * - * @exception IndexOutOfBoundsException if the index is out of - * range (index < 0 || index > size()) - */ - - public boolean addAll(final int index, final DoubleList c) - { - if (index > _limit) - { - throw new IndexOutOfBoundsException(); - } - if (c._limit != 0) - { - if ((_limit + c._limit) > _array.length) - { - growArray(_limit + c._limit); - } - - // make a hole - System.arraycopy(_array, index, _array, index + c._limit, - _limit - index); - - // fill it in - System.arraycopy(c._array, 0, _array, index, c._limit); - _limit += c._limit; - } - return true; - } - - /** - * Removes all of the elements from this list. This list will be - * empty after this call returns (unless it throws an exception). - */ - - public void clear() - { - _limit = 0; - } - - /** - * Returns true if this list contains the specified element. More - * formally, returns true if and only if this list contains at - * least one element e such that o == e - * - * @param o element whose presence in this list is to be tested. - * - * @return true if this list contains the specified element. - */ - - public boolean contains(final double o) - { - boolean rval = false; - - for (int j = 0; !rval && (j < _limit); j++) - { - if (_array[ j ] == o) - { - rval = true; - } - } - return rval; - } - - /** - * Returns true if this list contains all of the elements of the - * specified collection. - * - * @param c collection to be checked for containment in this list. - * - * @return true if this list contains all of the elements of the - * specified collection. - */ - - public boolean containsAll(final DoubleList c) - { - boolean rval = true; - - if (this != c) - { - for (int j = 0; rval && (j < c._limit); j++) - { - if (!contains(c._array[ j ])) - { - rval = false; - } - } - } - return rval; - } - - /** - * Compares the specified object with this list for equality. - * Returns true if and only if the specified object is also a - * list, both lists have the same size, and all corresponding - * pairs of elements in the two lists are equal. (Two elements e1 - * and e2 are equal if e1 == e2.) In other words, two lists are - * defined to be equal if they contain the same elements in the - * same order. This definition ensures that the equals method - * works properly across different implementations of the List - * interface. - * - * @param o the object to be compared for equality with this list. - * - * @return true if the specified object is equal to this list. - */ - - public boolean equals(final Object o) - { - boolean rval = this == o; - - if (!rval && (o != null) && (o.getClass() == this.getClass())) - { - DoubleList other = ( DoubleList ) o; - - if (other._limit == _limit) - { - - // assume match - rval = true; - for (int j = 0; rval && (j < _limit); j++) - { - rval = _array[ j ] == other._array[ j ]; - } - } - } - return rval; - } - - /** - * Returns the element at the specified position in this list. - * - * @param index index of element to return. - * - * @return the element at the specified position in this list. - * - * @exception IndexOutOfBoundsException if the index is out of - * range (index < 0 || index >= size()). - */ - - public double get(final int index) - { - if (index >= _limit) - { - throw new IndexOutOfBoundsException(); - } - return _array[ index ]; - } - - /** - * THIS MOST LIKELY DOES NOT WORK - * Returns the hash code value for this list. The hash code of a - * list is defined to be the result of the following calculation: - * - * - * hashCode = 1; - * Iterator i = list.iterator(); - * while (i.hasNext()) { - * Object obj = i.next(); - * hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode()); - * } - * - * - * This ensures that list1.equals(list2) implies that - * list1.hashCode()==list2.hashCode() for any two lists, list1 and - * list2, as required by the general contract of Object.hashCode. - * - * @return the hash code value for this list. - */ - - public int hashCode() - { - int hash = 0; - - for (int j = 0; j < _limit; j++) - { - hash = (31 * hash) + ((int) _array[ j ]); - } - return hash; - } - - /** - * Returns the index in this list of the first occurrence of the - * specified element, or -1 if this list does not contain this - * element. More formally, returns the lowest index i such that - * (o == get(i)), or -1 if there is no such index. - * - * @param o element to search for. - * - * @return the index in this list of the first occurrence of the - * specified element, or -1 if this list does not contain - * this element. - */ - - public int indexOf(final double o) - { - int rval = 0; - - for (; rval < _limit; rval++) - { - if (o == _array[ rval ]) - { - break; - } - } - if (rval == _limit) - { - rval = -1; // didn't find it - } - return rval; - } - - /** - * Returns true if this list contains no elements. - * - * @return true if this list contains no elements. - */ - - public boolean isEmpty() - { - return _limit == 0; - } - - /** - * Returns the index in this list of the last occurrence of the - * specified element, or -1 if this list does not contain this - * element. More formally, returns the highest index i such that - * (o == get(i)), or -1 if there is no such index. - * - * @param o element to search for. - * - * @return the index in this list of the last occurrence of the - * specified element, or -1 if this list does not contain - * this element. - */ - - public int lastIndexOf(final double o) - { - int rval = _limit - 1; - - for (; rval >= 0; rval--) - { - if (o == _array[ rval ]) - { - break; - } - } - return rval; - } - - /** - * Removes the element at the specified position in this list. - * Shifts any subsequent elements to the left (subtracts one from - * their indices). Returns the element that was removed from the - * list. - * - * @param index the index of the element to removed. - * - * @return the element previously at the specified position. - * - * @exception IndexOutOfBoundsException if the index is out of - * range (index < 0 || index >= size()). - */ - - public double remove(final int index) - { - if (index >= _limit) - { - throw new IndexOutOfBoundsException(); - } - double rval = _array[ index ]; - - System.arraycopy(_array, index + 1, _array, index, _limit - index); - _limit--; - return rval; - } - - /** - * Removes the first occurrence in this list of the specified - * element (optional operation). If this list does not contain - * the element, it is unchanged. More formally, removes the - * element with the lowest index i such that (o.equals(get(i))) - * (if such an element exists). - * - * @param o element to be removed from this list, if present. - * - * @return true if this list contained the specified element. - */ - - public boolean removeValue(final double o) - { - boolean rval = false; - - for (int j = 0; !rval && (j < _limit); j++) - { - if (o == _array[ j ]) - { - System.arraycopy(_array, j + 1, _array, j, _limit - j); - _limit--; - rval = true; - } - } - return rval; - } - - /** - * Removes from this list all the elements that are contained in - * the specified collection - * - * @param c collection that defines which elements will be removed - * from this list. - * - * @return true if this list changed as a result of the call. - */ - - public boolean removeAll(final DoubleList c) - { - boolean rval = false; - - for (int j = 0; j < c._limit; j++) - { - if (removeValue(c._array[ j ])) - { - rval = true; - } - } - return rval; - } - - /** - * Retains only the elements in this list that are contained in - * the specified collection. In other words, removes from this - * list all the elements that are not contained in the specified - * collection. - * - * @param c collection that defines which elements this set will - * retain. - * - * @return true if this list changed as a result of the call. - */ - - public boolean retainAll(final DoubleList c) - { - boolean rval = false; - - for (int j = 0; j < _limit; ) - { - if (!c.contains(_array[ j ])) - { - remove(j); - rval = true; - } - else - { - j++; - } - } - return rval; - } - - /** - * Replaces the element at the specified position in this list - * with the specified element - * - * @param index index of element to replace. - * @param element element to be stored at the specified position. - * - * @return the element previously at the specified position. - * - * @exception IndexOutOfBoundsException if the index is out of - * range (index < 0 || index >= size()). - */ - - public double set(final int index, final double element) - { - if (index >= _limit) - { - throw new IndexOutOfBoundsException(); - } - double rval = _array[ index ]; - - _array[ index ] = element; - return rval; - } - - /** - * Returns the number of elements in this list. If this list - * contains more than Doubleeger.MAX_VALUE elements, returns - * Doubleeger.MAX_VALUE. - * - * @return the number of elements in this DoubleList - */ - - public int size() - { - return _limit; - } - - /** - * Returns an array containing all of the elements in this list in - * proper sequence. Obeys the general contract of the - * Collection.toArray method. - * - * @return an array containing all of the elements in this list in - * proper sequence. - */ - - public double [] toArray() - { - double[] rval = new double[ _limit ]; - - System.arraycopy(_array, 0, rval, 0, _limit); - return rval; - } - - /** - * Returns an array containing all of the elements in this list in - * proper sequence. Obeys the general contract of the - * Collection.toArray(Object[]) method. - * - * @param a the array into which the elements of this list are to - * be stored, if it is big enough; otherwise, a new array - * is allocated for this purpose. - * - * @return an array containing the elements of this list. - */ - - public double [] toArray(final double [] a) - { - double[] rval; - - if (a.length == _limit) - { - System.arraycopy(_array, 0, a, 0, _limit); - rval = a; - } - else - { - rval = toArray(); - } - return rval; - } - - private void growArray(final int new_size) - { - int size = (new_size == _array.length) ? new_size + 1 - : new_size; - double[] new_array = new double[ size ]; - - System.arraycopy(_array, 0, new_array, 0, _limit); - _array = new_array; - } -} // end public class DoubleList diff --git a/src/java/org/apache/poi/util/DoubleList2d.java b/src/java/org/apache/poi/util/DoubleList2d.java deleted file mode 100644 index 45a44fd369..0000000000 --- a/src/java/org/apache/poi/util/DoubleList2d.java +++ /dev/null @@ -1,74 +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.util; - -import java.util.List; -import java.util.ArrayList; - -/** - * Provides an interface for interacting with 2d arrays of doubles. This - * implementation will return 0 for items not yet allocated and automatically - * increase the array size for set operations. You never get an index out of - * bounds. - * - * @author Glen Stampoultzis (glens at apache.org) - * @version $Id$ - */ -public class DoubleList2d -{ - // Implemented using a List of DoubleList's. - List rows = new ArrayList(); - - public double get(int col, int row) - { - if (row >= rows.size()) - { - return 0; - } - else - { - DoubleList cols = (DoubleList) rows.get(row); - if (col >= cols.size()) - return 0; - else - return cols.get( col ); - } - } - - public void set(int col, int row, double value) - { - resizeRows(row); - resizeCols(row,col); - DoubleList cols = (DoubleList) rows.get( row ); - cols.set( col, value ); - } - - private void resizeRows( int row ) - { - while (rows.size() <= row) - rows.add( new DoubleList() ); - } - - private void resizeCols( int row, int col ) - { - DoubleList cols = (DoubleList) rows.get( row ); - while (cols.size() <= col) - cols.add(0); - } - - -} diff --git a/src/java/org/apache/poi/util/LittleEndian.java b/src/java/org/apache/poi/util/LittleEndian.java index 373710b47a..69a68c8b31 100644 --- a/src/java/org/apache/poi/util/LittleEndian.java +++ b/src/java/org/apache/poi/util/LittleEndian.java @@ -28,13 +28,12 @@ import java.io.InputStream; *@author Marc Johnson (mjohnson at apache dot org) *@author Andrew Oliver (acoliver at apache dot org) */ -public final class LittleEndian implements LittleEndianConsts { +public class LittleEndian implements LittleEndianConsts { private LittleEndian() { - // no instances of this class + // no instances of this class } - /** * get a short value from a byte array * @@ -42,9 +41,10 @@ public final class LittleEndian implements LittleEndianConsts { *@param offset a starting offset into the byte array *@return the short (16-bit) value */ - - public static short getShort(final byte[] data, final int offset) { - return (short) getNumber(data, offset, SHORT_SIZE); + public static short getShort(byte[] data, int offset) { + int b0 = data[offset] & 0xFF; + int b1 = data[offset+1] & 0xFF; + return (short) ((b1 << 8) + (b0 << 0)); } @@ -55,73 +55,32 @@ public final class LittleEndian implements LittleEndianConsts { *@param offset a starting offset into the byte array *@return the unsigned short (16-bit) value in an integer */ - public static int getUShort(final byte[] data, final int offset) { - short num = (short) getNumber(data, offset, SHORT_SIZE); - int retNum; - if (num < 0) { - retNum = (Short.MAX_VALUE + 1) * 2 + num; - } else { - retNum = num; - } - return retNum; - } - - - /** - * get a short array from a byte array. - * - *@param data Description of the Parameter - *@param offset Description of the Parameter - *@param size Description of the Parameter - *@return The simpleShortArray value - */ - public static short[] getSimpleShortArray(final byte[] data, final int offset, final int size) { - short[] results = new short[size]; - for (int i = 0; i < size; i++) { - results[i] = getShort(data, offset + 2 + (i * 2)); - } - return results; - } - - - /** - * get a short array from a byte array. The short array is assumed to start - * with a word describing the length of the array. - * - *@param data Description of the Parameter - *@param offset Description of the Parameter - *@return The shortArray value - */ - public static short[] getShortArray(final byte[] data, final int offset) { - int size = (int) getNumber(data, offset, SHORT_SIZE); - short[] results = getSimpleShortArray(data, offset, size); - return results; + public static int getUShort(byte[] data, int offset) { + int b0 = data[offset] & 0xFF; + int b1 = data[offset+1] & 0xFF; + return (b1 << 8) + (b0 << 0); } - /** * get a short value from the beginning of a byte array * *@param data the byte array *@return the short (16-bit) value */ - - public static short getShort(final byte[] data) { + public static short getShort(byte[] data) { return getShort(data, 0); } - /** * get an unsigned short value from the beginning of a byte array * *@param data the byte array *@return the unsigned short (16-bit) value in an int */ - public static int getUShort(final byte[] data) { + public static int getUShort(byte[] data) { return getUShort(data, 0); } - /** * get an int value from a byte array * @@ -129,9 +88,13 @@ public final class LittleEndian implements LittleEndianConsts { *@param offset a starting offset into the byte array *@return the int (32-bit) value */ - - public static int getInt(final byte[] data, final int offset) { - return (int) getNumber(data, offset, INT_SIZE); + public static int getInt(byte[] data, int offset) { + int i=offset; + int b0 = data[i++] & 0xFF; + int b1 = data[i++] & 0xFF; + int b2 = data[i++] & 0xFF; + int b3 = data[i++] & 0xFF; + return (b3 << 24) + (b2 << 16) + (b1 << 8) + (b0 << 0); } @@ -139,10 +102,9 @@ public final class LittleEndian implements LittleEndianConsts { * get an int value from the beginning of a byte array * *@param data the byte array - *@return the int (32-bit) value + *@return the int (32-bit) value */ - - public static int getInt(final byte[] data) { + public static int getInt(byte[] data) { return getInt(data, 0); } @@ -154,15 +116,9 @@ public final class LittleEndian implements LittleEndianConsts { *@param offset a starting offset into the byte array *@return the unsigned int (32-bit) value in a long */ - public static long getUInt(final byte[] data, final int offset) { - int num = (int) getNumber(data, offset, INT_SIZE); - long retNum; - if (num < 0) { - retNum = ((long) Integer.MAX_VALUE + 1) * 2 + num; - } else { - retNum = num; - } - return retNum; + public static long getUInt(byte[] data, int offset) { + long retNum = getInt(data, offset); + return retNum & 0x00FFFFFFFF; } /** @@ -171,8 +127,8 @@ public final class LittleEndian implements LittleEndianConsts { *@param data the byte array *@return the unsigned int (32-bit) value in a long */ - public static long getUInt(final byte[] data) { - return getUInt(data,0); + public static long getUInt(byte[] data) { + return getUInt(data,0); } /** @@ -182,24 +138,16 @@ public final class LittleEndian implements LittleEndianConsts { *@param offset a starting offset into the byte array *@return the long (64-bit) value */ - - public static long getLong(final byte[] data, final int offset) { - return getNumber(data, offset, LONG_SIZE); - } - - - /** - * get a long value from the beginning of a byte array - * - *@param data the byte array - *@return the long (64-bit) value - */ - - public static long getLong(final byte[] data) { - return getLong(data, 0); + public static long getLong(byte[] data, int offset) { + long result = 0; + + for (int j = offset + LONG_SIZE - 1; j >= offset; j--) { + result <<= 8; + result |= 0xff & data[j]; + } + return result; } - /** * get a double value from a byte array, reads it in little endian format * then converts the resulting revolting IEEE 754 (curse them) floating @@ -209,24 +157,10 @@ public final class LittleEndian implements LittleEndianConsts { *@param offset a starting offset into the byte array *@return the double (64-bit) value */ - - public static double getDouble(final byte[] data, final int offset) { - return Double.longBitsToDouble(getNumber(data, offset, DOUBLE_SIZE)); - } - - - /** - * get a double value from the beginning of a byte array - * - *@param data the byte array - *@return the double (64-bit) value - */ - - public static double getDouble(final byte[] data) { - return getDouble(data, 0); + public static double getDouble(byte[] data, int offset) { + return Double.longBitsToDouble(getLong(data, offset)); } - /** * put a short value into a byte array * @@ -234,9 +168,10 @@ public final class LittleEndian implements LittleEndianConsts { *@param offset a starting offset into the byte array *@param value the short (16-bit) value */ - public static void putShort(final byte[] data, final int offset, - final short value) { - putNumber(data, offset, value, SHORT_SIZE); + public static void putShort(byte[] data, int offset, short value) { + int i = offset; + data[i++] = (byte)((value >>> 0) & 0xFF); + data[i++] = (byte)((value >>> 8) & 0xFF); } /** @@ -247,7 +182,7 @@ public final class LittleEndian implements LittleEndianConsts { * Added for consistency with other put~() methods */ public static void putByte(byte[] data, int offset, int value) { - putNumber(data, offset, value, LittleEndianConsts.BYTE_SIZE); + data[offset] = (byte) value; } /** @@ -259,10 +194,10 @@ public final class LittleEndian implements LittleEndianConsts { * * @exception ArrayIndexOutOfBoundsException may be thrown */ - public static void putUShort(final byte[] data, final int offset, - final int value) - { - putNumber(data, offset, value, SHORT_SIZE); + public static void putUShort(byte[] data, int offset, int value) { + int i = offset; + data[i++] = (byte)((value >>> 0) & 0xFF); + data[i++] = (byte)((value >>> 8) & 0xFF); } /** @@ -271,8 +206,7 @@ public final class LittleEndian implements LittleEndianConsts { *@param data the byte array *@param value the short (16-bit) value */ - - public static void putShort(final byte[] data, final short value) { + public static void putShort(byte[] data, short value) { putShort(data, 0, value); } @@ -284,10 +218,12 @@ public final class LittleEndian implements LittleEndianConsts { *@param offset a starting offset into the byte array *@param value the int (32-bit) value */ - - public static void putInt(final byte[] data, final int offset, - final int value) { - putNumber(data, offset, value, INT_SIZE); + public static void putInt(byte[] data, int offset, int value) { + int i = offset; + data[i++] = (byte)((value >>> 0) & 0xFF); + data[i++] = (byte)((value >>> 8) & 0xFF); + data[i++] = (byte)((value >>> 16) & 0xFF); + data[i++] = (byte)((value >>> 24) & 0xFF); } @@ -297,8 +233,7 @@ public final class LittleEndian implements LittleEndianConsts { *@param data the byte array *@param value the int (32-bit) value */ - - public static void putInt(final byte[] data, final int value) { + public static void putInt(byte[] data, int value) { putInt(data, 0, value); } @@ -310,22 +245,14 @@ public final class LittleEndian implements LittleEndianConsts { *@param offset a starting offset into the byte array *@param value the long (64-bit) value */ - - public static void putLong(final byte[] data, final int offset, - final long value) { - putNumber(data, offset, value, LONG_SIZE); - } - - - /** - * put a long value into beginning of a byte array - * - *@param data the byte array - *@param value the long (64-bit) value - */ - - public static void putLong(final byte[] data, final long value) { - putLong(data, 0, value); + public static void putLong(byte[] data, int offset, long value) { + int limit = LONG_SIZE + offset; + long v = value; + + for (int j = offset; j < limit; j++) { + data[j] = (byte) (v & 0xFF); + v >>= 8; + } } @@ -336,26 +263,13 @@ public final class LittleEndian implements LittleEndianConsts { *@param offset a starting offset into the byte array *@param value the double (64-bit) value */ - - public static void putDouble(final byte[] data, final int offset, - final double value) { + public static void putDouble(byte[] data, int offset, double value) { // Excel likes NaN to be a specific value. - if (Double.isNaN(value)) - putNumber(data, offset, -276939487313920L, DOUBLE_SIZE); - else - putNumber(data, offset, Double.doubleToLongBits(value), DOUBLE_SIZE); - } - - - /** - * put a double value into beginning of a byte array - * - *@param data the byte array - *@param value the double (64-bit) value - */ - - public static void putDouble(final byte[] data, final double value) { - putDouble(data, 0, value); + if (Double.isNaN(value)) { + putLong(data, offset, -276939487313920L); + } else { + putLong(data, offset, Double.doubleToLongBits(value)); + } } @@ -364,7 +278,6 @@ public final class LittleEndian implements LittleEndianConsts { * *@author Marc Johnson (mjohnson at apache dot org) */ - public static final class BufferUnderrunException extends IOException { BufferUnderrunException() { @@ -376,79 +289,72 @@ public final class LittleEndian implements LittleEndianConsts { /** * get a short value from an InputStream * - *@param stream the InputStream from which the short - * is to be read + *@param stream the InputStream from which the short is to be read *@return the short (16-bit) value *@exception IOException will be propagated back to the caller - *@exception BufferUnderrunException if the stream cannot provide enough - * bytes + *@exception BufferUnderrunException if the stream cannot provide enough bytes */ public static short readShort(InputStream stream) throws IOException, BufferUnderrunException { - return (short) readUShort(stream); - } + return (short) readUShort(stream); + } - public static int readUShort(InputStream stream) throws IOException, BufferUnderrunException { + public static int readUShort(InputStream stream) throws IOException, BufferUnderrunException { - int ch1 = stream.read(); - int ch2 = stream.read(); - if ((ch1 | ch2) < 0) { - throw new BufferUnderrunException(); - } - return ((ch2 << 8) + (ch1 << 0)); - } + int ch1 = stream.read(); + int ch2 = stream.read(); + if ((ch1 | ch2) < 0) { + throw new BufferUnderrunException(); + } + return (ch2 << 8) + (ch1 << 0); + } /** * get an int value from an InputStream * - *@param stream the InputStream from which the int is - * to be read - *@return the int (32-bit) value - *@exception IOException will be propagated back to the caller - *@exception BufferUnderrunException if the stream cannot provide enough - * bytes + *@param stream the InputStream from which the int is to be read + * @return the int (32-bit) value + * @exception IOException will be propagated back to the caller + * @exception BufferUnderrunException if the stream cannot provide enough bytes */ - public static int readInt(final InputStream stream) + public static int readInt(InputStream stream) throws IOException, BufferUnderrunException { - int ch1 = stream.read(); - int ch2 = stream.read(); - int ch3 = stream.read(); - int ch4 = stream.read(); - if ((ch1 | ch2 | ch3 | ch4) < 0) { - throw new BufferUnderrunException(); - } - return ((ch4 << 24) + (ch3<<16) + (ch2 << 8) + (ch1 << 0)); + int ch1 = stream.read(); + int ch2 = stream.read(); + int ch3 = stream.read(); + int ch4 = stream.read(); + if ((ch1 | ch2 | ch3 | ch4) < 0) { + throw new BufferUnderrunException(); + } + return (ch4 << 24) + (ch3<<16) + (ch2 << 8) + (ch1 << 0); } /** * get a long value from an InputStream * - *@param stream the InputStream from which the long - * is to be read - *@return the long (64-bit) value - *@exception IOException will be propagated back to the caller - *@exception BufferUnderrunException if the stream cannot provide enough - * bytes + * @param stream the InputStream from which the long is to be read + * @return the long (64-bit) value + * @exception IOException will be propagated back to the caller + * @exception BufferUnderrunException if the stream cannot provide enough bytes */ - - public static long readLong(final InputStream stream) + public static long readLong(InputStream stream) throws IOException, BufferUnderrunException { - int ch1 = stream.read(); - int ch2 = stream.read(); - int ch3 = stream.read(); - int ch4 = stream.read(); - int ch5 = stream.read(); - int ch6 = stream.read(); - int ch7 = stream.read(); - int ch8 = stream.read(); - if ((ch1 | ch2 | ch3 | ch4 | ch5 | ch6 | ch7 | ch8) < 0) { - throw new BufferUnderrunException(); - } - - return - ((long)ch8 << 56) + + int ch1 = stream.read(); + int ch2 = stream.read(); + int ch3 = stream.read(); + int ch4 = stream.read(); + int ch5 = stream.read(); + int ch6 = stream.read(); + int ch7 = stream.read(); + int ch8 = stream.read(); + if ((ch1 | ch2 | ch3 | ch4 | ch5 | ch6 | ch7 | ch8) < 0) { + throw new BufferUnderrunException(); + } + + return + ((long)ch8 << 56) + ((long)ch7 << 48) + ((long)ch6 << 40) + ((long)ch5 << 32) + @@ -458,114 +364,44 @@ public final class LittleEndian implements LittleEndianConsts { (ch1 << 0); } - /** - * Gets the number attribute of the LittleEndian class - * - *@param data Description of the Parameter - *@param offset Description of the Parameter - *@param size Description of the Parameter - *@return The number value - */ - private static long getNumber(final byte[] data, final int offset, - final int size) { - long result = 0; - - for (int j = offset + size - 1; j >= offset; j--) { - result <<= 8; - result |= 0xff & data[j]; - } - return result; - } - - - /** - * Description of the Method - * - *@param data Description of the Parameter - *@param offset Description of the Parameter - *@param value Description of the Parameter - *@param size Description of the Parameter - */ - private static void putNumber(final byte[] data, final int offset, - final long value, final int size) { - int limit = size + offset; - long v = value; - - for (int j = offset; j < limit; j++) { - data[j] = (byte) (v & 0xFF); - v >>= 8; - } - } - - /** * Convert an 'unsigned' byte to an integer. ie, don't carry across the * sign. * - *@param b Description of the Parameter - *@return Description of the Return Value + * @param b Description of the Parameter + * @return Description of the Return Value */ public static int ubyteToInt(byte b) { - return ((b & 0x80) == 0 ? (int) b : (b & (byte) 0x7f) + 0x80); + return b & 0xFF; } /** * get the unsigned value of a byte. * - *@param data the byte array. - *@param offset a starting offset into the byte array. - *@return the unsigned value of the byte as a 32 bit integer + * @param data the byte array. + * @param offset a starting offset into the byte array. + * @return the unsigned value of the byte as a 32 bit integer */ - public static int getUnsignedByte(final byte[] data, final int offset) { - return (int) getNumber(data, offset, BYTE_SIZE); - } - - - /** - * get the unsigned value of a byte. - * - *@param data the byte array - *@return the unsigned value of the byte as a 32 bit integer - */ - public static int getUnsignedByte(final byte[] data) { - return getUnsignedByte(data, 0); + public static int getUnsignedByte(byte[] data, int offset) { + return data[offset] & 0xFF; } /** * Copy a portion of a byte array * - *@param data the original byte array - *@param offset Where to start copying from. - *@param size Number of bytes to copy. - *@return The byteArray value - *@throws IndexOutOfBoundsException - if copying would cause access of + * @param data the original byte array + * @param offset Where to start copying from. + * @param size Number of bytes to copy. + * @return The byteArray value + * @throws IndexOutOfBoundsException - if copying would cause access of * data outside array bounds. */ - public static byte[] getByteArray(final byte[] data, int offset, int size) { + public static byte[] getByteArray(byte[] data, int offset, int size) { byte[] copy = new byte[size]; System.arraycopy(data, offset, copy, 0, size); return copy; } - - /** - *

Gets an unsigned int value (8 bytes) from a byte array.

- * - * @param data the byte array - * @param offset a starting offset into the byte array - * @return the unsigned int (32-bit) value in a long - */ - public static long getULong(final byte[] data, final int offset) - { - int num = (int) getNumber(data, offset, LONG_SIZE); - long retNum; - if (num < 0) - retNum = ((long) Integer.MAX_VALUE + 1) * 2 + num; - else - retNum = num; - return retNum; - } - } diff --git a/src/java/org/apache/poi/util/LittleEndianByteArrayInputStream.java b/src/java/org/apache/poi/util/LittleEndianByteArrayInputStream.java new file mode 100644 index 0000000000..984db5f8f3 --- /dev/null +++ b/src/java/org/apache/poi/util/LittleEndianByteArrayInputStream.java @@ -0,0 +1,120 @@ +/* ==================================================================== + 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.util; + +/** + * Adapts a plain byte array to {@link LittleEndianInput} + * + * + * @author Josh Micich + */ +public final class LittleEndianByteArrayInputStream implements LittleEndianInput { + private final byte[] _buf; + private final int _endIndex; + private int _readIndex; + + public LittleEndianByteArrayInputStream(byte[] buf, int startOffset, int maxReadLen) { + _buf = buf; + _readIndex = startOffset; + _endIndex = startOffset + maxReadLen; + } + public LittleEndianByteArrayInputStream(byte[] buf, int startOffset) { + this(buf, startOffset, buf.length - startOffset); + } + public LittleEndianByteArrayInputStream(byte[] buf) { + this(buf, 0, buf.length); + } + + public int available() { + return _endIndex - _readIndex; + } + private void checkPosition(int i) { + if (i > _endIndex - _readIndex) { + throw new RuntimeException("Buffer overrun"); + } + } + + public int getReadIndex() { + return _readIndex; + } + public byte readByte() { + checkPosition(1); + return _buf[_readIndex++]; + } + + public int readInt() { + checkPosition(4); + int i = _readIndex; + + int b0 = _buf[i++] & 0xFF; + int b1 = _buf[i++] & 0xFF; + int b2 = _buf[i++] & 0xFF; + int b3 = _buf[i++] & 0xFF; + _readIndex = i; + return (b3 << 24) + (b2 << 16) + (b1 << 8) + (b0 << 0); + } + public long readLong() { + checkPosition(8); + int i = _readIndex; + + int b0 = _buf[i++] & 0xFF; + int b1 = _buf[i++] & 0xFF; + int b2 = _buf[i++] & 0xFF; + int b3 = _buf[i++] & 0xFF; + int b4 = _buf[i++] & 0xFF; + int b5 = _buf[i++] & 0xFF; + int b6 = _buf[i++] & 0xFF; + int b7 = _buf[i++] & 0xFF; + _readIndex = i; + return (((long)b7 << 56) + + ((long)b6 << 48) + + ((long)b5 << 40) + + ((long)b4 << 32) + + ((long)b3 << 24) + + (b2 << 16) + + (b1 << 8) + + (b0 << 0)); + } + public short readShort() { + return (short)readUShort(); + } + public int readUByte() { + checkPosition(1); + return _buf[_readIndex++] & 0xFF; + } + public int readUShort() { + checkPosition(2); + int i = _readIndex; + + int b0 = _buf[i++] & 0xFF; + int b1 = _buf[i++] & 0xFF; + _readIndex = i; + return (b1 << 8) + (b0 << 0); + } + public void readFully(byte[] buf, int off, int len) { + checkPosition(len); + System.arraycopy(_buf, _readIndex, _buf, off, len); + _readIndex+=len; + } + public void readFully(byte[] buf) { + readFully(buf, 0, buf.length); + } + public double readDouble() { + return Double.longBitsToDouble(readLong()); + } +} diff --git a/src/java/org/apache/poi/util/LittleEndianByteArrayOutputStream.java b/src/java/org/apache/poi/util/LittleEndianByteArrayOutputStream.java new file mode 100644 index 0000000000..b77407c7a9 --- /dev/null +++ b/src/java/org/apache/poi/util/LittleEndianByteArrayOutputStream.java @@ -0,0 +1,87 @@ +/* ==================================================================== + 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.util; + + +/** + * Adapts a plain byte array to {@link LittleEndianOutput} + * + * + * @author Josh Micich + */ +public final class LittleEndianByteArrayOutputStream implements LittleEndianOutput { + private final byte[] _buf; + private final int _endIndex; + private int _writeIndex; + + public LittleEndianByteArrayOutputStream(byte[] buf, int startOffset, int maxWriteLen) { + _buf = buf; + _writeIndex = startOffset; + _endIndex = startOffset + maxWriteLen; + } + public LittleEndianByteArrayOutputStream(byte[] buf, int startOffset) { + this(buf, startOffset, buf.length - startOffset); + } + + private void checkPosition(int i) { + if (i > _endIndex - _writeIndex) { + throw new RuntimeException("Buffer overrun"); + } + } + + public void writeByte(int v) { + checkPosition(1); + _buf[_writeIndex++] = (byte)v; + } + + public void writeDouble(double v) { + writeLong(Double.doubleToLongBits(v)); + } + + public void writeInt(int v) { + checkPosition(4); + int i = _writeIndex; + _buf[i++] = (byte)((v >>> 0) & 0xFF); + _buf[i++] = (byte)((v >>> 8) & 0xFF); + _buf[i++] = (byte)((v >>> 16) & 0xFF); + _buf[i++] = (byte)((v >>> 24) & 0xFF); + _writeIndex = i; + } + + public void writeLong(long v) { + writeInt((int)(v >> 0)); + writeInt((int)(v >> 32)); + } + + public void writeShort(int v) { + checkPosition(2); + int i = _writeIndex; + _buf[i++] = (byte)((v >>> 0) & 0xFF); + _buf[i++] = (byte)((v >>> 8) & 0xFF); + _writeIndex = i; + } + public void write(byte[] b) { + int len = b.length; + checkPosition(len); + System.arraycopy(b, 0, _buf, _writeIndex, len); + _writeIndex += len; + } + public int getWriteIndex() { + return _writeIndex; + } +} diff --git a/src/java/org/apache/poi/util/LittleEndianInput.java b/src/java/org/apache/poi/util/LittleEndianInput.java index 433999dfc3..dafa7eadac 100644 --- a/src/java/org/apache/poi/util/LittleEndianInput.java +++ b/src/java/org/apache/poi/util/LittleEndianInput.java @@ -21,6 +21,7 @@ package org.apache.poi.util; * @author Josh Micich */ public interface LittleEndianInput { + int available(); byte readByte(); int readUByte(); short readShort(); @@ -30,6 +31,4 @@ public interface LittleEndianInput { double readDouble(); void readFully(byte[] buf); void readFully(byte[] buf, int off, int len); - String readUnicodeLEString(int nChars); - String readCompressedUnicode(int nChars); } diff --git a/src/java/org/apache/poi/util/LittleEndianInputStream.java b/src/java/org/apache/poi/util/LittleEndianInputStream.java index 035230491b..622ee23cd4 100644 --- a/src/java/org/apache/poi/util/LittleEndianInputStream.java +++ b/src/java/org/apache/poi/util/LittleEndianInputStream.java @@ -22,6 +22,10 @@ import java.io.IOException; import java.io.InputStream; /** + * Wraps an {@link InputStream} providing {@link LittleEndianInput}

+ * + * This class does not buffer any input, so the stream read position maintained + * by this class is consistent with that of the inner stream. * * @author Josh Micich */ @@ -29,7 +33,13 @@ public class LittleEndianInputStream extends FilterInputStream implements Little public LittleEndianInputStream(InputStream is) { super(is); } - + public int available() { + try { + return super.available(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } public byte readByte() { return (byte)readUByte(); } @@ -131,34 +141,4 @@ public class LittleEndianInputStream extends FilterInputStream implements Little buf[i] = (byte) ch; } } - public String readCompressedUnicode(int nChars) { - char[] buf = new char[nChars]; - for (int i = 0; i < buf.length; i++) { - int ch; - try { - ch = in.read(); - } catch (IOException e) { - throw new RuntimeException(e); - } - checkEOF(ch); - buf[i] = (char) ch; - - } - return new String(buf); - } - public String readUnicodeLEString(int nChars) { - char[] buf = new char[nChars]; - for (int i = 0; i < buf.length; i++) { - int ch; - try { - ch = in.read(); - } catch (IOException e) { - throw new RuntimeException(e); - } - checkEOF(ch); - buf[i] = (char) ch; - - } - return new String(buf); - } } diff --git a/src/java/org/apache/poi/util/StringUtil.java b/src/java/org/apache/poi/util/StringUtil.java index bb98de3d05..8a57d2340b 100644 --- a/src/java/org/apache/poi/util/StringUtil.java +++ b/src/java/org/apache/poi/util/StringUtil.java @@ -20,9 +20,14 @@ package org.apache.poi.util; import java.io.UnsupportedEncodingException; import java.text.FieldPosition; import java.text.NumberFormat; + +import org.apache.poi.hssf.record.RecordInputStream; /** - * Title: String Utility Description: Collection of string handling utilities - * + * Title: String Utility Description: Collection of string handling utilities

+ * + * Note - none of the methods in this class deals with {@link ContinueRecord}s. For such + * functionality, consider using {@link RecordInputStream +} * * *@author Andrew C. Oliver *@author Sergei Kozello (sergeikozello at mail.ru) @@ -84,64 +89,11 @@ public class StringUtil { * @param string the byte array to be converted * @return the converted string */ - public static String getFromUnicodeLE(final byte[] string) { + public static String getFromUnicodeLE(byte[] string) { if(string.length == 0) { return ""; } return getFromUnicodeLE(string, 0, string.length / 2); } - /** - * Given a byte array of 16-bit unicode characters in big endian - * format (most important byte first), return a Java String representation - * of it. - * - * { 0x00, 0x16 } -0x16 - * - * @param string the byte array to be converted - * @param offset the initial offset into the - * byte array. it is assumed that string[ offset ] and string[ offset + - * 1 ] contain the first 16-bit unicode character - * @param len the length of the final string - * @return the converted string - * @exception ArrayIndexOutOfBoundsException if offset is out of bounds for - * the byte array (i.e., is negative or is greater than or equal to - * string.length) - * @exception IllegalArgumentException if len is too large (i.e., - * there is not enough data in string to create a String of that - * length) - */ - public static String getFromUnicodeBE( - final byte[] string, - final int offset, - final int len) - throws ArrayIndexOutOfBoundsException, IllegalArgumentException { - if ((offset < 0) || (offset >= string.length)) { - throw new ArrayIndexOutOfBoundsException("Illegal offset"); - } - if ((len < 0) || (((string.length - offset) / 2) < len)) { - throw new IllegalArgumentException("Illegal length"); - } - try { - return new String(string, offset, len * 2, "UTF-16BE"); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - } - - /** - * Given a byte array of 16-bit unicode characters in big endian - * format (most important byte first), return a Java String representation - * of it. - * - * { 0x00, 0x16 } -0x16 - * - * @param string the byte array to be converted - * @return the converted string - */ - public static String getFromUnicodeBE(final byte[] string) { - if(string.length == 0) { return ""; } - return getFromUnicodeBE(string, 0, string.length / 2); - } - /** * Read 8 bit data (in ISO-8859-1 codepage) into a (unicode) Java * String and return. @@ -163,6 +115,52 @@ public class StringUtil { throw new RuntimeException(e); } } + public static String readCompressedUnicode(LittleEndianInput in, int nChars) { + char[] buf = new char[nChars]; + for (int i = 0; i < buf.length; i++) { + buf[i] = (char) in.readUByte(); + } + return new String(buf); + } + /** + * InputStream in is expected to contain: + *

    + *
  1. ushort nChars
  2. + *
  3. byte is16BitFlag
  4. + *
  5. byte[]/char[] characterData
  6. + *
+ * For this encoding, the is16BitFlag is always present even if nChars==0. + */ + public static String readUnicodeString(LittleEndianInput in) { + + int nChars = in.readUShort(); + byte flag = in.readByte(); + if ((flag & 0x01) == 0) { + return readCompressedUnicode(in, nChars); + } + return readUnicodeLE(in, nChars); + } + /** + * OutputStream out will get: + *
    + *
  1. ushort nChars
  2. + *
  3. byte is16BitFlag
  4. + *
  5. byte[]/char[] characterData
  6. + *
+ * For this encoding, the is16BitFlag is always present even if nChars==0. + */ + public static void writeUnicodeString(LittleEndianOutput out, String value) { + + int nChars = value.length(); + out.writeShort(nChars); + boolean is16Bit = hasMultibyte(value); + out.writeByte(is16Bit ? 0x01 : 0x00); + if (is16Bit) { + putUnicodeLE(value, out); + } else { + putCompressedUnicode(value, out); + } + } /** * Takes a unicode (java) string, and returns it as 8 bit data (in ISO-8859-1 @@ -220,6 +218,14 @@ public class StringUtil { } out.write(bytes); } + + public static String readUnicodeLE(LittleEndianInput in, int nChars) { + char[] buf = new char[nChars]; + for (int i = 0; i < buf.length; i++) { + buf[i] = (char) in.readUShort(); + } + return new String(buf); + } /** * Apply printf() like formatting to a string. diff --git a/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java b/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java index 5fb6f4aa03..edf62a9ad1 100755 --- a/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java +++ b/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java @@ -100,6 +100,7 @@ public final class AllRecordTests { result.addTestSuite(TestSheetPropertiesRecord.class); result.addTestSuite(TestSharedFormulaRecord.class); result.addTestSuite(TestStringRecord.class); + result.addTestSuite(TestStyleRecord.class); result.addTestSuite(TestSubRecord.class); result.addTestSuite(TestSupBookRecord.class); result.addTestSuite(TestTableRecord.class); diff --git a/src/testcases/org/apache/poi/hssf/record/TestCommonObjectDataSubRecord.java b/src/testcases/org/apache/poi/hssf/record/TestCommonObjectDataSubRecord.java index 53a49fda0d..8e6e7ce561 100644 --- a/src/testcases/org/apache/poi/hssf/record/TestCommonObjectDataSubRecord.java +++ b/src/testcases/org/apache/poi/hssf/record/TestCommonObjectDataSubRecord.java @@ -37,7 +37,7 @@ public final class TestCommonObjectDataSubRecord extends TestCase { }; public void testLoad() { - CommonObjectDataSubRecord record = new CommonObjectDataSubRecord(TestcaseRecordInputStream.createWithFakeSid(data), data.length); + CommonObjectDataSubRecord record = new CommonObjectDataSubRecord(TestcaseRecordInputStream.createLittleEndian(data), data.length); assertEquals( CommonObjectDataSubRecord.OBJECT_TYPE_LIST_BOX, record.getObjectType()); assertEquals((short) 1, record.getObjectId()); diff --git a/src/testcases/org/apache/poi/hssf/record/TestEmbeddedObjectRefSubRecord.java b/src/testcases/org/apache/poi/hssf/record/TestEmbeddedObjectRefSubRecord.java index deaf779bbb..02875117ec 100644 --- a/src/testcases/org/apache/poi/hssf/record/TestEmbeddedObjectRefSubRecord.java +++ b/src/testcases/org/apache/poi/hssf/record/TestEmbeddedObjectRefSubRecord.java @@ -14,11 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ + package org.apache.poi.hssf.record; -import java.io.ByteArrayInputStream; import java.util.Arrays; +import junit.framework.AssertionFailedError; import junit.framework.TestCase; import org.apache.poi.util.HexRead; @@ -37,33 +38,43 @@ public final class TestEmbeddedObjectRefSubRecord extends TestCase { public void testStore() { byte[] src = hr(data1); - src = TestcaseRecordInputStream.mergeDataAndSid(EmbeddedObjectRefSubRecord.sid, (short)src.length, src); +// src = TestcaseRecordInputStream.mergeDataAndSid(EmbeddedObjectRefSubRecord.sid, (short)src.length, src); - RecordInputStream in = new RecordInputStream(new ByteArrayInputStream(src)); - in.nextRecord(); + RecordInputStream in = TestcaseRecordInputStream.create(EmbeddedObjectRefSubRecord.sid, src); - EmbeddedObjectRefSubRecord record1 = new EmbeddedObjectRefSubRecord(in, src.length-4); + EmbeddedObjectRefSubRecord record1 = new EmbeddedObjectRefSubRecord(in, src.length); byte[] ser = record1.serialize(); - RecordInputStream in2 = new RecordInputStream(new ByteArrayInputStream(ser)); - in2.nextRecord(); + RecordInputStream in2 = TestcaseRecordInputStream.create(ser); EmbeddedObjectRefSubRecord record2 = new EmbeddedObjectRefSubRecord(in2, ser.length-4); - assertTrue(Arrays.equals(src, ser)); + confirmData(src, ser); assertEquals(record1.getOLEClassName(), record2.getOLEClassName()); byte[] ser2 = record1.serialize(); assertTrue(Arrays.equals(ser, ser2)); } + /** + * @param expectedData does not include sid & size + * @param actualFullRecordData includes sid & size + */ + private static void confirmData(byte[] expectedData, byte[] actualFullRecordData) { + assertEquals(expectedData.length, actualFullRecordData.length-4); + for (int i = 0; i < expectedData.length; i++) { + if(expectedData[i] != actualFullRecordData[i+4]) { + throw new AssertionFailedError("Difference at offset (" + i + ")"); + } + } + } + public void testCreate() { EmbeddedObjectRefSubRecord record1 = new EmbeddedObjectRefSubRecord(); byte[] ser = record1.serialize(); - RecordInputStream in2 = new RecordInputStream(new ByteArrayInputStream(ser)); - in2.nextRecord(); + RecordInputStream in2 = TestcaseRecordInputStream.create(ser); EmbeddedObjectRefSubRecord record2 = new EmbeddedObjectRefSubRecord(in2, ser.length-4); assertEquals(record1.getOLEClassName(), record2.getOLEClassName()); @@ -78,21 +89,17 @@ public final class TestEmbeddedObjectRefSubRecord extends TestCase { * taken from ftPictFmla sub-record in attachment 22645 (offset 0x40AB). */ private static final byte[] data45912 = hr( - "09 00 14 00 " + "12 00 0B 00 F8 02 88 04 3B 00 " + "00 00 00 01 00 00 00 01 " + "00 00"); public void testCameraTool_bug45912() { byte[] data = data45912; - RecordInputStream in = new RecordInputStream(new ByteArrayInputStream(data)); - in.nextRecord(); + RecordInputStream in = TestcaseRecordInputStream.create(EmbeddedObjectRefSubRecord.sid, data); - EmbeddedObjectRefSubRecord rec = new EmbeddedObjectRefSubRecord(in, data.length-4); + EmbeddedObjectRefSubRecord rec = new EmbeddedObjectRefSubRecord(in, data.length); byte[] ser2 = rec.serialize(); - assertTrue(Arrays.equals(data, ser2)); - - + confirmData(data, ser2); } private static byte[] hr(String string) { diff --git a/src/testcases/org/apache/poi/hssf/record/TestFormulaRecord.java b/src/testcases/org/apache/poi/hssf/record/TestFormulaRecord.java index 703664443c..cf8fe0bd2f 100644 --- a/src/testcases/org/apache/poi/hssf/record/TestFormulaRecord.java +++ b/src/testcases/org/apache/poi/hssf/record/TestFormulaRecord.java @@ -119,7 +119,6 @@ public final class TestFormulaRecord extends TestCase { public void testWithConcat() { // =CHOOSE(2,A2,A3,A4) byte[] data = { - 6, 0, 68, 0, 1, 0, 1, 0, 15, 0, 0, 0, 0, 0, 0, 0, 57, 64, 0, 0, 12, 0, 12, -4, 46, 0, 30, 2, 0, // Int - 2 @@ -134,8 +133,7 @@ public final class TestFormulaRecord extends TestCase { 25, 8, 3, 0, // Attr 66, 4, 100, 0 // CHOOSE }; - RecordInputStream inp = new RecordInputStream( new ByteArrayInputStream(data)); - inp.nextRecord(); + RecordInputStream inp = TestcaseRecordInputStream.create(FormatRecord.sid, data); FormulaRecord fr = new FormulaRecord(inp); diff --git a/src/testcases/org/apache/poi/hssf/record/TestHyperlinkRecord.java b/src/testcases/org/apache/poi/hssf/record/TestHyperlinkRecord.java index 1919a4b038..ce0e6e2030 100644 --- a/src/testcases/org/apache/poi/hssf/record/TestHyperlinkRecord.java +++ b/src/testcases/org/apache/poi/hssf/record/TestHyperlinkRecord.java @@ -239,8 +239,7 @@ public final class TestHyperlinkRecord extends TestCase { RecordInputStream is = TestcaseRecordInputStream.create(HyperlinkRecord.sid, data); HyperlinkRecord link = new HyperlinkRecord(is); byte[] bytes1 = link.serialize(); - is = new RecordInputStream(new ByteArrayInputStream(bytes1)); - is.nextRecord(); + is = TestcaseRecordInputStream.create(bytes1); link = new HyperlinkRecord(is); byte[] bytes2 = link.serialize(); assertEquals(bytes1.length, bytes2.length); diff --git a/src/testcases/org/apache/poi/hssf/record/TestSharedFormulaRecord.java b/src/testcases/org/apache/poi/hssf/record/TestSharedFormulaRecord.java index 8a52211a6a..7dea16b308 100644 --- a/src/testcases/org/apache/poi/hssf/record/TestSharedFormulaRecord.java +++ b/src/testcases/org/apache/poi/hssf/record/TestSharedFormulaRecord.java @@ -23,6 +23,7 @@ import junit.framework.TestCase; import org.apache.poi.hssf.record.formula.Ptg; import org.apache.poi.hssf.record.formula.RefPtg; +import org.apache.poi.util.LittleEndianInput; /** * @author Josh Micich @@ -59,7 +60,7 @@ public final class TestSharedFormulaRecord extends TestCase { */ public void testConvertSharedFormulasOperandClasses_bug45123() { - RecordInputStream in = TestcaseRecordInputStream.createWithFakeSid(SHARED_FORMULA_WITH_REF_ARRAYS_DATA); + LittleEndianInput in = TestcaseRecordInputStream.createLittleEndian(SHARED_FORMULA_WITH_REF_ARRAYS_DATA); int encodedLen = in.readUShort(); Ptg[] sharedFormula = Ptg.readTokens(encodedLen, in); diff --git a/src/testcases/org/apache/poi/hssf/record/TestStyleRecord.java b/src/testcases/org/apache/poi/hssf/record/TestStyleRecord.java new file mode 100644 index 0000000000..bfe11dd3a8 --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/record/TestStyleRecord.java @@ -0,0 +1,33 @@ +/* ==================================================================== + 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.hssf.record; + +import junit.framework.TestCase; +/** + * + */ +public final class TestStyleRecord extends TestCase { + public void testUnicodeReadName() { + byte[] data = { + 17, 0, 9, 0, 1, 56, 94, -60, -119, 95, 0, 83, 0, 104, 0, 101, 0, 101, 0, 116, 0, 49, 0, 92, 40, //92, 36 + }; + RecordInputStream in = TestcaseRecordInputStream.create(StyleRecord.sid, data); + StyleRecord sr = new StyleRecord(in); + assertEquals("\u5E38\u89C4_Sheet1", sr.getName()); // "_Sheet1" + } +} diff --git a/src/testcases/org/apache/poi/hssf/record/TestTextObjectBaseRecord.java b/src/testcases/org/apache/poi/hssf/record/TestTextObjectBaseRecord.java index b8f9e86468..9b53cdd339 100644 --- a/src/testcases/org/apache/poi/hssf/record/TestTextObjectBaseRecord.java +++ b/src/testcases/org/apache/poi/hssf/record/TestTextObjectBaseRecord.java @@ -55,11 +55,9 @@ public final class TestTextObjectBaseRecord extends TestCase { public void testLoad() { - RecordInputStream in = new RecordInputStream(new ByteArrayInputStream(data)); - in.nextRecord(); + RecordInputStream in = TestcaseRecordInputStream.create(data); TextObjectRecord record = new TextObjectRecord(in); - assertEquals(TextObjectRecord.HORIZONTAL_TEXT_ALIGNMENT_CENTERED, record.getHorizontalTextAlignment()); assertEquals(TextObjectRecord.VERTICAL_TEXT_ALIGNMENT_JUSTIFY, record.getVerticalTextAlignment()); assertEquals(true, record.isTextLocked()); diff --git a/src/testcases/org/apache/poi/hssf/record/TestTextObjectRecord.java b/src/testcases/org/apache/poi/hssf/record/TestTextObjectRecord.java index b7d45e28b0..19ec07c810 100644 --- a/src/testcases/org/apache/poi/hssf/record/TestTextObjectRecord.java +++ b/src/testcases/org/apache/poi/hssf/record/TestTextObjectRecord.java @@ -52,8 +52,7 @@ public final class TestTextObjectRecord extends TestCase { public void testRead() { - RecordInputStream is = new RecordInputStream(new ByteArrayInputStream(simpleData)); - is.nextRecord(); + RecordInputStream is =TestcaseRecordInputStream.create(simpleData); TextObjectRecord record = new TextObjectRecord(is); assertEquals(TextObjectRecord.sid, record.getSid()); @@ -79,8 +78,7 @@ public final class TestTextObjectRecord extends TestCase { assertTrue(Arrays.equals(simpleData, ser)); //read again - RecordInputStream is = new RecordInputStream(new ByteArrayInputStream(simpleData)); - is.nextRecord(); + RecordInputStream is = TestcaseRecordInputStream.create(simpleData); record = new TextObjectRecord(is); } @@ -101,8 +99,7 @@ public final class TestTextObjectRecord extends TestCase { assertEquals(22, ser.length); // just the TXO record //read again - RecordInputStream is = new RecordInputStream(new ByteArrayInputStream(ser)); - is.nextRecord(); + RecordInputStream is = TestcaseRecordInputStream.create(ser); record = new TextObjectRecord(is); assertEquals(0, record.getStr().length()); } diff --git a/src/testcases/org/apache/poi/hssf/record/TestcaseRecordInputStream.java b/src/testcases/org/apache/poi/hssf/record/TestcaseRecordInputStream.java index e16eb2022e..1bbc9d7a94 100755 --- a/src/testcases/org/apache/poi/hssf/record/TestcaseRecordInputStream.java +++ b/src/testcases/org/apache/poi/hssf/record/TestcaseRecordInputStream.java @@ -23,6 +23,8 @@ import java.io.InputStream; import junit.framework.Assert; import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.LittleEndianByteArrayInputStream; +import org.apache.poi.util.LittleEndianInput; /** * A Record Input Stream derivative that makes access to byte arrays used in the @@ -40,8 +42,8 @@ public final class TestcaseRecordInputStream { /** * Prepends a mock record identifier to the supplied data and opens a record input stream */ - public static RecordInputStream createWithFakeSid(byte[] data) { - return create(-5555, data); + public static LittleEndianInput createLittleEndian(byte[] data) { + return new LittleEndianByteArrayInputStream(data); } public static RecordInputStream create(int sid, byte[] data) { diff --git a/src/testcases/org/apache/poi/hssf/record/constant/TestConstantValueParser.java b/src/testcases/org/apache/poi/hssf/record/constant/TestConstantValueParser.java index 17a0901040..a72d0e1aed 100644 --- a/src/testcases/org/apache/poi/hssf/record/constant/TestConstantValueParser.java +++ b/src/testcases/org/apache/poi/hssf/record/constant/TestConstantValueParser.java @@ -21,11 +21,12 @@ import java.util.Arrays; import junit.framework.TestCase; -import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.hssf.record.TestcaseRecordInputStream; import org.apache.poi.hssf.record.UnicodeString; import org.apache.poi.hssf.usermodel.HSSFErrorConstants; import org.apache.poi.util.HexRead; +import org.apache.poi.util.LittleEndianByteArrayOutputStream; +import org.apache.poi.util.LittleEndianInput; /** * * @author Josh Micich @@ -52,14 +53,15 @@ public final class TestConstantValueParser extends TestCase { public void testEncode() { int size = ConstantValueParser.getEncodedSize(SAMPLE_VALUES); byte[] data = new byte[size]; - ConstantValueParser.encode(data, 0, SAMPLE_VALUES); + + ConstantValueParser.encode(new LittleEndianByteArrayOutputStream(data, 0), SAMPLE_VALUES); if (!Arrays.equals(data, SAMPLE_ENCODING)) { fail("Encoding differs"); } } public void testDecode() { - RecordInputStream in = TestcaseRecordInputStream.createWithFakeSid(SAMPLE_ENCODING); + LittleEndianInput in = TestcaseRecordInputStream.createLittleEndian(SAMPLE_ENCODING); Object[] values = ConstantValueParser.parse(in, 4); for (int i = 0; i < values.length; i++) { diff --git a/src/testcases/org/apache/poi/hssf/record/formula/TestArrayPtg.java b/src/testcases/org/apache/poi/hssf/record/formula/TestArrayPtg.java index 8d2fe631d2..94a16b3829 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/TestArrayPtg.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/TestArrayPtg.java @@ -24,6 +24,8 @@ import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.hssf.record.TestcaseRecordInputStream; import org.apache.poi.hssf.record.UnicodeString; import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.util.LittleEndianByteArrayOutputStream; +import org.apache.poi.util.LittleEndianInput; import junit.framework.AssertionFailedError; import junit.framework.TestCase; @@ -54,9 +56,9 @@ public final class TestArrayPtg extends TestCase { */ public void testReadWriteTokenValueBytes() { - ArrayPtg ptg = new ArrayPtg(TestcaseRecordInputStream.createWithFakeSid(ENCODED_PTG_DATA)); + ArrayPtg ptg = new ArrayPtg(TestcaseRecordInputStream.createLittleEndian(ENCODED_PTG_DATA)); - ptg.readTokenValues(TestcaseRecordInputStream.createWithFakeSid(ENCODED_CONSTANT_DATA)); + ptg.readTokenValues(TestcaseRecordInputStream.createLittleEndian(ENCODED_CONSTANT_DATA)); assertEquals(3, ptg.getColumnCount()); assertEquals(2, ptg.getRowCount()); Object[][] values = ptg.getTokenArrayValues(); @@ -70,7 +72,7 @@ public final class TestArrayPtg extends TestCase { assertEquals(new UnicodeString("FG"), values[1][2]); byte[] outBuf = new byte[ENCODED_CONSTANT_DATA.length]; - ptg.writeTokenValueBytes(outBuf, 0); + ptg.writeTokenValueBytes(new LittleEndianByteArrayOutputStream(outBuf, 0)); if(outBuf[0] == 4) { throw new AssertionFailedError("Identified bug 42564b"); @@ -82,8 +84,8 @@ public final class TestArrayPtg extends TestCase { * Excel stores array elements column by column. This test makes sure POI does the same. */ public void testElementOrdering() { - ArrayPtg ptg = new ArrayPtg(TestcaseRecordInputStream.createWithFakeSid(ENCODED_PTG_DATA)); - ptg.readTokenValues(TestcaseRecordInputStream.createWithFakeSid(ENCODED_CONSTANT_DATA)); + ArrayPtg ptg = new ArrayPtg(TestcaseRecordInputStream.createLittleEndian(ENCODED_PTG_DATA)); + ptg.readTokenValues(TestcaseRecordInputStream.createLittleEndian(ENCODED_CONSTANT_DATA)); assertEquals(3, ptg.getColumnCount()); assertEquals(2, ptg.getRowCount()); @@ -113,9 +115,9 @@ public final class TestArrayPtg extends TestCase { } public void testToFormulaString() { - ArrayPtg ptg = new ArrayPtg(TestcaseRecordInputStream.createWithFakeSid(ENCODED_PTG_DATA)); + ArrayPtg ptg = new ArrayPtg(TestcaseRecordInputStream.createLittleEndian(ENCODED_PTG_DATA)); - ptg.readTokenValues(TestcaseRecordInputStream.createWithFakeSid(ENCODED_CONSTANT_DATA)); + ptg.readTokenValues(TestcaseRecordInputStream.createLittleEndian(ENCODED_CONSTANT_DATA)); String actualFormula; try { @@ -146,7 +148,7 @@ public final class TestArrayPtg extends TestCase { // Force encoded operand class for tArray fullData[0] = (byte) (ArrayPtg.sid + operandClass); - RecordInputStream in = TestcaseRecordInputStream.createWithFakeSid(fullData); + LittleEndianInput in = TestcaseRecordInputStream.createLittleEndian(fullData); Ptg[] ptgs = Ptg.readTokens(ENCODED_PTG_DATA.length, in); assertEquals(1, ptgs.length); diff --git a/src/testcases/org/apache/poi/hssf/record/formula/TestAttrPtg.java b/src/testcases/org/apache/poi/hssf/record/formula/TestAttrPtg.java index d9342d61c0..7edb3f4fc5 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/TestAttrPtg.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/TestAttrPtg.java @@ -21,9 +21,9 @@ import java.util.Arrays; import junit.framework.AssertionFailedError; -import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.hssf.record.TestcaseRecordInputStream; import org.apache.poi.util.HexRead; +import org.apache.poi.util.LittleEndianInput; /** * Tests for {@link AttrPtg}. @@ -37,7 +37,7 @@ public final class TestAttrPtg extends AbstractPtgTestCase { */ public void testReserializeAttrChoose() { byte[] data = HexRead.readFromString("19, 04, 03, 00, 08, 00, 11, 00, 1A, 00, 23, 00"); - RecordInputStream in = TestcaseRecordInputStream.createWithFakeSid(data); + LittleEndianInput in = TestcaseRecordInputStream.createLittleEndian(data); Ptg[] ptgs = Ptg.readTokens(data.length, in); byte[] data2 = new byte[data.length]; try { diff --git a/src/testcases/org/apache/poi/hssf/record/formula/TestFuncPtg.java b/src/testcases/org/apache/poi/hssf/record/formula/TestFuncPtg.java index 827bea8332..bbf8c4cfb2 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/TestFuncPtg.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/TestFuncPtg.java @@ -34,7 +34,7 @@ public final class TestFuncPtg extends TestCase { 0, }; - FuncPtg ptg = new FuncPtg(TestcaseRecordInputStream.createWithFakeSid(fakeData) ); + FuncPtg ptg = new FuncPtg(TestcaseRecordInputStream.createLittleEndian(fakeData) ); assertEquals( "Len formula index is not 32(20H)", 0x20, ptg.getFunctionIndex() ); assertEquals( "Number of operands in the len formula", 1, ptg.getNumberOfOperands() ); assertEquals( "Function Name", "LEN", ptg.getName() ); diff --git a/src/testcases/org/apache/poi/hssf/record/formula/TestReferencePtg.java b/src/testcases/org/apache/poi/hssf/record/formula/TestReferencePtg.java index 6be329ed5a..dc8fd6b9b6 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/TestReferencePtg.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/TestReferencePtg.java @@ -23,10 +23,10 @@ import junit.framework.AssertionFailedError; import junit.framework.TestCase; import org.apache.poi.hssf.HSSFTestDataSamples; -import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.hssf.record.TestcaseRecordInputStream; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.util.LittleEndianInput; /** * Tests for {@link RefPtg}. @@ -94,7 +94,7 @@ public final class TestReferencePtg extends TestCase { 0x2C, 33, 44, 55, 66, }; public void testReadWrite_tRefN_bug45091() { - RecordInputStream in = TestcaseRecordInputStream.createWithFakeSid(tRefN_data); + LittleEndianInput in = TestcaseRecordInputStream.createLittleEndian(tRefN_data); Ptg[] ptgs = Ptg.readTokens(tRefN_data.length, in); byte[] outData = new byte[5]; Ptg.serializePtgs(ptgs, outData, 0); diff --git a/src/testcases/org/apache/poi/poifs/filesystem/TestDocumentInputStream.java b/src/testcases/org/apache/poi/poifs/filesystem/TestDocumentInputStream.java index b42f7ef71a..751826e3fb 100644 --- a/src/testcases/org/apache/poi/poifs/filesystem/TestDocumentInputStream.java +++ b/src/testcases/org/apache/poi/poifs/filesystem/TestDocumentInputStream.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -15,18 +14,16 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - package org.apache.poi.poifs.filesystem; -import java.io.*; - -import java.util.*; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.Arrays; -import junit.framework.*; +import junit.framework.TestCase; import org.apache.poi.poifs.property.DirectoryProperty; -import org.apache.poi.poifs.property.DocumentProperty; import org.apache.poi.poifs.storage.RawDataBlock; /** @@ -35,22 +32,9 @@ import org.apache.poi.poifs.storage.RawDataBlock; * @author Marc Johnson */ -public class TestDocumentInputStream - extends TestCase -{ +public final class TestDocumentInputStream extends TestCase { - /** - * Constructor TestDocumentInputStream - * - * @param name - * - * @exception IOException - */ - - public TestDocumentInputStream(String name) - throws IOException - { - super(name); + protected void setUp() throws Exception { int blocks = (_workbook_size + 511) / 512; _workbook_data = new byte[ 512 * blocks ]; @@ -86,13 +70,8 @@ public class TestDocumentInputStream /** * test constructor - * - * @exception IOException */ - - public void testConstructor() - throws IOException - { + public void testConstructor() throws IOException { DocumentInputStream stream = new DocumentInputStream(_workbook); assertEquals(_workbook_size, stream.available()); @@ -100,13 +79,8 @@ public class TestDocumentInputStream /** * test available() behavior - * - * @exception IOException */ - - public void testAvailable() - throws IOException - { + public void testAvailable() throws IOException { DocumentInputStream stream = new DocumentInputStream(_workbook); assertEquals(_workbook_size, stream.available()); @@ -115,9 +89,7 @@ public class TestDocumentInputStream { stream.available(); fail("Should have caught IOException"); - } - catch (IOException ignored) - { + } catch (IllegalStateException ignored) { // as expected } @@ -125,13 +97,8 @@ public class TestDocumentInputStream /** * test mark/reset/markSupported. - * - * @exception IOException */ - - public void testMarkFunctions() - throws IOException - { + public void testMarkFunctions() throws IOException { DocumentInputStream stream = new DocumentInputStream(_workbook); byte[] buffer = new byte[ _workbook_size / 5 ]; @@ -169,13 +136,8 @@ public class TestDocumentInputStream /** * test simple read method - * - * @exception IOException */ - - public void testReadSingleByte() - throws IOException - { + public void testReadSingleByte() throws IOException { DocumentInputStream stream = new DocumentInputStream(_workbook); int remaining = _workbook_size; @@ -205,13 +167,8 @@ public class TestDocumentInputStream /** * Test buffered read - * - * @exception IOException */ - - public void testBufferRead() - throws IOException - { + public void testBufferRead() throws IOException { DocumentInputStream stream = new DocumentInputStream(_workbook); try @@ -275,23 +232,14 @@ public class TestDocumentInputStream /** * Test complex buffered read - * - * @exception IOException */ - - public void testComplexBufferRead() - throws IOException - { + public void testComplexBufferRead() throws IOException { DocumentInputStream stream = new DocumentInputStream(_workbook); - try - { + try { stream.read(null, 0, 1); fail("Should have caught NullPointerException"); - } - catch (NullPointerException ignored) - { - + } catch (IllegalArgumentException ignored) { // as expected } @@ -391,13 +339,8 @@ public class TestDocumentInputStream /** * test skip - * - * @exception IOException */ - - public void testSkip() - throws IOException - { + public void testSkip() throws IOException { DocumentInputStream stream = new DocumentInputStream(_workbook); assertEquals(_workbook_size, stream.available()); @@ -422,17 +365,4 @@ public class TestDocumentInputStream stream.skip(2 + ( long ) Integer.MAX_VALUE)); assertEquals(0, stream.available()); } - - /** - * main method to run the unit tests - * - * @param ignored_args - */ - - public static void main(String [] ignored_args) - { - System.out.println( - "Testing org.apache.poi.poifs.filesystem.DocumentInputStream"); - junit.textui.TestRunner.run(TestDocumentInputStream.class); - } } diff --git a/src/testcases/org/apache/poi/poifs/storage/AllPOIFSStorageTests.java b/src/testcases/org/apache/poi/poifs/storage/AllPOIFSStorageTests.java index 8c15d389e3..b589922587 100755 --- a/src/testcases/org/apache/poi/poifs/storage/AllPOIFSStorageTests.java +++ b/src/testcases/org/apache/poi/poifs/storage/AllPOIFSStorageTests.java @@ -26,22 +26,22 @@ import junit.framework.TestSuite; */ public final class AllPOIFSStorageTests { - public static Test suite() { - TestSuite result = new TestSuite("Tests for org.apache.poi.poifs.storage"); - result.addTestSuite(TestBATBlock.class); - result.addTestSuite(TestBlockAllocationTableReader.class); - result.addTestSuite(TestBlockAllocationTableWriter.class); - result.addTestSuite(TestBlockListImpl.class); - result.addTestSuite(TestDocumentBlock.class); - result.addTestSuite(TestHeaderBlockReader.class); - result.addTestSuite(TestHeaderBlockWriter.class); - result.addTestSuite(TestPropertyBlock.class); - result.addTestSuite(TestRawDataBlock.class); - result.addTestSuite(TestRawDataBlockList.class); - result.addTestSuite(TestSmallBlockTableReader.class); - result.addTestSuite(TestSmallBlockTableWriter.class); - result.addTestSuite(TestSmallDocumentBlock.class); - result.addTestSuite(TestSmallDocumentBlockList.class); - return result; - } + public static Test suite() { + TestSuite result = new TestSuite(AllPOIFSStorageTests.class.getName()); + result.addTestSuite(TestBATBlock.class); + result.addTestSuite(TestBlockAllocationTableReader.class); + result.addTestSuite(TestBlockAllocationTableWriter.class); + result.addTestSuite(TestBlockListImpl.class); + result.addTestSuite(TestDocumentBlock.class); + result.addTestSuite(TestHeaderBlockReader.class); + result.addTestSuite(TestHeaderBlockWriter.class); + result.addTestSuite(TestPropertyBlock.class); + result.addTestSuite(TestRawDataBlock.class); + result.addTestSuite(TestRawDataBlockList.class); + result.addTestSuite(TestSmallBlockTableReader.class); + result.addTestSuite(TestSmallBlockTableWriter.class); + result.addTestSuite(TestSmallDocumentBlock.class); + result.addTestSuite(TestSmallDocumentBlockList.class); + return result; + } } diff --git a/src/testcases/org/apache/poi/poifs/storage/TestDocumentBlock.java b/src/testcases/org/apache/poi/poifs/storage/TestDocumentBlock.java index bb0ca8cd6c..3c15e2deae 100644 --- a/src/testcases/org/apache/poi/poifs/storage/TestDocumentBlock.java +++ b/src/testcases/org/apache/poi/poifs/storage/TestDocumentBlock.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -15,25 +14,21 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - package org.apache.poi.poifs.storage; -import java.io.*; - -import java.util.*; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; -import junit.framework.*; +import junit.framework.TestCase; /** * Class to test DocumentBlock functionality * * @author Marc Johnson */ - -public class TestDocumentBlock - extends TestCase -{ +public final class TestDocumentBlock extends TestCase { static final private byte[] _testdata; static @@ -44,25 +39,10 @@ public class TestDocumentBlock _testdata[ j ] = ( byte ) j; } } - ; - - /** - * Constructor TestDocumentBlock - * - * @param name - */ - - public TestDocumentBlock(String name) - { - super(name); - } /** * Test the writing DocumentBlock constructor. - * - * @exception IOException */ - public void testConstructor() throws IOException { @@ -88,46 +68,10 @@ public class TestDocumentBlock assertEquals(_testdata.length, size); } - /** - * test static read method - * - * @exception IOException - */ - - public void testRead() - throws IOException - { - DocumentBlock[] blocks = new DocumentBlock[ 4 ]; - ByteArrayInputStream input = new ByteArrayInputStream(_testdata); - - for (int j = 0; j < 4; j++) - { - blocks[ j ] = new DocumentBlock(input); - } - for (int j = 1; j <= 2000; j += 17) - { - byte[] buffer = new byte[ j ]; - int offset = 0; - - for (int k = 0; k < (2000 / j); k++) - { - DocumentBlock.read(blocks, buffer, offset); - for (int n = 0; n < buffer.length; n++) - { - assertEquals("checking byte " + (k * j) + n, - _testdata[ (k * j) + n ], buffer[ n ]); - } - offset += j; - } - } - } /** * Test 'reading' constructor - * - * @exception IOException */ - public void testReadingConstructor() throws IOException { @@ -164,17 +108,4 @@ public class TestDocumentBlock assertEquals(( byte ) 0xFF, copy[ j ]); } } - - /** - * main method to run the unit tests - * - * @param ignored_args - */ - - public static void main(String [] ignored_args) - { - System.out - .println("Testing org.apache.poi.poifs.storage.DocumentBlock"); - junit.textui.TestRunner.run(TestDocumentBlock.class); - } } diff --git a/src/testcases/org/apache/poi/poifs/storage/TestSmallDocumentBlock.java b/src/testcases/org/apache/poi/poifs/storage/TestSmallDocumentBlock.java index 05e51974f8..dfbfbb4e51 100644 --- a/src/testcases/org/apache/poi/poifs/storage/TestSmallDocumentBlock.java +++ b/src/testcases/org/apache/poi/poifs/storage/TestSmallDocumentBlock.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -15,25 +14,24 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - package org.apache.poi.poifs.storage; -import java.io.*; - -import java.util.*; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; -import junit.framework.*; +import junit.framework.TestCase; /** * Class to test SmallDocumentBlock functionality * * @author Marc Johnson */ - -public class TestSmallDocumentBlock - extends TestCase -{ +public final class TestSmallDocumentBlock extends TestCase { static final private byte[] _testdata; static final private int _testdata_size = 2999; @@ -45,25 +43,10 @@ public class TestSmallDocumentBlock _testdata[ j ] = ( byte ) j; } } - ; - - /** - * constructor - * - * @param name - */ - - public TestSmallDocumentBlock(String name) - { - super(name); - } /** * Test conversion from DocumentBlocks - * - * @exception IOException */ - public void testConvert1() throws IOException { @@ -113,12 +96,7 @@ public class TestSmallDocumentBlock /** * Test conversion from byte array - * - * @exception IOException; - * - * @exception IOException */ - public void testConvert2() throws IOException { @@ -154,57 +132,9 @@ public class TestSmallDocumentBlock } } - /** - * Test read method - * - * @exception IOException - */ - - public void testRead() - throws IOException - { - ByteArrayInputStream stream = new ByteArrayInputStream(_testdata); - List documents = new ArrayList(); - - while (true) - { - DocumentBlock block = new DocumentBlock(stream); - - documents.add(block); - if (block.partiallyRead()) - { - break; - } - } - SmallDocumentBlock[] blocks = - SmallDocumentBlock - .convert(( BlockWritable [] ) documents - .toArray(new DocumentBlock[ 0 ]), _testdata_size); - - for (int j = 1; j <= _testdata_size; j += 38) - { - byte[] buffer = new byte[ j ]; - int offset = 0; - - for (int k = 0; k < (_testdata_size / j); k++) - { - SmallDocumentBlock.read(blocks, buffer, offset); - for (int n = 0; n < buffer.length; n++) - { - assertEquals("checking byte " + (k * j) + n, - _testdata[ (k * j) + n ], buffer[ n ]); - } - offset += j; - } - } - } - /** * test fill - * - * @exception IOException */ - public void testFill() throws IOException { @@ -294,17 +224,4 @@ public class TestSmallDocumentBlock } } } - - /** - * main method to run the unit tests - * - * @param ignored_args - */ - - public static void main(String [] ignored_args) - { - System.out.println( - "Testing org.apache.poi.poifs.storage.SmallDocumentBlock"); - junit.textui.TestRunner.run(TestSmallDocumentBlock.class); - } } diff --git a/src/testcases/org/apache/poi/util/AllPOIUtilTests.java b/src/testcases/org/apache/poi/util/AllPOIUtilTests.java index bb6d382047..6364c8c354 100755 --- a/src/testcases/org/apache/poi/util/AllPOIUtilTests.java +++ b/src/testcases/org/apache/poi/util/AllPOIUtilTests.java @@ -27,12 +27,11 @@ import junit.framework.TestSuite; public final class AllPOIUtilTests { public static Test suite() { - TestSuite result = new TestSuite("Tests for org.apache.poi.util"); + TestSuite result = new TestSuite(AllPOIUtilTests.class.getName()); result.addTestSuite(TestArrayUtil.class); result.addTestSuite(TestBinaryTree.class); result.addTestSuite(TestBitField.class); result.addTestSuite(TestByteField.class); - result.addTestSuite(TestDoubleList2d.class); result.addTestSuite(TestHexDump.class); result.addTestSuite(TestIntegerField.class); result.addTestSuite(TestIntList.class); diff --git a/src/testcases/org/apache/poi/util/TestDoubleList2d.java b/src/testcases/org/apache/poi/util/TestDoubleList2d.java deleted file mode 100644 index b85e7eb4e5..0000000000 --- a/src/testcases/org/apache/poi/util/TestDoubleList2d.java +++ /dev/null @@ -1,50 +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.util; - -import junit.framework.TestCase; - -/** - * @version $Id$ - */ -public class TestDoubleList2d - extends TestCase -{ - public void testAccess() - throws Exception - { - DoubleList2d array = new DoubleList2d(); - assertEquals( 0, array.get( 0, 0 ), 0.00001 ); - assertEquals( 0, array.get( 1, 1 ), 0.00001 ); - assertEquals( 0, array.get( 100, 100 ), 0.00001 ); - array.set( 100, 100, 999 ); - assertEquals( 999, array.get( 100, 100 ), 0.00001 ); - assertEquals( 0, array.get( 0, 0 ), 0.00001 ); - array.set( 0, 0, 999 ); - assertEquals( 999, array.get( 0, 0 ), 0.00001 ); - - try - { - array.get( -1, -1 ); - fail(); - } - catch ( ArrayIndexOutOfBoundsException e ) - { - // pass - } - } -} diff --git a/src/testcases/org/apache/poi/util/TestLittleEndian.java b/src/testcases/org/apache/poi/util/TestLittleEndian.java index 0ffcd189da..bba8a653e3 100644 --- a/src/testcases/org/apache/poi/util/TestLittleEndian.java +++ b/src/testcases/org/apache/poi/util/TestLittleEndian.java @@ -97,13 +97,13 @@ public final class TestLittleEndian extends TestCase { * test the getDouble() method */ public void testGetDouble() { - assertEquals(_doubles[0], LittleEndian.getDouble(_double_array), 0.000001 ); + assertEquals(_doubles[0], LittleEndian.getDouble(_double_array, 0), 0.000001 ); assertEquals(_doubles[1], LittleEndian.getDouble( _double_array, LittleEndian.DOUBLE_SIZE), 0.000001); - assertTrue(Double.isNaN(LittleEndian.getDouble(_nan_double_array))); + assertTrue(Double.isNaN(LittleEndian.getDouble(_nan_double_array, 0))); - double nan = LittleEndian.getDouble(_nan_double_array); + double nan = LittleEndian.getDouble(_nan_double_array, 0); byte[] data = new byte[8]; - LittleEndian.putDouble(data, nan); + LittleEndian.putDouble(data, 0, nan); for ( int i = 0; i < data.length; i++ ) { assertEquals(data[i], _nan_double_array[i]); } @@ -144,7 +144,7 @@ public final class TestLittleEndian extends TestCase { (byte) 0x02, }; - assertEquals(0xFFFFFFFFFFFFFF01L, LittleEndian.getLong(testdata)); + assertEquals(0xFFFFFFFFFFFFFF01L, LittleEndian.getLong(testdata, 0)); assertEquals(0x02FFFFFFFFFFFFFFL, LittleEndian.getLong(testdata, 1)); } @@ -194,7 +194,7 @@ public final class TestLittleEndian extends TestCase { public void testPutDouble() { byte[] received = new byte[ LittleEndian.DOUBLE_SIZE + 1 ]; - LittleEndian.putDouble(received, _doubles[0]); + LittleEndian.putDouble(received, 0, _doubles[0]); assertTrue(compareByteArrays(received, _double_array, 0, LittleEndian.DOUBLE_SIZE)); LittleEndian.putDouble(received, 1, _doubles[1]); byte[] expected = new byte[ LittleEndian.DOUBLE_SIZE + 1 ]; @@ -224,7 +224,7 @@ public final class TestLittleEndian extends TestCase { long testdata0 = 0xFFFFFFFFFFFFFF01L; long testdata1 = 0x02FFFFFFFFFFFFFFL; - LittleEndian.putLong(received, testdata0); + LittleEndian.putLong(received, 0, testdata0); assertTrue(compareByteArrays(received, expected, 0, LittleEndian.LONG_SIZE)); LittleEndian.putLong(received, 1, testdata1); assertTrue(compareByteArrays(received, expected, 1, LittleEndian.LONG_SIZE)); diff --git a/src/testcases/org/apache/poi/util/TestStringUtil.java b/src/testcases/org/apache/poi/util/TestStringUtil.java index b22439cd09..f1b5cfd954 100644 --- a/src/testcases/org/apache/poi/util/TestStringUtil.java +++ b/src/testcases/org/apache/poi/util/TestStringUtil.java @@ -42,43 +42,7 @@ public class TestStringUtil super( name ); } - /** - * test simple form of getFromUnicode - */ - public void testSimpleGetFromUnicode() - { - byte[] test_data = new byte[32]; - int index = 0; - - for ( int k = 0; k < 16; k++ ) - { - test_data[index++] = (byte) 0; - test_data[index++] = (byte) ( 'a' + k ); - } - - assertEquals( "abcdefghijklmnop", - StringUtil.getFromUnicodeBE( test_data ) ); - } - - /** - * test simple form of getFromUnicode with symbols with code below and more 127 - */ - public void testGetFromUnicodeSymbolsWithCodesMoreThan127() - { - byte[] test_data = new byte[]{0x04, 0x22, - 0x04, 0x35, - 0x04, 0x41, - 0x04, 0x42, - 0x00, 0x20, - 0x00, 0x74, - 0x00, 0x65, - 0x00, 0x73, - 0x00, 0x74, - }; - assertEquals( "\u0422\u0435\u0441\u0442 test", - StringUtil.getFromUnicodeBE( test_data ) ); - } /** * test getFromUnicodeHigh for symbols with code below and more 127 @@ -101,62 +65,7 @@ public class TestStringUtil StringUtil.getFromUnicodeLE( test_data ) ); } - /** - * Test more complex form of getFromUnicode - */ - public void testComplexGetFromUnicode() - { - byte[] test_data = new byte[32]; - int index = 0; - for ( int k = 0; k < 16; k++ ) - { - test_data[index++] = (byte) 0; - test_data[index++] = (byte) ( 'a' + k ); - } - assertEquals( "abcdefghijklmno", - StringUtil.getFromUnicodeBE( test_data, 0, 15 ) ); - assertEquals( "bcdefghijklmnop", - StringUtil.getFromUnicodeBE( test_data, 2, 15 ) ); - try - { - StringUtil.getFromUnicodeBE( test_data, -1, 16 ); - fail( "Should have caught ArrayIndexOutOfBoundsException" ); - } - catch ( ArrayIndexOutOfBoundsException ignored ) - { - // as expected - } - try - { - StringUtil.getFromUnicodeBE( test_data, 32, 16 ); - fail( "Should have caught ArrayIndexOutOfBoundsException" ); - } - catch ( ArrayIndexOutOfBoundsException ignored ) - { - // as expected - } - - try - { - StringUtil.getFromUnicodeBE( test_data, 1, 16 ); - fail( "Should have caught IllegalArgumentException" ); - } - catch ( IllegalArgumentException ignored ) - { - // as expected - } - - try - { - StringUtil.getFromUnicodeBE( test_data, 1, -1 ); - fail( "Should have caught IllegalArgumentException" ); - } - catch ( IllegalArgumentException ignored ) - { - // as expected - } - } /** * Test putCompressedUnicode