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
This commit is contained in:
Josh Micich 2008-10-26 07:18:17 +00:00
parent 2ccdeeaee6
commit 5965d73fd1
89 changed files with 2781 additions and 4155 deletions

View File

@ -1,4 +1,3 @@
/* ==================================================================== /* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
@ -16,10 +15,8 @@
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.contrib.poibrowser; package org.apache.poi.contrib.poibrowser;
import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -160,17 +157,7 @@ public class TreeReaderListener implements POIFSReaderListener
throw new RuntimeException(t.getMessage()); throw new RuntimeException(t.getMessage());
} }
try
{
is.close(); is.close();
}
catch (IOException ex)
{
System.err.println
("Unexpected exception while closing " +
event.getName() + " in " + event.getPath().toString());
ex.printStackTrace(System.err);
}
final MutableTreeNode parentNode = getNode(d.path, filename, rootNode); final MutableTreeNode parentNode = getNode(d.path, filename, rootNode);
final MutableTreeNode nameNode = new DefaultMutableTreeNode(d.name); final MutableTreeNode nameNode = new DefaultMutableTreeNode(d.name);

View File

@ -788,7 +788,7 @@ public final class Workbook implements Model {
if(r instanceof ExtendedFormatRecord) { if(r instanceof ExtendedFormatRecord) {
} else if(r instanceof StyleRecord) { } else if(r instanceof StyleRecord) {
StyleRecord sr = (StyleRecord)r; StyleRecord sr = (StyleRecord)r;
if(sr.getIndex() == xfIndex) { if(sr.getXFIndex() == xfIndex) {
return sr; return sr;
} }
} else { } else {
@ -806,7 +806,7 @@ public final class Workbook implements Model {
// Style records always follow after // Style records always follow after
// the ExtendedFormat records // the ExtendedFormat records
StyleRecord newSR = new StyleRecord(); StyleRecord newSR = new StyleRecord();
newSR.setIndex((short)xfIndex); newSR.setXFIndex(xfIndex);
// Find the spot // Find the spot
int addAt = -1; 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.StyleRecord
* @see org.apache.poi.hssf.record.Record * @see org.apache.poi.hssf.record.Record
*/ */
protected Record createStyle(int id) { // we'll need multiple editions protected Record createStyle(int id) { // we'll need multiple editions
StyleRecord retval = new StyleRecord(); StyleRecord retval = new StyleRecord();
switch (id) { switch (id) {
case 0 : case 0 :
retval.setIndex(( short ) 0xffff8010); retval.setXFIndex(0x010);
retval.setBuiltin(( byte ) 3); retval.setBuiltinStyle(3);
retval.setOutlineStyleLevel(( byte ) 0xffffffff); retval.setOutlineStyleLevel(( byte ) 0xffffffff);
break; break;
case 1 : case 1 :
retval.setIndex(( short ) 0xffff8011); retval.setXFIndex(0x011);
retval.setBuiltin(( byte ) 6); retval.setBuiltinStyle(6);
retval.setOutlineStyleLevel(( byte ) 0xffffffff); retval.setOutlineStyleLevel(( byte ) 0xffffffff);
break; break;
case 2 : case 2 :
retval.setIndex(( short ) 0xffff8012); retval.setXFIndex(0x012);
retval.setBuiltin(( byte ) 4); retval.setBuiltinStyle(4);
retval.setOutlineStyleLevel(( byte ) 0xffffffff); retval.setOutlineStyleLevel(( byte ) 0xffffffff);
break; break;
case 3 : case 3 :
retval.setIndex(( short ) 0xffff8013); retval.setXFIndex(0x013);
retval.setBuiltin(( byte ) 7); retval.setBuiltinStyle(7);
retval.setOutlineStyleLevel(( byte ) 0xffffffff); retval.setOutlineStyleLevel(( byte ) 0xffffffff);
break; break;
case 4 : case 4 :
retval.setIndex(( short ) 0xffff8000); retval.setXFIndex(0x000);
retval.setBuiltin(( byte ) 0); retval.setBuiltinStyle(0);
retval.setOutlineStyleLevel(( byte ) 0xffffffff); retval.setOutlineStyleLevel(( byte ) 0xffffffff);
break; break;
case 5 : case 5 :
retval.setIndex(( short ) 0xffff8014); retval.setXFIndex(0x014);
retval.setBuiltin(( byte ) 5); retval.setBuiltinStyle(5);
retval.setOutlineStyleLevel(( byte ) 0xffffffff); retval.setOutlineStyleLevel(( byte ) 0xffffffff);
break; break;
} }

View File

@ -18,17 +18,18 @@
package org.apache.poi.hssf.record; package org.apache.poi.hssf.record;
import org.apache.poi.hssf.record.constant.ConstantValueParser; 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 <P> * Title: CRN(0x005A) <p/>
* Description: This record stores the contents of an external cell or cell range <P> * Description: This record stores the contents of an external cell or cell range <p/>
* REFERENCE: 5.23<P> * REFERENCE: OOO 5.23<p/>
* *
* @author josh micich * @author josh micich
*/ */
public final class CRNRecord extends Record { 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_1_last_column_index;
private int field_2_first_column_index; private int field_2_first_column_index;
@ -45,8 +46,8 @@ public final class CRNRecord extends Record {
public CRNRecord(RecordInputStream in) { public CRNRecord(RecordInputStream in) {
field_1_last_column_index = in.readByte() & 0x00FF; field_1_last_column_index = in.readUByte();
field_2_first_column_index = in.readByte() & 0x00FF; field_2_first_column_index = in.readUByte();
field_3_row_index = in.readShort(); field_3_row_index = in.readShort();
int nValues = field_1_last_column_index - field_2_first_column_index + 1; int nValues = field_1_last_column_index - field_2_first_column_index + 1;
field_4_constant_values = ConstantValueParser.parse(in, nValues); 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) { public int serialize(int offset, byte [] data) {
int dataSize = getDataSize(); int dataSize = getDataSize();
LittleEndian.putShort(data, 0 + offset, sid); int recSize = 4 + dataSize;
LittleEndian.putShort(data, 2 + offset, (short) dataSize); LittleEndianOutput out = new LittleEndianByteArrayOutputStream(data, offset, recSize);
LittleEndian.putByte(data, 4 + offset, field_1_last_column_index); out.writeShort(sid);
LittleEndian.putByte(data, 5 + offset, field_2_first_column_index); out.writeShort(dataSize);
LittleEndian.putShort(data, 6 + offset, (short) field_3_row_index); out.writeByte(field_1_last_column_index);
ConstantValueParser.encode(data, 8 + offset, field_4_constant_values); out.writeByte(field_2_first_column_index);
return getRecordSize(); out.writeShort(field_3_row_index);
ConstantValueParser.encode(out, field_4_constant_values);
return recSize;
} }
public int getRecordSize() { public int getRecordSize() {

View File

@ -27,6 +27,7 @@ import org.apache.poi.hssf.record.formula.RefPtg;
import org.apache.poi.util.HexDump; import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.LittleEndianInput; import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndianInputStream;
import org.apache.poi.util.LittleEndianOutput; import org.apache.poi.util.LittleEndianOutput;
import org.apache.poi.util.StringUtil; import org.apache.poi.util.StringUtil;
@ -111,10 +112,10 @@ public final class EmbeddedObjectRefSubRecord extends SubRecord {
field_3_unicode_flag = ( in.readByte() & 0x01 ) != 0; field_3_unicode_flag = ( in.readByte() & 0x01 ) != 0;
remaining -= LittleEndian.BYTE_SIZE; remaining -= LittleEndian.BYTE_SIZE;
if (field_3_unicode_flag) { if (field_3_unicode_flag) {
field_4_ole_classname = in.readUnicodeLEString(nChars); field_4_ole_classname = StringUtil.readUnicodeLE(in, nChars);
stringByteCount = nChars * 2; stringByteCount = nChars * 2;
} else { } else {
field_4_ole_classname = in.readCompressedUnicode(nChars); field_4_ole_classname = StringUtil.readCompressedUnicode(in, nChars);
stringByteCount = nChars; stringByteCount = nChars;
} }
} else { } else {
@ -156,12 +157,7 @@ public final class EmbeddedObjectRefSubRecord extends SubRecord {
} }
private static Ptg readRefPtg(byte[] formulaRawBytes) { private static Ptg readRefPtg(byte[] formulaRawBytes) {
byte[] data = new byte[formulaRawBytes.length + 4]; LittleEndianInput in = new LittleEndianInputStream(new ByteArrayInputStream(formulaRawBytes));
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(); byte ptgSid = in.readByte();
switch(ptgSid) { switch(ptgSid) {
case AreaPtg.sid: return new AreaPtg(in); case AreaPtg.sid: return new AreaPtg(in);

View File

@ -51,7 +51,7 @@ public final class LinkedDataFormulaField {
.append( "=" ) .append( "=" )
.append(ptg.toString() ) .append(ptg.toString() )
.append( "\n" ) .append( "\n" )
.append(ptg.toDebugString() ) .append(ptg.toString())
.append( "\n" ); .append( "\n" );
} }
} }

View File

@ -17,12 +17,12 @@
package org.apache.poi.hssf.record; 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.LittleEndian;
import org.apache.poi.util.LittleEndianInput; import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndianInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ByteArrayOutputStream;
/** /**
* Title: Record Input Stream<P> * Title: Record Input Stream<P>
@ -34,57 +34,112 @@ public final class RecordInputStream extends InputStream implements LittleEndian
/** Maximum size of a single record (minus the 4 byte header) without a continue*/ /** Maximum size of a single record (minus the 4 byte header) without a continue*/
public final static short MAX_RECORD_DATA_SIZE = 8224; public final static short MAX_RECORD_DATA_SIZE = 8224;
private static final int INVALID_SID_VALUE = -1; 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 = { };
private InputStream in; /** {@link LittleEndianInput} facet of the wrapped {@link InputStream} */
private short currentSid; private final LittleEndianInput _le;
private short currentLength = -1; /** the record identifier of the BIFF record currently being read */
private short nextSid; private int _currentSid;
/**
private final byte[] data = new byte[MAX_RECORD_DATA_SIZE]; * Length of the data section of the current BIFF record (always 4 less than the total record size).
private short recordOffset; * When uninitialised, this field is set to {@link #DATA_LEN_NEEDS_TO_BE_READ}.
private long pos; */
private int _currentDataLength;
private boolean autoContinue = true; /**
* 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 { public RecordInputStream(InputStream in) throws RecordFormatException {
this.in = in; if (in instanceof LittleEndianInput) {
try { // accessing directly is an optimisation
nextSid = LittleEndian.readShort(in); _le = (LittleEndianInput) in;
//Don't increment the pos just yet (technically we are at the start of } else {
//the record stream until nextRecord is called). // less optimal, but should work OK just the same. Often occurs in junit tests.
} catch (IOException ex) { _le = new LittleEndianInputStream(in);
throw new RecordFormatException("Error reading bytes", ex);
} }
_nextSid = readNextSid();
}
/**
* @returns the number of bytes available in the current BIFF record
* @see #remaining()
*/
public int available() {
return remaining();
} }
/** This method will read a byte from the current record*/
public int read() { public int read() {
checkRecordPosition(LittleEndian.BYTE_SIZE); checkRecordPosition(LittleEndian.BYTE_SIZE);
_currentDataOffset += LittleEndian.BYTE_SIZE;
byte result = data[recordOffset]; return _le.readUByte();
recordOffset += LittleEndian.BYTE_SIZE; }
pos += LittleEndian.BYTE_SIZE; public int read(byte[] b, int off, int len) {
return result; int limit = Math.min(len, remaining());
if (limit == 0) {
return 0;
}
readFully(b, off,limit);
return limit;
} }
public short getSid() { public short getSid() {
return currentSid; return (short) _currentSid;
}
public short getLength() {
return currentLength;
}
public short getRecordOffset() {
return recordOffset;
}
public long getPos() {
return pos;
} }
/**
* 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() { public boolean hasNextRecord() {
return nextSid != INVALID_SID_VALUE; 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;
}
/**
*
* @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;
} }
/** Moves to the next record in the stream. /** Moves to the next record in the stream.
@ -92,57 +147,34 @@ public final class RecordInputStream extends InputStream implements LittleEndian
* <i>Note: The auto continue flag is reset to true</i> * <i>Note: The auto continue flag is reset to true</i>
*/ */
public void nextRecord() throws RecordFormatException { public void nextRecord() throws RecordFormatException {
if ((currentLength != -1) && (currentLength != recordOffset)) { if (_nextSid == INVALID_SID_VALUE) {
System.out.println("WARN. Unread "+remaining()+" bytes of record 0x"+Integer.toHexString(currentSid)); throw new IllegalStateException("EOF - next record not available");
} }
currentSid = nextSid; if (_currentDataLength != DATA_LEN_NEEDS_TO_BE_READ) {
pos += LittleEndian.SHORT_SIZE; throw new IllegalStateException("Cannot call nextRecord() without checking hasNextRecord() first");
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; _currentSid = _nextSid;
} else { _currentDataOffset = 0;
nextSid = LittleEndian.readShort(in); _currentDataLength = _le.readUShort();
if (nextSid == INVALID_SID_VALUE) { if (_currentDataLength > MAX_RECORD_DATA_SIZE) {
throw new RecordFormatException("Found sid " + nextSid + " after record with sid 0x" throw new RecordFormatException("The content of an excel record cannot exceed "
+ Integer.toHexString(currentSid).toUpperCase()); + MAX_RECORD_DATA_SIZE + " bytes");
} }
}
} catch (IOException ex) {
throw new RecordFormatException("Error reading bytes", ex);
}
}
public void setAutoContinue(boolean enable) {
this.autoContinue = enable;
}
public boolean getAutoContinue() {
return autoContinue;
} }
private void checkRecordPosition(int requiredByteCount) { private void checkRecordPosition(int requiredByteCount) {
if (remaining() < requiredByteCount) { int nAvailable = remaining();
if (isContinueNext() && autoContinue) { if (nAvailable >= requiredByteCount) {
// all OK
return;
}
if (nAvailable == 0 && isContinueNext()) {
nextRecord(); nextRecord();
} else { return;
throw new ArrayIndexOutOfBoundsException();
}
} }
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() { public byte readByte() {
checkRecordPosition(LittleEndian.BYTE_SIZE); checkRecordPosition(LittleEndian.BYTE_SIZE);
_currentDataOffset += LittleEndian.BYTE_SIZE;
byte result = data[recordOffset]; return _le.readByte();
recordOffset += LittleEndian.BYTE_SIZE;
pos += LittleEndian.BYTE_SIZE;
return result;
} }
/** /**
@ -162,29 +191,20 @@ public final class RecordInputStream extends InputStream implements LittleEndian
*/ */
public short readShort() { public short readShort() {
checkRecordPosition(LittleEndian.SHORT_SIZE); checkRecordPosition(LittleEndian.SHORT_SIZE);
_currentDataOffset += LittleEndian.SHORT_SIZE;
short result = LittleEndian.getShort(data, recordOffset); return _le.readShort();
recordOffset += LittleEndian.SHORT_SIZE;
pos += LittleEndian.SHORT_SIZE;
return result;
} }
public int readInt() { public int readInt() {
checkRecordPosition(LittleEndian.INT_SIZE); checkRecordPosition(LittleEndian.INT_SIZE);
_currentDataOffset += LittleEndian.INT_SIZE;
int result = LittleEndian.getInt(data, recordOffset); return _le.readInt();
recordOffset += LittleEndian.INT_SIZE;
pos += LittleEndian.INT_SIZE;
return result;
} }
public long readLong() { public long readLong() {
checkRecordPosition(LittleEndian.LONG_SIZE); checkRecordPosition(LittleEndian.LONG_SIZE);
_currentDataOffset += LittleEndian.LONG_SIZE;
long result = LittleEndian.getLong(data, recordOffset); return _le.readLong();
recordOffset += LittleEndian.LONG_SIZE;
pos += LittleEndian.LONG_SIZE;
return result;
} }
/** /**
@ -200,22 +220,18 @@ public final class RecordInputStream extends InputStream implements LittleEndian
*/ */
public int readUShort() { public int readUShort() {
checkRecordPosition(LittleEndian.SHORT_SIZE); checkRecordPosition(LittleEndian.SHORT_SIZE);
_currentDataOffset += LittleEndian.SHORT_SIZE;
int result = LittleEndian.getUShort(data, recordOffset); return _le.readUShort();
recordOffset += LittleEndian.SHORT_SIZE;
pos += LittleEndian.SHORT_SIZE;
return result;
} }
public double readDouble() { public double readDouble() {
checkRecordPosition(LittleEndian.DOUBLE_SIZE); checkRecordPosition(LittleEndian.DOUBLE_SIZE);
long valueLongBits = LittleEndian.getLong(data, recordOffset); _currentDataOffset += LittleEndian.DOUBLE_SIZE;
long valueLongBits = _le.readLong();
double result = Double.longBitsToDouble(valueLongBits); double result = Double.longBitsToDouble(valueLongBits);
if (Double.isNaN(result)) { if (Double.isNaN(result)) {
throw new RuntimeException("Did not expect to read NaN"); // (Because Excel typically doesn't write NaN 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; return result;
} }
public void readFully(byte[] buf) { 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) { public void readFully(byte[] buf, int off, int len) {
checkRecordPosition(len); checkRecordPosition(len);
System.arraycopy(data, recordOffset, buf, off, len); _le.readFully(buf, off, len);
recordOffset+=len; _currentDataOffset+=len;
pos+=len;
} }
public String readString() { public String readString() {
@ -321,10 +336,11 @@ public final class RecordInputStream extends InputStream implements LittleEndian
*/ */
public byte[] readRemainder() { public byte[] readRemainder() {
int size = remaining(); int size = remaining();
if (size ==0) {
return EMPTY_BYTE_ARRAY;
}
byte[] result = new byte[size]; byte[] result = new byte[size];
System.arraycopy(data, recordOffset, result, 0, size); readFully(result);
recordOffset += size;
pos += size;
return result; return result;
} }
@ -355,14 +371,29 @@ public final class RecordInputStream extends InputStream implements LittleEndian
* @return The number of bytes remaining in the current record * @return The number of bytes remaining in the current record
*/ */
public int remaining() { public int remaining() {
return (currentLength - recordOffset); 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. * @return <code>true</code> when a {@link ContinueRecord} is next.
*/ */
public boolean isContinueNext() { private boolean isContinueNext() {
return (nextSid == ContinueRecord.sid); 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;
} }
} }

View File

@ -19,184 +19,77 @@ package org.apache.poi.hssf.record;
import org.apache.poi.util.BitField; import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory; import org.apache.poi.util.BitFieldFactory;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.StringUtil; import org.apache.poi.util.StringUtil;
/** /**
* Title: Style Record<P> * Title: Style Record (0x0293)<p/>
* Description: Describes a builtin to the gui or user defined style<P> * Description: Describes a builtin to the gui or user defined style<P>
* REFERENCE: PG 390 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)<P> * REFERENCE: PG 390 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)<P>
* @author Andrew C. Oliver (acoliver at apache dot org) * @author Andrew C. Oliver (acoliver at apache dot org)
* @author aviks : string fixes for UserDefined Style * @author aviks : string fixes for UserDefined Style
* @version 2.0-pre
*/ */
public final class StyleRecord extends Record { public final class StyleRecord extends Record {
public final static short sid = 0x0293; public final static short sid = 0x0293;
private static final BitField fHighByte = BitFieldFactory.getInstance(0x01); private static final BitField is16BitUnicodeFlag = BitFieldFactory.getInstance(0x01);
public final static short STYLE_USER_DEFINED = 0; private static final BitField styleIndexMask = BitFieldFactory.getInstance(0x0FFF);
public final static short STYLE_BUILT_IN = 1; private static final BitField isBuiltinFlag = BitFieldFactory.getInstance(0x8000);
// shared by both user defined and builtin styles /** shared by both user defined and built-in styles */
private short field_1_xf_index; // TODO: bitfield candidate private int field_1_xf_index;
// only for built in styles // only for built in styles
private byte field_2_builtin_style; private int field_2_builtin_style;
private byte field_3_outline_style_level; private int field_3_outline_style_level;
// only for user defined styles // only for user defined styles
private short field_2_name_length; //OO doc says 16 bit length, so we believe private int field_3_string_options;
private byte field_3_string_options;
private String field_4_name; private String field_4_name;
public StyleRecord() /**
{ * 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) public StyleRecord(RecordInputStream in) {
{
field_1_xf_index = in.readShort(); field_1_xf_index = in.readShort();
if (getType() == STYLE_BUILT_IN) if (isBuiltin()) {
{
field_2_builtin_style = in.readByte(); field_2_builtin_style = in.readByte();
field_3_outline_style_level = 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 { } else {
field_4_name=StringUtil.getFromCompressedUnicode(string, 0, field_2_name_length); 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);
} }
} }
} }
// 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 * set the actual index of the style extended format record
* @see #setIndex(short) * @param xfIndex of the xf record
* @param index of the xf record
*/ */
public void setXFIndex(int xfIndex) {
public void setXFIndex(short index) field_1_xf_index = styleIndexMask.setValue(field_1_xf_index, xfIndex);
{
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);
} }
/** /**
@ -204,143 +97,99 @@ public final class StyleRecord extends Record {
* @see #getIndex() * @see #getIndex()
* @return index of the xf record * @return index of the xf record
*/ */
public int getXFIndex() {
public short getXFIndex() return styleIndexMask.getValue(field_1_xf_index);
{
return ( short ) (field_1_xf_index & 0x1FFF);
} }
// end bitfields /**
// only for user defined records * 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 user defined record get the length of the style name * if this is a builtin style set the number of the built in style
* @return length of the style's name * @param builtinStyleId style number (0-7)
* @see #getName() *
*/ */
public void setBuiltinStyle(int builtinStyleId) {
field_1_xf_index = isBuiltinFlag.set(field_1_xf_index);
field_2_builtin_style = builtinStyleId;
}
public short getNameLength() /**
{ * set the row or column level of the style (if builtin 1||2)
return field_2_name_length; */
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 * get the style's name
* @return name of the style * @return name of the style
* @see #getNameLength()
*/ */
public String getName() {
public String getName()
{
return field_4_name; return field_4_name;
} }
// end user defined public String toString() {
// only for buildin records StringBuffer sb = new StringBuffer();
/** sb.append("[STYLE]\n");
* if this is a builtin style get the number of the built in style sb.append(" .xf_index_raw =").append(HexDump.shortToHex(field_1_xf_index)).append("\n");
* @return builtin style number (0-7) 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");
public byte getBuiltin() sb.append(" .outline_level=").append(HexDump.byteToHex(field_3_outline_style_level)).append("\n");
{ } else {
return field_2_builtin_style; sb.append(" .name =").append(getName()).append("\n");
}
sb.append("[/STYLE]\n");
return sb.toString();
} }
/**
* get the row or column level of the style (if builtin 1||2)
*/
public byte getOutlineStyleLevel() private int getDataSize() {
{ if (isBuiltin()) {
return field_3_outline_style_level; 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;
} }
// end builtin records public int serialize(int offset, byte [] data) {
public String toString() int dataSize = getDataSize();
{
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); LittleEndian.putShort(data, 0 + offset, sid);
if (getType() == STYLE_BUILT_IN) LittleEndian.putUShort(data, 2 + offset, dataSize);
{
LittleEndian.putShort(data, 2 + offset, LittleEndian.putUShort(data, 4 + offset, field_1_xf_index);
(( short ) 0x04)); // 4 bytes (8 total) if (isBuiltin()) {
} LittleEndian.putByte(data, 6 + offset, field_2_builtin_style);
else LittleEndian.putByte(data, 7 + offset, field_3_outline_style_level);
{ } else {
LittleEndian.putShort(data, 2 + offset, LittleEndian.putUShort(data, 6 + offset, field_4_name.length());
(( short ) (getRecordSize()-4))); LittleEndian.putByte(data, 8 + offset, field_3_string_options);
}
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); StringUtil.putCompressedUnicode(getName(), data, 9 + offset);
} }
return getRecordSize(); return 4+dataSize;
} }
public int getRecordSize() public int getRecordSize() {
{ return 4 + getDataSize();
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() public short getSid()

View File

@ -27,6 +27,7 @@ import org.apache.poi.hssf.record.formula.Ref3DPtg;
import org.apache.poi.hssf.record.formula.RefPtg; import org.apache.poi.hssf.record.formula.RefPtg;
import org.apache.poi.util.HexDump; import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.LittleEndianByteArrayInputStream;
import org.apache.poi.util.LittleEndianInput; import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndianOutput; import org.apache.poi.util.LittleEndianOutput;
import org.apache.poi.util.LittleEndianOutputStream; import org.apache.poi.util.LittleEndianOutputStream;
@ -209,12 +210,7 @@ public abstract class SubRecord {
out.writeShort(_unknownShort13); out.writeShort(_unknownShort13);
} }
private static Ptg readRefPtg(byte[] formulaRawBytes) { private static Ptg readRefPtg(byte[] formulaRawBytes) {
byte[] data = new byte[formulaRawBytes.length + 4]; LittleEndianInput in = new LittleEndianByteArrayInputStream(formulaRawBytes);
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(); byte ptgSid = in.readByte();
switch(ptgSid) { switch(ptgSid) {
case AreaPtg.sid: return new AreaPtg(in); case AreaPtg.sid: return new AreaPtg(in);

View File

@ -84,9 +84,11 @@ public final class SupBookRecord extends Record {
* @param offset of the record's data (provided a big array of the file) * @param offset of the record's data (provided a big array of the file)
*/ */
public SupBookRecord(RecordInputStream in) { public SupBookRecord(RecordInputStream in) {
int recLen = in.remaining();
field_1_number_of_sheets = in.readShort(); field_1_number_of_sheets = in.readShort();
if(in.getLength() > SMALL_RECORD_SIZE) { if(recLen > SMALL_RECORD_SIZE) {
// 5.38.1 External References // 5.38.1 External References
_isAddInFunctions = false; _isAddInFunctions = false;

View File

@ -25,6 +25,8 @@ import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory; import org.apache.poi.util.BitFieldFactory;
import org.apache.poi.util.HexDump; import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian; 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 * 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); _text = new HSSFRichTextString(text);
if (field_7_formattingDataLength > 0) { if (field_7_formattingDataLength > 0) {
if (in.isContinueNext() && in.remaining() == 0) {
in.nextRecord();
processFontRuns(in, _text, field_7_formattingDataLength); processFontRuns(in, _text, field_7_formattingDataLength);
} else {
throw new RecordFormatException(
"Expected Continue Record to hold font runs for TextObjectRecord");
}
} }
} }
@ -154,10 +150,6 @@ public final class TextObjectRecord extends Record {
throw new RecordFormatException("Bad format run data length " + formattingRunDataLength 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; int nRuns = formattingRunDataLength / FORMAT_RUN_ENCODED_SIZE;
for (int i = 0; i < nRuns; i++) { for (int i = 0; i < nRuns; i++) {
short index = in.readShort(); short index = in.readShort();
@ -190,36 +182,31 @@ public final class TextObjectRecord extends Record {
private int serializeTXORecord(int offset, byte[] data) { private int serializeTXORecord(int offset, byte[] data) {
int dataSize = getDataSize(); int dataSize = getDataSize();
int recSize = dataSize+4;
LittleEndianOutput out = new LittleEndianByteArrayOutputStream(data, offset, recSize);
LittleEndian.putUShort(data, 0 + offset, TextObjectRecord.sid); out.writeShort(TextObjectRecord.sid);
LittleEndian.putUShort(data, 2 + offset, dataSize); out.writeShort(dataSize);
out.writeShort(field_1_options);
LittleEndian.putUShort(data, 4 + offset, field_1_options); out.writeShort(field_2_textOrientation);
LittleEndian.putUShort(data, 6 + offset, field_2_textOrientation); out.writeShort(field_3_reserved4);
LittleEndian.putUShort(data, 8 + offset, field_3_reserved4); out.writeShort(field_4_reserved5);
LittleEndian.putUShort(data, 10 + offset, field_4_reserved5); out.writeShort(field_5_reserved6);
LittleEndian.putUShort(data, 12 + offset, field_5_reserved6); out.writeShort(_text.length());
LittleEndian.putUShort(data, 14 + offset, _text.length()); out.writeShort(getFormattingDataLength());
LittleEndian.putUShort(data, 16 + offset, getFormattingDataLength()); out.writeInt(field_8_reserved7);
LittleEndian.putInt(data, 18 + offset, field_8_reserved7);
if (_linkRefPtg != null) { if (_linkRefPtg != null) {
int pos = offset+22;
int formulaSize = _linkRefPtg.getSize(); int formulaSize = _linkRefPtg.getSize();
LittleEndian.putUShort(data, pos, formulaSize); out.writeShort(formulaSize);
pos += LittleEndian.SHORT_SIZE; out.writeInt(_unknownPreFormulaInt);
LittleEndian.putInt(data, pos, _unknownPreFormulaInt); _linkRefPtg.write(out);
pos += LittleEndian.INT_SIZE;
_linkRefPtg.writeBytes(data, pos);
pos += formulaSize;
if (_unknownPostFormulaByte != null) { if (_unknownPostFormulaByte != null) {
LittleEndian.putByte(data, pos, _unknownPostFormulaByte.byteValue()); out.writeByte(_unknownPostFormulaByte.byteValue());
pos += LittleEndian.BYTE_SIZE;
} }
} }
return recSize;
return 4 + dataSize;
} }
private int serializeTrailingRecords(int offset, byte[] data) { private int serializeTrailingRecords(int offset, byte[] data) {

View File

@ -36,13 +36,8 @@ import java.util.Collections;
* @author Andrew C. Oliver * @author Andrew C. Oliver
* @author Marc Johnson (mjohnson at apache dot org) * @author Marc Johnson (mjohnson at apache dot org)
* @author Glen Stampoultzis (glens at apache.org) * @author Glen Stampoultzis (glens at apache.org)
* @version 2.0-pre
*/ */
public final class UnicodeString implements Comparable {
public class UnicodeString
implements Comparable
{
public final static short sid = 0xFFF;
private short field_1_charCount; // = 0; private short field_1_charCount; // = 0;
private byte field_2_optionflags; // = 0; private byte field_2_optionflags; // = 0;
private String field_3_string; // = null; private String field_3_string; // = null;
@ -53,8 +48,8 @@ public class UnicodeString
private static final BitField richText = BitFieldFactory.getInstance(0x8); private static final BitField richText = BitFieldFactory.getInstance(0x8);
public static class FormatRun implements Comparable { public static class FormatRun implements Comparable {
private short character; short character;
private short fontIndex; short fontIndex;
public FormatRun(short character, short fontIndex) { public FormatRun(short character, short fontIndex) {
this.character = character; this.character = character;
@ -102,15 +97,6 @@ public class UnicodeString
setString(str); 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() public int hashCode()
@ -142,9 +128,9 @@ public class UnicodeString
&& field_3_string.equals(other.field_3_string)); && field_3_string.equals(other.field_3_string));
if (!eq) return false; 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)) 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; return true;
if (((field_4_format_runs == null) && (other.field_4_format_runs != null)) || if (((field_4_format_runs == null) && (other.field_4_format_runs != null)) ||
(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 * @param in the RecordInputstream to read the record from
*/ */
protected void fillFields(RecordInputStream in) public UnicodeString(RecordInputStream in) {
{
field_1_charCount = in.readShort(); field_1_charCount = in.readShort();
field_2_optionflags = in.readByte(); field_2_optionflags = in.readByte();
@ -206,34 +192,12 @@ public class UnicodeString
extensionLength = in.readInt(); 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); 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) { if (isCompressed) {
char ch = (char)in.readUByte(); // avoid sex field_3_string = in.readCompressedUnicode(field_1_charCount);
tmpString.append(ch);
} else { } else {
char ch = (char) in.readShort(); field_3_string = in.readUnicodeLEString(field_1_charCount);
tmpString.append(ch);
} }
stringCharCount --;
}
field_3_string = tmpString.toString();
//Turn back on autocontinuation
in.setAutoContinue(true);
if (isRichText() && (runCount > 0)) { if (isRichText() && (runCount > 0)) {
@ -305,13 +269,8 @@ public class UnicodeString
} }
/** /**
* get the actual string this contains as a java String object * @return the actual string this contains as a java String object
*
*
* @return String
*
*/ */
public String getString() public String getString()
{ {
return field_3_string; return field_3_string;
@ -341,7 +300,7 @@ public class UnicodeString
} }
} }
if (useUTF16) if (useUTF16)
//Set the uncomressed bit //Set the uncompressed bit
field_2_optionflags = highByte.setByte(field_2_optionflags); field_2_optionflags = highByte.setByte(field_2_optionflags);
else field_2_optionflags = highByte.clearByte(field_2_optionflags); else field_2_optionflags = highByte.clearByte(field_2_optionflags);
} }
@ -497,8 +456,8 @@ public class UnicodeString
LittleEndian.putShort(data, offset, ContinueRecord.sid); LittleEndian.putShort(data, offset, ContinueRecord.sid);
offset+=2; offset+=2;
//Record the location of the last continue legnth position, but dont write //Record the location of the last continue length position, but don't write
//anything there yet (since we dont know what it will be!) //anything there yet (since we don't know what it will be!)
stats.lastLengthPos = offset; stats.lastLengthPos = offset;
offset += 2; offset += 2;
@ -514,7 +473,6 @@ public class UnicodeString
//Basic string overhead //Basic string overhead
pos = writeContinueIfRequired(stats, 3, pos, data); pos = writeContinueIfRequired(stats, 3, pos, data);
// byte[] retval = new byte[ 3 + (getString().length() * charsize)];
LittleEndian.putShort(data, pos, getCharCount()); LittleEndian.putShort(data, pos, getCharCount());
pos += 2; pos += 2;
data[ pos ] = getOptionFlags(); 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 //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. //the byte to start with that represents the first byte of the continue record.
if (strSize > stats.remainingSize) { 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 //we need an extra byte after the continue record ie we didnt finish
//writing out the string the 1st time through //writing out the string the 1st time through
//But hang on, how many continue records did we span? What if this is //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. //a REALLY long string. We need to work this all out.
int ammountThatCantFit = strSize; int amountThatCantFit = strSize;
int strPos = 0; int strPos = 0;
while (ammountThatCantFit > 0) { while (amountThatCantFit > 0) {
int ammountWritten = Math.min(stats.remainingSize, ammountThatCantFit); int amountWritten = Math.min(stats.remainingSize, amountThatCantFit);
//Make sure that the ammount that cant fit takes into account //Make sure that the amount that can't fit takes into account
//whether we are writing double byte unicode //whether we are writing double byte unicode
if (isUncompressedUnicode()) { if (isUncompressedUnicode()) {
//We have the '-1' here because whether this is the first record or //We have the '-1' here because whether this is the first record or
//subsequent continue records, there is always the case that the //subsequent continue records, there is always the case that the
//number of bytes in a string on doube byte boundaries is actually odd. //number of bytes in a string on double byte boundaries is actually odd.
if ( ( (ammountWritten ) % 2) == 1) if ( ( (amountWritten ) % 2) == 1)
ammountWritten--; amountWritten--;
} }
System.arraycopy(strBytes, strPos, data, pos, ammountWritten); System.arraycopy(strBytes, strPos, data, pos, amountWritten);
pos += ammountWritten; pos += amountWritten;
strPos += ammountWritten; strPos += amountWritten;
stats.recordSize += ammountWritten; stats.recordSize += amountWritten;
stats.remainingSize -= ammountWritten; stats.remainingSize -= amountWritten;
//Ok lets subtract what we can write //Ok lets subtract what we can write
ammountThatCantFit -= ammountWritten; amountThatCantFit -= amountWritten;
//Each iteration of this while loop is another continue record, unless //Each iteration of this while loop is another continue record, unless
//everything now fits. //everything now fits.
if (ammountThatCantFit > 0) { if (amountThatCantFit > 0) {
//We know that a continue WILL be requied, but use this common method //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 //The first byte after a continue mid string is the extra byte to
//indicate if this run is compressed or not. //indicate if this run is compressed or not.
@ -686,7 +644,7 @@ public class UnicodeString
return highByte.isSet(getOptionFlags()); 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. * 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) public int compareTo(Object obj)
{ {
UnicodeString str = ( UnicodeString ) 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 //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)) if ((field_5_ext_rst == null) && (str.field_5_ext_rst == null))
return 0; return 0;
if ((field_5_ext_rst == null) && (str.field_5_ext_rst != null)) if ((field_5_ext_rst == null) && (str.field_5_ext_rst != null))

View File

@ -1,4 +1,3 @@
/* ==================================================================== /* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
@ -16,110 +15,129 @@
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.hssf.record; package org.apache.poi.hssf.record;
import java.util.Arrays;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.StringUtil; import org.apache.poi.util.StringUtil;
/** /**
* Title: Write Access Record<P> * Title: Write Access Record (0x005C)<p/>
* 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 * Description: Stores the username of that who owns the spreadsheet generator (on unix the user's
* you installed the thing)<P> * 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)<P> * <p/>
* REFERENCE: PG 424 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)
* <p/>
*
* @author Andrew C. Oliver (acoliver at apache dot org) * @author Andrew C. Oliver (acoliver at apache dot org)
* @version 2.0-pre
*/ */
public final class WriteAccessRecord extends Record {
public class WriteAccessRecord private static final byte PAD_CHAR = (byte) ' ';
extends Record public final static short sid = 0x005C;
{ private static final int DATA_SIZE = 112;
public final static short sid = 0x5c;
private String field_1_username; private String field_1_username;
/** this record is always padded to a constant length */
private byte[] padding;
public WriteAccessRecord() public WriteAccessRecord() {
{ setUsername("");
padding = new byte[DATA_SIZE - 3];
} }
public WriteAccessRecord(RecordInputStream in) public WriteAccessRecord(RecordInputStream in) {
{ if (in.remaining() > DATA_SIZE) {
byte[] data = in.readRemainder(); throw new RecordFormatException("Expected data size (" + DATA_SIZE + ") but got ("
//The string is always 112 characters (padded with spaces), therefore + in.remaining() + ")");
//this record can not be continued. }
// 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 int nChars = in.readUShort();
//header doesnt provide a correct size indication.??? int is16BitFlag = in.readUByte();
//But the header is present, so we need to skip over it. int expectedPadSize = DATA_SIZE - 3;
//Odd, Odd, Odd ;-) if ((is16BitFlag & 0x01) == 0x00) {
field_1_username = StringUtil.getFromCompressedUnicode(data, 3, data.length - 3); 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. * 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") * @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);
public void setUsername(String username)
{
field_1_username = username; field_1_username = username;
} }
/** /**
* get the username for the user that created the report. HSSF uses the logged in user. On * get the username for the user that created the report. HSSF uses the
* natively created M$ Excel sheet this would be the name you typed in when you installed it * logged in user. On natively created M$ Excel sheet this would be the name
* in most cases. * you typed in when you installed it in most cases.
*
* @return username of the user who is logged in (probably "tomcat" or "apache") * @return username of the user who is logged in (probably "tomcat" or "apache")
*/ */
public String getUsername() {
public String getUsername()
{
return field_1_username; return field_1_username;
} }
public String toString() public String toString() {
{
StringBuffer buffer = new StringBuffer(); StringBuffer buffer = new StringBuffer();
buffer.append("[WRITEACCESS]\n"); buffer.append("[WRITEACCESS]\n");
buffer.append(" .name = ") buffer.append(" .name = ").append(field_1_username.toString()).append("\n");
.append(field_1_username.toString()).append("\n");
buffer.append("[/WRITEACCESS]\n"); buffer.append("[/WRITEACCESS]\n");
return buffer.toString(); return buffer.toString();
} }
public int serialize(int offset, byte [] data) public int serialize(int offset, byte[] data) {
{
String username = getUsername(); String username = getUsername();
StringBuffer temp = new StringBuffer(0x70 - (0x3)); boolean is16bit = StringUtil.hasMultibyte(username);
temp.append(username); LittleEndian.putUShort(data, 0 + offset, sid);
while (temp.length() < 0x70 - 0x3) LittleEndian.putUShort(data, 2 + offset, DATA_SIZE);
{ LittleEndian.putUShort(data, 4 + offset, username.length());
temp.append( LittleEndian.putByte(data, 6 + offset, is16bit ? 0x01 : 0x00);
" "); // (70 = fixed lenght -3 = the overhead bits of unicode string) int pos = offset + 7;
if (is16bit) {
StringUtil.putUnicodeLE(username, data, pos);
pos += username.length() * 2;
} else {
StringUtil.putCompressedUnicode(username, data, pos);
pos += username.length();
} }
username = temp.toString(); System.arraycopy(padding, 0, data, pos, padding.length);
UnicodeString str = new UnicodeString(username); return 4 + DATA_SIZE;
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() public int getRecordSize() {
{ return 4 + DATA_SIZE;
return 116;
} }
public short getSid() public short getSid() {
{
return sid; return sid;
} }
} }

View File

@ -17,10 +17,11 @@
package org.apache.poi.hssf.record.constant; 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;
import org.apache.poi.hssf.record.UnicodeString.UnicodeRecordStats; 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. * 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 // 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]; Object[] result = new Object[nValues];
for (int i = 0; i < result.length; i++) { for (int i = 0; i < result.length; i++) {
result[i] = readAConstantValue(in); result[i] = readAConstantValue(in);
@ -55,7 +56,7 @@ public final class ConstantValueParser {
return result; return result;
} }
private static Object readAConstantValue(RecordInputStream in) { private static Object readAConstantValue(LittleEndianInput in) {
byte grbit = in.readByte(); byte grbit = in.readByte();
switch(grbit) { switch(grbit) {
case TYPE_EMPTY: case TYPE_EMPTY:
@ -64,7 +65,7 @@ public final class ConstantValueParser {
case TYPE_NUMBER: case TYPE_NUMBER:
return new Double(in.readDouble()); return new Double(in.readDouble());
case TYPE_STRING: case TYPE_STRING:
return in.readUnicodeString(); return new UnicodeString(StringUtil.readUnicodeString(in));
case TYPE_BOOLEAN: case TYPE_BOOLEAN:
return readBoolean(in); return readBoolean(in);
case TYPE_ERROR_CODE: case TYPE_ERROR_CODE:
@ -77,7 +78,7 @@ public final class ConstantValueParser {
throw new RuntimeException("Unknown grbit value (" + grbit + ")"); 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' byte val = (byte)in.readLong(); // 7 bytes 'not used'
switch(val) { switch(val) {
case FALSE_ENCODING: case FALSE_ENCODING:
@ -116,46 +117,43 @@ public final class ConstantValueParser {
return urs.recordSize; return urs.recordSize;
} }
public static void encode(byte[] data, int offset, Object[] values) { public static void encode(LittleEndianOutput out, Object[] values) {
int currentOffset = offset;
for (int i = 0; i < values.length; i++) { 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) { if (value == EMPTY_REPRESENTATION) {
LittleEndian.putByte(data, offset, TYPE_EMPTY); out.writeByte(TYPE_EMPTY);
LittleEndian.putLong(data, offset+1, 0L); out.writeLong(0L);
return 9; return;
} }
if (value instanceof Boolean) { if (value instanceof Boolean) {
Boolean bVal = ((Boolean)value); Boolean bVal = ((Boolean)value);
LittleEndian.putByte(data, offset, TYPE_BOOLEAN); out.writeByte(TYPE_BOOLEAN);
long longVal = bVal.booleanValue() ? 1L : 0L; long longVal = bVal.booleanValue() ? 1L : 0L;
LittleEndian.putLong(data, offset+1, longVal); out.writeLong(longVal);
return 9; return;
} }
if (value instanceof Double) { if (value instanceof Double) {
Double dVal = (Double) value; Double dVal = (Double) value;
LittleEndian.putByte(data, offset, TYPE_NUMBER); out.writeByte(TYPE_NUMBER);
LittleEndian.putDouble(data, offset+1, dVal.doubleValue()); out.writeDouble(dVal.doubleValue());
return 9; return;
} }
if (value instanceof UnicodeString) { if (value instanceof UnicodeString) {
UnicodeString usVal = (UnicodeString) value; UnicodeString usVal = (UnicodeString) value;
LittleEndian.putByte(data, offset, TYPE_STRING); out.writeByte(TYPE_STRING);
UnicodeRecordStats urs = new UnicodeRecordStats(); StringUtil.writeUnicodeString(out, usVal.getString());
usVal.serialize(urs, offset +1, data); return;
return 1 + urs.recordSize;
} }
if (value instanceof ErrorConstant) { if (value instanceof ErrorConstant) {
ErrorConstant ecVal = (ErrorConstant) value; ErrorConstant ecVal = (ErrorConstant) value;
LittleEndian.putByte(data, offset, TYPE_ERROR_CODE); out.writeByte(TYPE_ERROR_CODE);
LittleEndian.putUShort(data, offset+1, ecVal.getErrorCode()); long longVal = ecVal.getErrorCode();
LittleEndian.putUShort(data, offset+3, 0); out.writeLong(longVal);
LittleEndian.putInt(data, offset+5, 0); return;
return 9;
} }
throw new IllegalStateException("Unexpected value type (" + value.getClass().getName() + "'"); throw new IllegalStateException("Unexpected value type (" + value.getClass().getName() + "'");

View File

@ -98,7 +98,6 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
buf.append(")"); buf.append(")");
} }
public abstract void writeBytes(byte[] array, int offset);
public abstract int getSize(); public abstract int getSize();

View File

@ -17,8 +17,8 @@
package org.apache.poi.hssf.record.formula; package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndianOutput;
/** /**
* Common superclass of 2-D area refs * Common superclass of 2-D area refs
@ -29,24 +29,30 @@ public abstract class Area2DPtgBase extends AreaPtgBase {
protected Area2DPtgBase(int firstRow, int lastRow, int firstColumn, int lastColumn, boolean firstRowRelative, boolean lastRowRelative, boolean firstColRelative, boolean lastColRelative) { 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); super(firstRow, lastRow, firstColumn, lastColumn, firstRowRelative, lastRowRelative, firstColRelative, lastColRelative);
} }
protected Area2DPtgBase(RecordInputStream in) {
protected Area2DPtgBase(LittleEndianInput in) {
readCoordinates(in); readCoordinates(in);
} }
protected abstract byte getSid(); protected abstract byte getSid();
public final void writeBytes(byte [] array, int offset) { public final void write(LittleEndianOutput out) {
LittleEndian.putByte(array, offset+0, getSid() + getPtgClass()); out.writeByte(getSid() + getPtgClass());
writeCoordinates(array, offset+1); writeCoordinates(out);
} }
public Area2DPtgBase(String arearef) { public Area2DPtgBase(String arearef) {
super(arearef); super(arearef);
} }
public final int getSize() { public final int getSize() {
return SIZE; return SIZE;
} }
public final String toFormulaString() { public final String toFormulaString() {
return formatReferenceAsString(); return formatReferenceAsString();
} }
public final String toString() { public final String toString() {
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
sb.append(getClass().getName()); sb.append(getClass().getName());

View File

@ -17,11 +17,11 @@
package org.apache.poi.hssf.record.formula; 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.ExternSheetReferenceToken;
import org.apache.poi.ss.formula.FormulaRenderingWorkbook; import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
import org.apache.poi.ss.formula.WorkbookDependentFormula; 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)<P> * Title: Area 3D Ptg - 3D reference (Sheet + Area)<P>
@ -44,7 +44,7 @@ public final class Area3DPtg extends AreaPtgBase implements WorkbookDependentFor
setExternSheetIndex( externIdx ); setExternSheetIndex( externIdx );
} }
public Area3DPtg(RecordInputStream in) { public Area3DPtg(LittleEndianInput in) {
field_1_index_extern_sheet = in.readShort(); field_1_index_extern_sheet = in.readShort();
readCoordinates(in); readCoordinates(in);
} }
@ -67,10 +67,10 @@ public final class Area3DPtg extends AreaPtgBase implements WorkbookDependentFor
return sb.toString(); return sb.toString();
} }
public void writeBytes(byte[] array, int offset) { public void write(LittleEndianOutput out) {
LittleEndian.putByte(array, offset + 0, sid + getPtgClass()); out.writeByte(sid + getPtgClass());
LittleEndian.putUShort(array, 1 + offset, field_1_index_extern_sheet); out.writeShort(field_1_index_extern_sheet);
writeCoordinates(array, offset+3); writeCoordinates(out);
} }
public int getSize() { public int getSize() {

View File

@ -17,9 +17,9 @@
package org.apache.poi.hssf.record.formula; 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.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. * AreaErr - handles deleted cell area references.
@ -36,16 +36,16 @@ public final class AreaErrPtg extends OperandPtg {
unused2 = 0; unused2 = 0;
} }
public AreaErrPtg(RecordInputStream in) { public AreaErrPtg(LittleEndianInput in) {
// 8 bytes unused: // 8 bytes unused:
unused1 = in.readInt(); unused1 = in.readInt();
unused2 = in.readInt(); unused2 = in.readInt();
} }
public void writeBytes(byte[] array, int offset) { public void write(LittleEndianOutput out) {
LittleEndian.putByte(array, offset + 0, sid + getPtgClass()); out.writeByte(sid + getPtgClass());
LittleEndian.putInt(array, offset + 1, unused1); out.writeInt(unused1);
LittleEndian.putInt(array, offset + 5, unused2); out.writeInt(unused2);
} }
public String toFormulaString() { public String toFormulaString() {

View File

@ -17,7 +17,7 @@
package org.apache.poi.hssf.record.formula; 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. * 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 class AreaNPtg extends Area2DPtgBase {
public final static short sid = 0x2D; public final static short sid = 0x2D;
public AreaNPtg(RecordInputStream in) { public AreaNPtg(LittleEndianInput in) {
super(in); super(in);
} }

View File

@ -17,7 +17,7 @@
package org.apache.poi.hssf.record.formula; 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. * 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) { 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); super(firstRow, lastRow, firstColumn, lastColumn, firstRowRelative, lastRowRelative, firstColRelative, lastColRelative);
} }
public AreaPtg(RecordInputStream in) { public AreaPtg(LittleEndianInput in) {
super(in); super(in);
} }
public AreaPtg(String arearef) { public AreaPtg(String arearef) {

View File

@ -17,12 +17,12 @@
package org.apache.poi.hssf.record.formula; 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.AreaReference;
import org.apache.poi.ss.util.CellReference; import org.apache.poi.ss.util.CellReference;
import org.apache.poi.util.BitField; import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory; 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. * Specifies a rectangular area of cells A1:A4 for instance.
@ -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_1_first_row = in.readUShort();
field_2_last_row = in.readUShort(); field_2_last_row = in.readUShort();
field_3_first_column = in.readUShort(); field_3_first_column = in.readUShort();
field_4_last_column = in.readUShort(); field_4_last_column = in.readUShort();
} }
protected final void writeCoordinates(byte[] array, int offset) { protected final void writeCoordinates(LittleEndianOutput out) {
LittleEndian.putUShort(array, offset + 0, field_1_first_row); out.writeShort(field_1_first_row);
LittleEndian.putUShort(array, offset + 2, field_2_last_row); out.writeShort(field_2_last_row);
LittleEndian.putUShort(array, offset + 4, field_3_first_column); out.writeShort(field_3_first_column);
LittleEndian.putUShort(array, offset + 6, field_4_last_column); out.writeShort(field_4_last_column);
} }
/** /**

View File

@ -17,11 +17,11 @@
package org.apache.poi.hssf.record.formula; 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.UnicodeString;
import org.apache.poi.hssf.record.constant.ConstantValueParser; import org.apache.poi.hssf.record.constant.ConstantValueParser;
import org.apache.poi.hssf.record.constant.ErrorConstant; 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 * ArrayPtg - handles arrays
@ -54,7 +54,7 @@ public final class ArrayPtg extends Ptg {
private short token_2_rows; private short token_2_rows;
private Object[] token_3_arrayValues; private Object[] token_3_arrayValues;
public ArrayPtg(RecordInputStream in) { public ArrayPtg(LittleEndianInput in) {
field_1_reserved = new byte[RESERVED_FIELD_LEN]; field_1_reserved = new byte[RESERVED_FIELD_LEN];
// TODO - add readFully method to RecordInputStream // TODO - add readFully method to RecordInputStream
for(int i=0; i< RESERVED_FIELD_LEN; i++) { 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. * AFTER the last Ptg in the expression.
* See page 304-305 of Excel97-2007BinaryFileFormat(xls)Specification.pdf * See page 304-305 of Excel97-2007BinaryFileFormat(xls)Specification.pdf
*/ */
public void readTokenValues(RecordInputStream in) { public void readTokenValues(LittleEndianInput in) {
int nColumns = in.readUByte(); int nColumns = in.readUByte();
short nRows = in.readShort(); short nRows = in.readShort();
//The token_1_columns and token_2_rows do not follow the documentation. //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) { if (token_3_arrayValues == null) {
sb.append(" #values#uninitialised#\n"); sb.append(" #values#uninitialised#\n");
} else { } else {
sb.append(" ").append(formatAsString()); sb.append(" ").append(toFormulaString());
} }
return sb.toString(); return sb.toString();
} }
@ -153,17 +153,16 @@ public final class ArrayPtg extends Ptg {
return rowIx * token_1_columns + colIx; return rowIx * token_1_columns + colIx;
} }
public void writeBytes(byte[] data, int offset) { public void write(LittleEndianOutput out) {
out.writeByte(sid + getPtgClass());
LittleEndian.putByte(data, offset + 0, sid + getPtgClass()); out.write(field_1_reserved);
System.arraycopy(field_1_reserved, 0, data, offset+1, RESERVED_FIELD_LEN);
} }
public int writeTokenValueBytes(byte[] data, int offset) { public int writeTokenValueBytes(LittleEndianOutput out) {
LittleEndian.putByte(data, offset + 0, token_1_columns-1); out.writeByte(token_1_columns-1);
LittleEndian.putUShort(data, offset + 1, token_2_rows-1); out.writeShort(token_2_rows-1);
ConstantValueParser.encode(data, offset + 3, token_3_arrayValues); ConstantValueParser.encode(out, token_3_arrayValues);
return 3 + ConstantValueParser.getEncodedSize(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); + ConstantValueParser.getEncodedSize(token_3_arrayValues);
} }
public String formatAsString() { // TODO - fold into toFormulaString public String toFormulaString() {
StringBuffer b = new StringBuffer(); StringBuffer b = new StringBuffer();
b.append("{"); b.append("{");
for (int y=0;y<getRowCount();y++) { for (int y=0;y<getRowCount();y++) {
@ -201,9 +200,6 @@ public final class ArrayPtg extends Ptg {
b.append("}"); b.append("}");
return b.toString(); return b.toString();
} }
public String toFormulaString() {
return formatAsString();
}
private static String getConstantText(Object o) { private static String getConstantText(Object o) {

View File

@ -17,10 +17,11 @@
package org.apache.poi.hssf.record.formula; 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.BitField;
import org.apache.poi.util.BitFieldFactory; import org.apache.poi.util.BitFieldFactory;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndianOutput;
/** /**
* "Special Attributes" * "Special Attributes"
@ -78,7 +79,7 @@ public final class AttrPtg extends ControlPtg {
_chooseFuncOffset = -1; _chooseFuncOffset = -1;
} }
public AttrPtg(RecordInputStream in) public AttrPtg(LittleEndianInput in)
{ {
field_1_options = in.readByte(); field_1_options = in.readByte();
field_2_data = in.readShort(); field_2_data = in.readShort();
@ -213,19 +214,16 @@ public final class AttrPtg extends ControlPtg {
return sb.toString(); return sb.toString();
} }
public void writeBytes(byte [] array, int offset) public void write(LittleEndianOutput out) {
{ out.writeByte(sid + getPtgClass());
LittleEndian.putByte(array, offset+0, sid); out.writeByte(field_1_options);
LittleEndian.putByte(array, offset+1, field_1_options); out.writeShort(field_2_data);
LittleEndian.putShort(array,offset+2, field_2_data);
int[] jt = _jumpTable; int[] jt = _jumpTable;
if (jt != null) { if (jt != null) {
int joff = offset+4;
for (int i = 0; i < jt.length; i++) { for (int i = 0; i < jt.length; i++) {
LittleEndian.putUShort(array, joff, jt[i]); out.writeShort(jt[i]);
joff+=2;
} }
LittleEndian.putUShort(array, joff, _chooseFuncOffset); out.writeShort(_chooseFuncOffset);
} }
} }

View File

@ -17,21 +17,22 @@
package org.apache.poi.hssf.record.formula; package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndianOutput;
/** /**
* Boolean (boolean) * Boolean (boolean) Stores a (java) boolean value in a formula.
* Stores a (java) boolean value in a formula. *
* @author Paul Krause (pkrause at soundbite dot com) * @author Paul Krause (pkrause at soundbite dot com)
* @author Andrew C. Oliver (acoliver at apache dot org) * @author Andrew C. Oliver (acoliver at apache dot org)
* @author Jason Height (jheight at chariot dot net dot au) * @author Jason Height (jheight at chariot dot net dot au)
*/ */
public final class BoolPtg extends ScalarConstantPtg { public final class BoolPtg extends ScalarConstantPtg {
public final static int SIZE = 2; public final static int SIZE = 2;
public final static byte sid = 0x1d; public final static byte sid = 0x1D;
private final boolean _value; private final boolean _value;
public BoolPtg(RecordInputStream in) { public BoolPtg(LittleEndianInput in) {
_value = (in.readByte() == 1); _value = (in.readByte() == 1);
} }
@ -43,9 +44,9 @@ public final class BoolPtg extends ScalarConstantPtg {
return _value; return _value;
} }
public void writeBytes(byte [] array, int offset) { public void write(LittleEndianOutput out) {
array[ offset + 0 ] = sid; out.writeByte(sid + getPtgClass());
array[ offset + 1 ] = (byte) (_value ? 1 : 0); out.writeByte(_value ? 1 : 0);
} }
public int getSize() { public int getSize() {

View File

@ -17,11 +17,11 @@
package org.apache.poi.hssf.record.formula; 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.HSSFErrorConstants;
import org.apache.poi.ss.formula.WorkbookDependentFormula;
import org.apache.poi.ss.formula.FormulaRenderingWorkbook; 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 Area 3D Ptg - 3D referecnce (Sheet + Area)<P> * Title: Deleted Area 3D Ptg - 3D referecnce (Sheet + Area)<P>
@ -42,7 +42,7 @@ public final class DeletedArea3DPtg extends OperandPtg implements WorkbookDepend
unused2 = 0; unused2 = 0;
} }
public DeletedArea3DPtg(RecordInputStream in) { public DeletedArea3DPtg(LittleEndianInput in) {
field_1_index_extern_sheet = in.readUShort(); field_1_index_extern_sheet = in.readUShort();
unused1 = in.readInt(); unused1 = in.readInt();
unused2 = in.readInt(); unused2 = in.readInt();
@ -60,10 +60,10 @@ public final class DeletedArea3DPtg extends OperandPtg implements WorkbookDepend
public int getSize() { public int getSize() {
return 11; return 11;
} }
public void writeBytes(byte[] data, int offset) { public void write(LittleEndianOutput out) {
LittleEndian.putByte(data, 0 + offset, sid + getPtgClass()); out.writeByte(sid + getPtgClass());
LittleEndian.putUShort(data, 1 + offset, field_1_index_extern_sheet); out.writeShort(field_1_index_extern_sheet);
LittleEndian.putInt(data, 3 + offset, unused1); out.writeInt(unused1);
LittleEndian.putInt(data, 7 + offset, unused2); out.writeInt(unused2);
} }
} }

View File

@ -18,11 +18,11 @@
package org.apache.poi.hssf.record.formula; 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.HSSFErrorConstants;
import org.apache.poi.ss.formula.WorkbookDependentFormula;
import org.apache.poi.ss.formula.FormulaRenderingWorkbook; 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 <P> * Title: Deleted Reference 3D Ptg <P>
@ -37,7 +37,7 @@ public final class DeletedRef3DPtg extends OperandPtg implements WorkbookDepende
private final int unused1; private final int unused1;
/** Creates new DeletedRef3DPtg */ /** Creates new DeletedRef3DPtg */
public DeletedRef3DPtg(RecordInputStream in) { public DeletedRef3DPtg(LittleEndianInput in) {
field_1_index_extern_sheet = in.readUShort(); field_1_index_extern_sheet = in.readUShort();
unused1 = in.readInt(); unused1 = in.readInt();
} }
@ -60,9 +60,9 @@ public final class DeletedRef3DPtg extends OperandPtg implements WorkbookDepende
public int getSize() { public int getSize() {
return 7; return 7;
} }
public void writeBytes(byte[] data, int offset) { public void write(LittleEndianOutput out) {
LittleEndian.putByte(data, 0 + offset, sid + getPtgClass()); out.writeByte(sid + getPtgClass());
LittleEndian.putUShort(data, 1 + offset, field_1_index_extern_sheet); out.writeShort(field_1_index_extern_sheet);
LittleEndian.putInt(data, 3 + offset, unused1); out.writeInt(unused1);
} }
} }

View File

@ -17,8 +17,9 @@
package org.apache.poi.hssf.record.formula; 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.HSSFErrorConstants;
import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndianOutput;
/** /**
* @author Daniel Noll (daniel at nuix dot com dot au) * @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; field_1_error_code = errorCode;
} }
public static ErrPtg read(RecordInputStream in) { public static ErrPtg read(LittleEndianInput in) {
return valueOf(in.readByte()); return valueOf(in.readByte());
} }
public void writeBytes(byte [] array, int offset) public void write(LittleEndianOutput out) {
{ out.writeByte(sid + getPtgClass());
array[offset] = (byte) (sid + getPtgClass()); out.writeByte(field_1_error_code);
array[offset + 1] = (byte)field_1_error_code;
} }
public String toFormulaString() { public String toFormulaString() {

View File

@ -18,8 +18,8 @@
package org.apache.poi.hssf.record.formula; package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordFormatException; import org.apache.poi.hssf.record.RecordFormatException;
import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndianOutput;
/** /**
* *
@ -33,17 +33,16 @@ public final class ExpPtg extends ControlPtg {
private final short field_1_first_row; private final short field_1_first_row;
private final short field_2_first_col; private final short field_2_first_col;
public ExpPtg(RecordInputStream in) public ExpPtg(LittleEndianInput in)
{ {
field_1_first_row = in.readShort(); field_1_first_row = in.readShort();
field_2_first_col = in.readShort(); field_2_first_col = in.readShort();
} }
public void writeBytes(byte [] array, int offset) public void write(LittleEndianOutput out) {
{ out.writeByte(sid + getPtgClass());
array[offset+0]= (byte) (sid); out.writeShort(field_1_first_row);
LittleEndian.putShort(array,offset+1,field_1_first_row); out.writeShort(field_2_first_col);
LittleEndian.putShort(array,offset+3,field_2_first_col);
} }
public int getSize() public int getSize()

View File

@ -16,10 +16,10 @@
==================================================================== */ ==================================================================== */
package org.apache.poi.hssf.record.formula; 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.FunctionMetadata;
import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry; import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndianOutput;
/** /**
* @author aviks * @author aviks
@ -35,7 +35,7 @@ public final class FuncPtg extends AbstractFunctionPtg {
/**Creates new function pointer from a byte array /**Creates new function pointer from a byte array
* usually called while reading an excel file. * usually called while reading an excel file.
*/ */
public FuncPtg(RecordInputStream in) { public FuncPtg(LittleEndianInput in) {
//field_1_num_args = data[ offset + 0 ]; //field_1_num_args = data[ offset + 0 ];
field_2_fnc_index = in.readShort(); field_2_fnc_index = in.readShort();
@ -55,9 +55,9 @@ public final class FuncPtg extends AbstractFunctionPtg {
paramClass = fm.getParameterClassCodes(); paramClass = fm.getParameterClassCodes();
} }
public void writeBytes(byte[] array, int offset) { public void write(LittleEndianOutput out) {
array[offset+0]= (byte) (sid + getPtgClass()); out.writeByte(sid + getPtgClass());
LittleEndian.putShort(array,offset+1,field_2_fnc_index); out.writeShort(field_2_fnc_index);
} }
public int getNumberOfOperands() { public int getNumberOfOperands() {

View File

@ -16,10 +16,10 @@
==================================================================== */ ==================================================================== */
package org.apache.poi.hssf.record.formula; 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.FunctionMetadata;
import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry; 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 /**Creates new function pointer from a byte array
* usually called while reading an excel file. * usually called while reading an excel file.
*/ */
public FuncVarPtg(RecordInputStream in) { public FuncVarPtg(LittleEndianInput in) {
field_1_num_args = in.readByte(); field_1_num_args = in.readByte();
field_2_fnc_index = in.readShort(); field_2_fnc_index = in.readShort();
FunctionMetadata fm = FunctionMetadataRegistry.getFunctionByIndex(field_2_fnc_index); 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) { public void write(LittleEndianOutput out) {
array[offset+0]=(byte) (sid + getPtgClass()); out.writeByte(sid + getPtgClass());
array[offset+1]=field_1_num_args; out.writeByte(field_1_num_args);
LittleEndian.putShort(array,offset+2,field_2_fnc_index); out.writeShort(field_2_fnc_index);
} }
public int getNumberOfOperands() { public int getNumberOfOperands() {

View File

@ -17,12 +17,13 @@
package org.apache.poi.hssf.record.formula; package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndianOutput;
/** /**
* Integer (unsigned short integer) * Integer (unsigned short integer) Stores an unsigned short value (java int) in
* Stores an unsigned short value (java int) in a formula * a formula
*
* @author Andrew C. Oliver (acoliver at apache dot org) * @author Andrew C. Oliver (acoliver at apache dot org)
* @author Jason Height (jheight at chariot dot net dot au) * @author Jason Height (jheight at chariot dot net dot au)
*/ */
@ -33,23 +34,24 @@ public final class IntPtg extends ScalarConstantPtg {
/** /**
* Excel represents integers 0..65535 with the tInt token. * Excel represents integers 0..65535 with the tInt token.
*
* @return <code>true</code> if the specified value is within the range of values * @return <code>true</code> if the specified value is within the range of values
* <tt>IntPtg</tt> can represent. * <tt>IntPtg</tt> can represent.
*/ */
public static boolean isInRange(int i) { public static boolean isInRange(int i) {
return i>=MIN_VALUE && i <=MAX_VALUE; return i >= MIN_VALUE && i <= MAX_VALUE;
} }
public final static int SIZE = 3; public final static int SIZE = 3;
public final static byte sid = 0x1e; public final static byte sid = 0x1e;
private final int field_1_value; private final int field_1_value;
public IntPtg(RecordInputStream in) { public IntPtg(LittleEndianInput in) {
this(in.readUShort()); this(in.readUShort());
} }
public IntPtg(int value) { public IntPtg(int value) {
if(!isInRange(value)) { if (!isInRange(value)) {
throw new IllegalArgumentException("value is out of range: " + value); throw new IllegalArgumentException("value is out of range: " + value);
} }
field_1_value = value; field_1_value = value;
@ -59,9 +61,9 @@ public final class IntPtg extends ScalarConstantPtg {
return field_1_value; return field_1_value;
} }
public void writeBytes(byte [] array, int offset) { public void write(LittleEndianOutput out) {
array[ offset + 0 ] = sid; out.writeByte(sid + getPtgClass());
LittleEndian.putUShort(array, offset + 1, getValue()); out.writeShort(getValue());
} }
public int getSize() { public int getSize() {

View File

@ -17,6 +17,7 @@
package org.apache.poi.hssf.record.formula; package org.apache.poi.hssf.record.formula;
import org.apache.poi.util.LittleEndianOutput;
/** /**
* @author Daniel Noll (daniel at nuix dot com dot au) * @author Daniel Noll (daniel at nuix dot com dot au)
@ -34,37 +35,28 @@ public final class IntersectionPtg extends OperationPtg {
return true; return true;
} }
public int getSize() public int getSize() {
{
return 1; return 1;
} }
public void writeBytes( byte[] array, int offset ) public void write(LittleEndianOutput out) {
{ out.writeByte(sid + getPtgClass());
array[ offset + 0 ] = sid;
} }
/** Implementation of method from Ptg */ public String toFormulaString() {
public String toFormulaString()
{
return " "; return " ";
} }
public String toFormulaString(String[] operands) {
/** implementation of method from OperationsPtg*/
public String toFormulaString(String[] operands)
{
StringBuffer buffer = new StringBuffer(); StringBuffer buffer = new StringBuffer();
buffer.append(operands[ 0 ]); buffer.append(operands[0]);
buffer.append(" "); buffer.append(" ");
buffer.append(operands[ 1 ]); buffer.append(operands[1]);
return buffer.toString(); return buffer.toString();
} }
public int getNumberOfOperands() public int getNumberOfOperands() {
{
return 2; return 2;
} }
} }

View File

@ -17,8 +17,8 @@
package org.apache.poi.hssf.record.formula; package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndianOutput;
/** /**
* @author Daniel Noll (daniel at nuix dot com dot au) * @author Daniel Noll (daniel at nuix dot com dot au)
@ -26,55 +26,32 @@ import org.apache.poi.util.LittleEndian;
public class MemAreaPtg extends OperandPtg { public class MemAreaPtg extends OperandPtg {
public final static short sid = 0x26; public final static short sid = 0x26;
private final static int SIZE = 7; private final static int SIZE = 7;
private int field_1_reserved; private final int field_1_reserved;
private short field_2_subex_len; private final int field_2_subex_len;
/** Creates new MemAreaPtg */ /** Creates new MemAreaPtg */
public MemAreaPtg() public MemAreaPtg(int subexLen) {
{ field_1_reserved = 0;
field_2_subex_len = subexLen;
} }
public MemAreaPtg(RecordInputStream in) public MemAreaPtg(LittleEndianInput in) {
{
field_1_reserved = in.readInt(); field_1_reserved = in.readInt();
field_2_subex_len = in.readShort(); field_2_subex_len = in.readShort();
} }
public void setReserved(int res) public void write(LittleEndianOutput out) {
{ out.writeByte(sid + getPtgClass());
field_1_reserved = res; out.writeInt(field_1_reserved);
out.writeShort(field_2_subex_len);
} }
public int getReserved() public int getSize() {
{
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; return SIZE;
} }
public String toFormulaString() public String toFormulaString() {
{
return ""; // TODO: Not sure how to format this. -- DN return ""; // TODO: Not sure how to format this. -- DN
} }

View File

@ -17,8 +17,8 @@
package org.apache.poi.hssf.record.formula; package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.util.LittleEndianOutput;
/** /**
* *
@ -26,27 +26,32 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook;
* @author Jason Height (jheight at chariot dot net dot au) * @author Jason Height (jheight at chariot dot net dot au)
* @author Daniel Noll (daniel at nuix dot com dot au) * @author Daniel Noll (daniel at nuix dot com dot au)
*/ */
public final class MemErrPtg extends OperandPtg {
public final class MemErrPtg extends MemAreaPtg {
public final static short sid = 0x27; public final static short sid = 0x27;
private final static int SIZE = 7;
private int field_1_reserved;
private short field_2_subex_len;
/** Creates new MemErrPtg */ public MemErrPtg(LittleEndianInput in) {
field_1_reserved = in.readInt();
public MemErrPtg() field_2_subex_len = in.readShort();
{
} }
public MemErrPtg(RecordInputStream in) { public void write(LittleEndianOutput out) {
super(in); out.writeByte(sid + getPtgClass());
out.writeInt(field_1_reserved);
out.writeShort(field_2_subex_len);
} }
public void writeBytes(byte [] array, int offset) { public int getSize() {
super.writeBytes(array, offset); return SIZE;
array[offset] = (byte) (sid + getPtgClass());
} }
public String toFormulaString() public String toFormulaString() {
{
return "ERR#"; return "ERR#";
} }
public byte getDefaultOperandClass() {
return Ptg.CLASS_VALUE;
}
} }

View File

@ -17,8 +17,8 @@
package org.apache.poi.hssf.record.formula; package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndianOutput;
/** /**
* @author Glen Stampoultzis (glens at apache.org) * @author Glen Stampoultzis (glens at apache.org)
@ -28,10 +28,11 @@ public final class MemFuncPtg extends OperandPtg {
public final static byte sid = 0x29; public final static byte sid = 0x29;
private final int field_1_len_ref_subexpression; private final int field_1_len_ref_subexpression;
/**Creates new function pointer from a byte array /**
* usually called while reading an excel file. * Creates new function pointer from a byte array usually called while
* reading an excel file.
*/ */
public MemFuncPtg(RecordInputStream in) { public MemFuncPtg(LittleEndianInput in) {
this(in.readUShort()); this(in.readUShort());
} }
@ -39,34 +40,28 @@ public final class MemFuncPtg extends OperandPtg {
field_1_len_ref_subexpression = subExprLen; field_1_len_ref_subexpression = subExprLen;
} }
public int getSize() public int getSize() {
{
return 3; return 3;
} }
public void writeBytes( byte[] array, int offset ) public void write(LittleEndianOutput out) {
{ out.writeByte(sid + getPtgClass());
array[offset + 0] = sid ; out.writeShort(field_1_len_ref_subexpression);
LittleEndian.putUShort( array, offset + 1, field_1_len_ref_subexpression );
} }
public String toFormulaString() public String toFormulaString() {
{
return ""; return "";
} }
public byte getDefaultOperandClass() public byte getDefaultOperandClass() {
{
return Ptg.CLASS_REF; return Ptg.CLASS_REF;
} }
public int getNumberOfOperands() public int getNumberOfOperands() {
{
return field_1_len_ref_subexpression; return field_1_len_ref_subexpression;
} }
public int getLenRefSubexpression() public int getLenRefSubexpression() {
{
return field_1_len_ref_subexpression; return field_1_len_ref_subexpression;
} }
} }

View File

@ -17,10 +17,13 @@
package org.apache.poi.hssf.record.formula; package org.apache.poi.hssf.record.formula;
import org.apache.poi.util.LittleEndianOutput;
/** /**
* Missing Function Arguments * Missing Function Arguments
* *
* Avik Sengupta &lt;avik at apache.org&gt; * Avik Sengupta &lt;avik at apache.org&gt;
*
* @author Jason Height (jheight at chariot dot net dot au) * @author Jason Height (jheight at chariot dot net dot au)
*/ */
public final class MissingArgPtg extends ScalarConstantPtg { public final class MissingArgPtg extends ScalarConstantPtg {
@ -34,8 +37,8 @@ public final class MissingArgPtg extends ScalarConstantPtg {
// enforce singleton // enforce singleton
} }
public void writeBytes(byte [] array, int offset) { public void write(LittleEndianOutput out) {
array[ offset + 0 ] = sid; out.writeByte(sid + getPtgClass());
} }
public int getSize() { public int getSize() {

View File

@ -17,10 +17,10 @@
package org.apache.poi.hssf.record.formula; 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.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;
/** /**
* *
@ -38,12 +38,12 @@ public final class NamePtg extends OperandPtg implements WorkbookDependentFormul
* @param nameIndex zero-based index to name within workbook * @param nameIndex zero-based index to name within workbook
*/ */
public NamePtg(int nameIndex) { public NamePtg(int nameIndex) {
field_1_label_index = 1+nameIndex; // convert to 1-based field_1_label_index = 1 + nameIndex; // convert to 1-based
} }
/** Creates new NamePtg */ /** Creates new NamePtg */
public NamePtg(RecordInputStream in) { public NamePtg(LittleEndianInput in) {
field_1_label_index = in.readShort(); field_1_label_index = in.readShort();
field_2_zero = in.readShort(); field_2_zero = in.readShort();
} }
@ -52,23 +52,23 @@ public final class NamePtg extends OperandPtg implements WorkbookDependentFormul
* @return zero based index to a defined name record in the LinkTable * @return zero based index to a defined name record in the LinkTable
*/ */
public int getIndex() { public int getIndex() {
return field_1_label_index-1; // convert to zero based return field_1_label_index - 1; // convert to zero based
} }
public void writeBytes(byte [] array, int offset) { public void write(LittleEndianOutput out) {
LittleEndian.putByte(array, offset + 0, sid + getPtgClass()); out.writeByte(sid + getPtgClass());
LittleEndian.putUShort(array, offset + 1, field_1_label_index); out.writeShort(field_1_label_index);
LittleEndian.putUShort(array, offset + 3, field_2_zero); out.writeShort(field_2_zero);
} }
public int getSize() { public int getSize() {
return SIZE; return SIZE;
} }
public String toFormulaString(FormulaRenderingWorkbook book) public String toFormulaString(FormulaRenderingWorkbook book) {
{
return book.getNameText(this); return book.getNameText(this);
} }
public String toFormulaString() { public String toFormulaString() {
throw new RuntimeException("3D references need a workbook to determine formula text"); throw new RuntimeException("3D references need a workbook to determine formula text");
} }

View File

@ -17,10 +17,10 @@
package org.apache.poi.hssf.record.formula; 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.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); this(sheetRefIndex, nameIndex + 1, 0);
} }
public NameXPtg(RecordInputStream in) { public NameXPtg(LittleEndianInput in) {
this(in.readUShort(), in.readUShort(), in.readUShort()); this(in.readUShort(), in.readUShort(), in.readUShort());
} }
public void writeBytes(byte[] array, int offset) { public void write(LittleEndianOutput out) {
LittleEndian.putByte(array, offset + 0, sid + getPtgClass()); out.writeByte(sid + getPtgClass());
LittleEndian.putUShort(array, offset + 1, _sheetRefIndex); out.writeShort(_sheetRefIndex);
LittleEndian.putUShort(array, offset + 3, _nameNumber); out.writeShort(_nameNumber);
LittleEndian.putUShort(array, offset + 5, _reserved); out.writeShort(_reserved);
} }
public int getSize() { public int getSize() {

View File

@ -17,13 +17,13 @@
package org.apache.poi.hssf.record.formula; package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndianOutput;
/** /**
* Number * Number Stores a floating point value in a formula value stored in a 8 byte
* Stores a floating point value in a formula * field using IEEE notation
* value stored in a 8 byte field using IEEE notation *
* @author Avik Sengupta * @author Avik Sengupta
* @author Jason Height (jheight at chariot dot net dot au) * @author Jason Height (jheight at chariot dot net dot au)
*/ */
@ -32,15 +32,16 @@ public final class NumberPtg extends ScalarConstantPtg {
public final static byte sid = 0x1f; public final static byte sid = 0x1f;
private final double field_1_value; private final double field_1_value;
/** Create a NumberPtg from a byte array read from disk */ public NumberPtg(LittleEndianInput in) {
public NumberPtg(RecordInputStream in) {
this(in.readDouble()); 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 * Create a NumberPtg from a string representation of the number Number
* that calls this method. * format is not checked, it is expected to be validated in the parser that
* @param value : String representation of a floating point number * calls this method.
*
* @param value String representation of a floating point number
*/ */
public NumberPtg(String value) { public NumberPtg(String value) {
this(Double.parseDouble(value)); this(Double.parseDouble(value));
@ -54,9 +55,9 @@ public final class NumberPtg extends ScalarConstantPtg {
return field_1_value; return field_1_value;
} }
public void writeBytes(byte [] array, int offset) { public void write(LittleEndianOutput out) {
array[ offset + 0 ] = sid; out.writeByte(sid + getPtgClass());
LittleEndian.putDouble(array, offset + 1, getValue()); out.writeDouble(getValue());
} }
public int getSize() { public int getSize() {
@ -65,7 +66,6 @@ public final class NumberPtg extends ScalarConstantPtg {
public String toFormulaString() { public String toFormulaString() {
// TODO - java's rendering of double values is not quite same as excel's // TODO - java's rendering of double values is not quite same as excel's
// Maybe use HSSFDataFormatter?
return String.valueOf(field_1_value); return String.valueOf(field_1_value);
} }
} }

View File

@ -15,17 +15,18 @@
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.hssf.record.formula; 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 * While formula tokens are stored in RPN order and thus do not need parenthesis
* precedence reasons, Parenthesis tokens ARE written to ensure that user entered * for precedence reasons, Parenthesis tokens ARE written to ensure that user
* parenthesis are displayed as-is on reading back * entered parenthesis are displayed as-is on reading back
*
* Avik Sengupta &lt;lists@aviksengupta.com&gt; Andrew C. Oliver (acoliver at
* apache dot org)
* *
* Avik Sengupta &lt;lists@aviksengupta.com&gt;
* Andrew C. Oliver (acoliver at apache dot org)
* @author Jason Height (jheight at chariot dot net dot au) * @author Jason Height (jheight at chariot dot net dot au)
*/ */
public final class ParenthesisPtg extends ControlPtg { public final class ParenthesisPtg extends ControlPtg {
@ -34,27 +35,24 @@ public final class ParenthesisPtg extends ControlPtg {
public final static byte sid = 0x15; public final static byte sid = 0x15;
public static final ControlPtg instance = new ParenthesisPtg(); public static final ControlPtg instance = new ParenthesisPtg();
private ParenthesisPtg() { private ParenthesisPtg() {
// enforce singleton // enforce singleton
} }
public void writeBytes(byte [] array, int offset) public void write(LittleEndianOutput out) {
{ out.writeByte(sid + getPtgClass());
array[ offset + 0 ] = sid;
} }
public int getSize() public int getSize() {
{
return SIZE; return SIZE;
} }
public String toFormulaString() public String toFormulaString() {
{
return "()"; return "()";
} }
public String toFormulaString(String[] operands) { public String toFormulaString(String[] operands) {
return "("+operands[0]+")"; return "(" + operands[0] + ")";
} }
} }

View File

@ -20,8 +20,8 @@ package org.apache.poi.hssf.record.formula;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.util.LittleEndianByteArrayOutputStream;
import org.apache.poi.util.HexDump; import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndianOutput; import org.apache.poi.util.LittleEndianOutput;
/** /**
@ -48,12 +48,12 @@ public abstract class Ptg implements Cloneable {
* Reads <tt>size</tt> bytes of the input stream, to create an array of <tt>Ptg</tt>s. * Reads <tt>size</tt> bytes of the input stream, to create an array of <tt>Ptg</tt>s.
* Extra data (beyond <tt>size</tt>) may be read if and <tt>ArrayPtg</tt>s are present. * Extra data (beyond <tt>size</tt>) may be read if and <tt>ArrayPtg</tt>s 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); List temp = new ArrayList(4 + size / 2);
int pos = 0; int pos = 0;
List arrayPtgs = null; List arrayPtgs = null;
while (pos < size) { while (pos < size) {
Ptg ptg = Ptg.createPtg( in ); Ptg ptg = Ptg.createPtg(in);
if (ptg instanceof ArrayPtg) { if (ptg instanceof ArrayPtg) {
if (arrayPtgs == null) { if (arrayPtgs == null) {
arrayPtgs = new ArrayList(5); arrayPtgs = new ArrayList(5);
@ -77,7 +77,7 @@ public abstract class Ptg implements Cloneable {
return toPtgArray(temp); return toPtgArray(temp);
} }
public static Ptg createPtg(RecordInputStream in) { public static Ptg createPtg(LittleEndianInput in) {
byte id = in.readByte(); byte id = in.readByte();
if (id < 0x20) { if (id < 0x20) {
@ -97,7 +97,7 @@ public abstract class Ptg implements Cloneable {
return retval; return retval;
} }
private static Ptg createClassifiedPtg(byte id, RecordInputStream in) { private static Ptg createClassifiedPtg(byte id, LittleEndianInput in) {
int baseId = id & 0x1F | 0x20; int baseId = id & 0x1F | 0x20;
@ -126,9 +126,9 @@ public abstract class Ptg implements Cloneable {
Integer.toHexString(id) + " (" + ( int ) id + ")"); Integer.toHexString(id) + " (" + ( int ) id + ")");
} }
private static Ptg createBasePtg(byte id, RecordInputStream in) { private static Ptg createBasePtg(byte id, LittleEndianInput in) {
switch(id) { 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 ExpPtg.sid: return new ExpPtg(in); // 0x01
case TblPtg.sid: return new TblPtg(in); // 0x02 case TblPtg.sid: return new TblPtg(in); // 0x02
case AddPtg.sid: return AddPtg.instance; // 0x03 case AddPtg.sid: return AddPtg.instance; // 0x03
@ -228,32 +228,30 @@ public abstract class Ptg implements Cloneable {
* @return number of bytes written * @return number of bytes written
*/ */
public static int serializePtgs(Ptg[] ptgs, byte[] array, int offset) { public static int serializePtgs(Ptg[] ptgs, byte[] array, int offset) {
int pos = 0; int nTokens = ptgs.length;
int size = ptgs.length;
LittleEndianByteArrayOutputStream out = new LittleEndianByteArrayOutputStream(array, offset);
List arrayPtgs = null; List arrayPtgs = null;
for (int k = 0; k < size; k++) { for (int k = 0; k < nTokens; k++) {
Ptg ptg = ptgs[k]; Ptg ptg = ptgs[k];
ptg.writeBytes(array, pos + offset); ptg.write(out);
if (ptg instanceof ArrayPtg) { if (ptg instanceof ArrayPtg) {
if (arrayPtgs == null) { if (arrayPtgs == null) {
arrayPtgs = new ArrayList(5); arrayPtgs = new ArrayList(5);
} }
arrayPtgs.add(ptg); arrayPtgs.add(ptg);
pos += ArrayPtg.PLAIN_TOKEN_SIZE;
} else {
pos += ptg.getSize();
} }
} }
if (arrayPtgs != null) { if (arrayPtgs != null) {
for (int i=0;i<arrayPtgs.size();i++) { for (int i=0;i<arrayPtgs.size();i++) {
ArrayPtg p = (ArrayPtg)arrayPtgs.get(i); ArrayPtg p = (ArrayPtg)arrayPtgs.get(i);
pos += p.writeTokenValueBytes(array, pos + offset); p.writeTokenValueBytes(out);
} }
} }
return pos; return out.getWriteIndex() - offset;
} }
/** /**
@ -266,38 +264,12 @@ public abstract class Ptg implements Cloneable {
*/ */
// public abstract int getDataSize(); // public abstract int getDataSize();
public final byte[] getBytes() public abstract void write(LittleEndianOutput out);
{
int size = getSize();
byte[] bytes = new byte[ size ];
writeBytes(bytes, 0);
return bytes;
}
/** write this Ptg to a byte array*/
public abstract void writeBytes(byte [] array, int offset);
public void write(LittleEndianOutput out) {
out.write(getBytes()); // TODO - optimise - just a hack for the moment
}
/** /**
* return a string representation of this token alone * return a string representation of this token alone
*/ */
public abstract String toFormulaString(); public abstract String toFormulaString();
/**
* dump a debug representation (hexdump) to a string
*/
public final String toDebugString() {
byte[] ba = new byte[getSize()];
writeBytes(ba,0);
try {
return HexDump.dump(ba,0,0);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/** Overridden toString method to ensure object hash is not printed. /** Overridden toString method to ensure object hash is not printed.
* This helps get rid of gratuitous diffs when comparing two dumps * This helps get rid of gratuitous diffs when comparing two dumps

View File

@ -17,6 +17,8 @@
package org.apache.poi.hssf.record.formula; package org.apache.poi.hssf.record.formula;
import org.apache.poi.util.LittleEndianOutput;
/** /**
* @author Daniel Noll (daniel at nuix dot com dot au) * @author Daniel Noll (daniel at nuix dot com dot au)
@ -40,9 +42,8 @@ public final class RangePtg extends OperationPtg {
return SIZE; return SIZE;
} }
public void writeBytes( byte[] array, int offset ) public void write(LittleEndianOutput out) {
{ out.writeByte(sid + getPtgClass());
array[ offset + 0 ] = sid;
} }
public String toFormulaString() public String toFormulaString()

View File

@ -17,8 +17,8 @@
package org.apache.poi.hssf.record.formula; package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndianOutput;
/** /**
* @author Josh Micich * @author Josh Micich
@ -41,21 +41,25 @@ abstract class Ref2DPtgBase extends RefPtgBase {
setColRelative(isColumnRelative); setColRelative(isColumnRelative);
} }
protected Ref2DPtgBase(RecordInputStream in) { protected Ref2DPtgBase(LittleEndianInput in) {
readCoordinates(in); readCoordinates(in);
} }
public final void writeBytes(byte [] array, int offset) {
LittleEndian.putByte(array, offset+0, getSid() + getPtgClass()); public void write(LittleEndianOutput out) {
writeCoordinates(array, offset+1); out.writeByte(getSid() + getPtgClass());
writeCoordinates(out);
} }
public final String toFormulaString() { public final String toFormulaString() {
return formatReferenceAsString(); return formatReferenceAsString();
} }
protected abstract byte getSid(); protected abstract byte getSid();
public final int getSize() { public final int getSize() {
return SIZE; return SIZE;
} }
public final String toString() { public final String toString() {
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
sb.append(getClass().getName()); sb.append(getClass().getName());

View File

@ -17,12 +17,12 @@
package org.apache.poi.hssf.record.formula; package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.ss.util.CellReference; import org.apache.poi.ss.util.CellReference;
import org.apache.poi.ss.formula.ExternSheetReferenceToken; import org.apache.poi.ss.formula.ExternSheetReferenceToken;
import org.apache.poi.ss.formula.FormulaRenderingWorkbook; import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
import org.apache.poi.ss.formula.WorkbookDependentFormula; 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: Reference 3D Ptg <P> * Title: Reference 3D Ptg <P>
@ -41,7 +41,7 @@ public final class Ref3DPtg extends RefPtgBase implements WorkbookDependentFormu
/** Creates new AreaPtg */ /** Creates new AreaPtg */
public Ref3DPtg() {} public Ref3DPtg() {}
public Ref3DPtg(RecordInputStream in) { public Ref3DPtg(LittleEndianInput in) {
field_1_index_extern_sheet = in.readShort(); field_1_index_extern_sheet = in.readShort();
readCoordinates(in); readCoordinates(in);
} }
@ -66,10 +66,10 @@ public final class Ref3DPtg extends RefPtgBase implements WorkbookDependentFormu
return sb.toString(); return sb.toString();
} }
public void writeBytes(byte [] array, int offset) { public void write(LittleEndianOutput out) {
LittleEndian.putByte(array, 0 + offset, sid + getPtgClass()); out.writeByte(sid + getPtgClass());
LittleEndian.putUShort(array, 1 + offset, getExternSheetIndex()); out.writeShort(getExternSheetIndex());
writeCoordinates(array, offset + 3); writeCoordinates(out);
} }
public int getSize() { public int getSize() {

View File

@ -17,10 +17,9 @@
package org.apache.poi.hssf.record.formula; 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.HSSFErrorConstants;
import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndianOutput;
/** /**
* RefError - handles deleted cell reference * RefError - handles deleted cell reference
@ -35,7 +34,7 @@ public final class RefErrorPtg extends OperandPtg {
public RefErrorPtg() { public RefErrorPtg() {
field_1_reserved = 0; field_1_reserved = 0;
} }
public RefErrorPtg(RecordInputStream in) { public RefErrorPtg(LittleEndianInput in) {
field_1_reserved = in.readInt(); field_1_reserved = in.readInt();
} }
@ -43,9 +42,9 @@ public final class RefErrorPtg extends OperandPtg {
return getClass().getName(); return getClass().getName();
} }
public void writeBytes(byte [] array, int offset) { public void write(LittleEndianOutput out) {
LittleEndian.putByte(array, offset+0, sid + getPtgClass()); out.writeByte(sid + getPtgClass());
LittleEndian.putInt(array,offset+1,field_1_reserved); out.writeInt(field_1_reserved);
} }
public int getSize() public int getSize()

View File

@ -17,7 +17,7 @@
package org.apache.poi.hssf.record.formula; package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.util.LittleEndianInput;
/** /**
* RefNPtg * RefNPtg
@ -26,7 +26,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
public final class RefNPtg extends Ref2DPtgBase { public final class RefNPtg extends Ref2DPtgBase {
public final static byte sid = 0x2C; public final static byte sid = 0x2C;
public RefNPtg(RecordInputStream in) { public RefNPtg(LittleEndianInput in) {
super(in); super(in);
} }

View File

@ -17,7 +17,7 @@
package org.apache.poi.hssf.record.formula; 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) * ReferencePtg - handles references (such as A1, A2, IA4)
@ -39,7 +39,7 @@ public final class RefPtg extends Ref2DPtgBase {
super(row, column, isRowRelative, isColumnRelative); super(row, column, isRowRelative, isColumnRelative);
} }
public RefPtg(RecordInputStream in) { public RefPtg(LittleEndianInput in) {
super(in); super(in);
} }

View File

@ -17,14 +17,15 @@
package org.apache.poi.hssf.record.formula; 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.hssf.util.CellReference;
import org.apache.poi.util.BitField; import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory; 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) * 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) * @author Jason Height (jheight at chariot dot net dot au)
*/ */
@ -34,10 +35,9 @@ public abstract class RefPtgBase extends OperandPtg {
/** The row index - zero based unsigned 16 bit value */ /** The row index - zero based unsigned 16 bit value */
private int field_1_row; private int field_1_row;
/** Field 2 /**
* - lower 8 bits is the zero based unsigned byte column index * Field 2 - lower 8 bits is the zero based unsigned byte column index - bit
* - bit 16 - isRowRelative * 16 - isRowRelative - bit 15 - isColumnRelative
* - bit 15 - isColumnRelative
*/ */
private int field_2_col; private int field_2_col;
private static final BitField rowRelative = BitFieldFactory.getInstance(0x8000); private static final BitField rowRelative = BitFieldFactory.getInstance(0x8000);
@ -45,7 +45,7 @@ public abstract class RefPtgBase extends OperandPtg {
private static final BitField column = BitFieldFactory.getInstance(0x00FF); private static final BitField column = BitFieldFactory.getInstance(0x00FF);
protected RefPtgBase() { protected RefPtgBase() {
//Required for clone methods // Required for clone methods
} }
/** /**
@ -53,7 +53,7 @@ public abstract class RefPtgBase extends OperandPtg {
* numeric fields. * numeric fields.
*/ */
protected RefPtgBase(String cellref) { protected RefPtgBase(String cellref) {
CellReference c= new CellReference(cellref); CellReference c = new CellReference(cellref);
setRow(c.getRow()); setRow(c.getRow());
setColumn(c.getCol()); setColumn(c.getCol());
setColRelative(!c.isColAbsolute()); setColRelative(!c.isColAbsolute());
@ -67,26 +67,27 @@ public abstract class RefPtgBase extends OperandPtg {
setColRelative(isColumnRelative); setColRelative(isColumnRelative);
} }
protected final void readCoordinates(RecordInputStream in) { protected final void readCoordinates(LittleEndianInput in) {
field_1_row = in.readUShort(); field_1_row = in.readUShort();
field_2_col = in.readUShort(); field_2_col = in.readUShort();
} }
protected final void writeCoordinates(byte[] array, int offset) {
LittleEndian.putUShort(array, offset + 0, field_1_row); protected final void writeCoordinates(LittleEndianOutput out) {
LittleEndian.putUShort(array, offset + 2, field_2_col); out.writeShort(field_1_row);
out.writeShort(field_2_col);
} }
public final void setRow(int row) { public final void setRow(int rowIndex) {
if(row < 0 || row >= MAX_ROW_NUMBER) { if (rowIndex < 0 || rowIndex >= MAX_ROW_NUMBER) {
throw new IllegalArgumentException("The row number, when specified as an integer, must be between 0 and " + MAX_ROW_NUMBER); throw new IllegalArgumentException("rowIndex must be between 0 and " + MAX_ROW_NUMBER);
} }
field_1_row = row; field_1_row = rowIndex;
} }
/** /**
* @return the row number as an int, between 0 and 65535 * @return the row number as an int, between 0 and 65535
*/ */
public final int getRow(){ public final int getRow() {
return field_1_row; return field_1_row;
} }
@ -95,7 +96,7 @@ public abstract class RefPtgBase extends OperandPtg {
} }
public final void setRowRelative(boolean rel) { public final void setRowRelative(boolean rel) {
field_2_col=rowRelative.setBoolean(field_2_col,rel); field_2_col = rowRelative.setBoolean(field_2_col, rel);
} }
public final boolean isColRelative() { public final boolean isColRelative() {
@ -103,11 +104,11 @@ public abstract class RefPtgBase extends OperandPtg {
} }
public final void setColRelative(boolean rel) { public final void setColRelative(boolean rel) {
field_2_col=colRelative.setBoolean(field_2_col,rel); field_2_col = colRelative.setBoolean(field_2_col, rel);
} }
public final void setColumn(int col) { public final void setColumn(int col) {
if(col < 0 || col >= 0x100) { if (col < 0 || col >= 0x100) {
throw new IllegalArgumentException("Specified colIx (" + col + ") is out of range"); throw new IllegalArgumentException("Specified colIx (" + col + ") is out of range");
} }
field_2_col = column.setValue(field_2_col, col); field_2_col = column.setValue(field_2_col, col);
@ -116,6 +117,7 @@ public abstract class RefPtgBase extends OperandPtg {
public final int getColumn() { public final int getColumn() {
return column.getValue(field_2_col); return column.getValue(field_2_col);
} }
protected final String formatReferenceAsString() { protected final String formatReferenceAsString() {
// Only make cell references as needed. Memory is an issue // Only make cell references as needed. Memory is an issue
CellReference cr = new CellReference(getRow(), getColumn(), !isRowRelative(), !isColRelative()); CellReference cr = new CellReference(getRow(), getColumn(), !isRowRelative(), !isColRelative());

View File

@ -17,9 +17,8 @@
package org.apache.poi.hssf.record.formula; package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.BitField; import org.apache.poi.util.LittleEndianOutput;
import org.apache.poi.util.BitFieldFactory;
import org.apache.poi.util.StringUtil; import org.apache.poi.util.StringUtil;
/** /**
@ -31,28 +30,25 @@ import org.apache.poi.util.StringUtil;
* @author Bernard Chesnoy * @author Bernard Chesnoy
*/ */
public final class StringPtg extends ScalarConstantPtg { public final class StringPtg extends ScalarConstantPtg {
public final static int SIZE = 9;
public final static byte sid = 0x17; public final static byte sid = 0x17;
private static final BitField fHighByte = BitFieldFactory.getInstance(0x01); /** the character (") used in formulas to delimit string literals */
/** the character (")used in formulas to delimit string literals */
private static final char FORMULA_DELIMITER = '"'; private static final char FORMULA_DELIMITER = '"';
private final boolean _is16bitUnicode;
/** /**
* NOTE: OO doc says 16bit length, but BiffViewer says 8 Book says something * NOTE: OO doc says 16bit length, but BiffViewer says 8 Book says something
* totally different, so don't look there! * 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; private final String field_3_string;
/** Create a StringPtg from a stream */ /** Create a StringPtg from a stream */
public StringPtg(RecordInputStream in) { public StringPtg(LittleEndianInput in) {
field_1_length = in.readUByte(); int nChars = in.readUByte(); // Note - nChars is 8-bit
field_2_options = in.readByte(); _is16bitUnicode = (in.readByte() & 0x01) != 0;
if (fHighByte.isSet(field_2_options)) { if (_is16bitUnicode) {
field_3_string = in.readUnicodeLEString(field_1_length); field_3_string = StringUtil.readUnicodeLE(in, nChars);
} else { } else {
field_3_string = in.readCompressedUnicode(field_1_length); field_3_string = StringUtil.readCompressedUnicode(in, nChars);
} }
} }
@ -69,32 +65,27 @@ public final class StringPtg extends ScalarConstantPtg {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"String literals in formulas can't be bigger than 255 characters ASCII"); "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_3_string = value;
field_1_length = value.length(); // for the moment, we support only ASCII strings in formulas we create
} }
public String getValue() { public String getValue() {
return field_3_string; return field_3_string;
} }
public void writeBytes(byte[] array, int offset) { public void write(LittleEndianOutput out) {
array[offset + 0] = sid; out.writeByte(sid + getPtgClass());
array[offset + 1] = (byte) field_1_length; out.writeByte(field_3_string.length()); // Note - nChars is 8-bit
array[offset + 2] = field_2_options; out.writeByte(_is16bitUnicode ? 0x01 : 0x00);
if (fHighByte.isSet(field_2_options)) { if (_is16bitUnicode) {
StringUtil.putUnicodeLE(getValue(), array, offset + 3); StringUtil.putUnicodeLE(field_3_string, out);
} else { } else {
StringUtil.putCompressedUnicode(getValue(), array, offset + 3); StringUtil.putCompressedUnicode(field_3_string, out);
} }
} }
public int getSize() { public int getSize() {
if (fHighByte.isSet(field_2_options)) { return 3 + field_3_string.length() * (_is16bitUnicode ? 2 : 1);
return 2 * field_1_length + 3;
} else {
return field_1_length + 3;
}
} }
public String toFormulaString() { public String toFormulaString() {

View File

@ -18,8 +18,8 @@
package org.apache.poi.hssf.record.formula; package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordFormatException; import org.apache.poi.hssf.record.RecordFormatException;
import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndianOutput;
/** /**
* This ptg indicates a data table. * This ptg indicates a data table.
@ -43,15 +43,15 @@ public final class TblPtg extends ControlPtg {
/** The column number of the upper left corner */ /** The column number of the upper left corner */
private final int field_2_first_col; private final int field_2_first_col;
public TblPtg(RecordInputStream in) { public TblPtg(LittleEndianInput in) {
field_1_first_row = in.readUShort(); field_1_first_row = in.readUShort();
field_2_first_col = in.readUShort(); field_2_first_col = in.readUShort();
} }
public void writeBytes(byte [] array, int offset) { public void write(LittleEndianOutput out) {
LittleEndian.putByte(array, offset+0, sid); out.writeByte(sid + getPtgClass());
LittleEndian.putUShort(array, offset+1, field_1_first_row); out.writeShort(field_1_first_row);
LittleEndian.putUShort(array, offset+3, field_2_first_col); out.writeShort(field_2_first_col);
} }
public int getSize() { public int getSize() {

View File

@ -17,6 +17,8 @@
package org.apache.poi.hssf.record.formula; package org.apache.poi.hssf.record.formula;
import org.apache.poi.util.LittleEndianOutput;
/** /**
* @author Glen Stampoultzis (glens at apache.org) * @author Glen Stampoultzis (glens at apache.org)
@ -39,9 +41,8 @@ public final class UnionPtg extends OperationPtg {
return 1; return 1;
} }
public void writeBytes( byte[] array, int offset ) public void write(LittleEndianOutput out) {
{ out.writeByte(sid + getPtgClass());
array[ offset + 0 ] = sid;
} }
public String toFormulaString() public String toFormulaString()

View File

@ -16,7 +16,7 @@
==================================================================== */ ==================================================================== */
package org.apache.poi.hssf.record.formula; 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 { public class UnknownPtg extends Ptg {
private short size = 1; private short size = 1;
private final int _sid;
/** Creates new UnknownPtg */ public UnknownPtg(int sid) {
_sid = sid;
public UnknownPtg()
{
}
public UnknownPtg(RecordInputStream in) {
// doesn't need anything
} }
public boolean isBaseToken() { 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() public int getSize()
@ -55,8 +50,6 @@ public class UnknownPtg extends Ptg {
public byte getDefaultOperandClass() {return Ptg.CLASS_VALUE;} public byte getDefaultOperandClass() {return Ptg.CLASS_VALUE;}
public Object clone() { public Object clone() {
return new UnknownPtg(); return this;
} }
} }

View File

@ -17,11 +17,12 @@
package org.apache.poi.hssf.record.formula; 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. * Common superclass of all value operators. Subclasses include all unary and
* Subclasses include all unary and binary operators except for the reference operators (IntersectionPtg, RangePtg, UnionPtg) * binary operators except for the reference operators (IntersectionPtg,
* RangePtg, UnionPtg)
* *
* @author Josh Micich * @author Josh Micich
*/ */
@ -38,8 +39,8 @@ public abstract class ValueOperatorPtg extends OperationPtg {
return Ptg.CLASS_VALUE; return Ptg.CLASS_VALUE;
} }
public final void writeBytes(byte[] array, int offset) { public void write(LittleEndianOutput out) {
array[offset + 0] = getSid(); out.writeByte(getSid());
} }
protected abstract byte getSid(); protected abstract byte getSid();
@ -47,6 +48,7 @@ public abstract class ValueOperatorPtg extends OperationPtg {
public final int getSize() { public final int getSize() {
return 1; return 1;
} }
public final String toFormulaString() { public final String toFormulaString() {
// TODO - prune this method out of the hierarchy // TODO - prune this method out of the hierarchy
throw new RuntimeException("toFormulaString(String[] operands) should be used for subclasses of OperationPtgs"); throw new RuntimeException("toFormulaString(String[] operands) should be used for subclasses of OperationPtgs");

View File

@ -975,7 +975,7 @@ public class HSSFCellStyle implements CellStyle
if(sr == null) { if(sr == null) {
return null; return null;
} }
if(sr.getType() == StyleRecord.STYLE_BUILT_IN) { if(sr.isBuiltin()) {
return null; return null;
} }
return sr.getName(); return sr.getName();
@ -990,7 +990,7 @@ public class HSSFCellStyle implements CellStyle
if(sr == null) { if(sr == null) {
sr = workbook.createStyleRecord(index); 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!"); throw new IllegalArgumentException("Unable to set user specified style names for built in styles!");
} }
sr.setName(styleName); sr.setName(styleName);

View File

@ -1,4 +1,3 @@
/* ==================================================================== /* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
@ -16,436 +15,313 @@
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.poifs.filesystem; 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 * This class provides methods to read a DocumentEntry managed by a
* Filesystem instance. * {@link POIFSFileSystem} instance.
* *
* @author Marc Johnson (mjohnson at apache dot org) * @author Marc Johnson (mjohnson at apache dot org)
*/ */
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;
public class DocumentInputStream private static final int SIZE_SHORT = 2;
extends InputStream private static final int SIZE_INT = 4;
{ private static final int SIZE_LONG = 8;
// current offset into the Document /** current offset into the Document */
private int _current_offset; private int _current_offset;
// current marked offset into the Document (used by mark and /** current marked offset into the Document (used by mark and reset) */
// reset)
private int _marked_offset; private int _marked_offset;
// the Document's size /** the Document's size */
private int _document_size; private int _document_size;
// have we been closed? /** have we been closed? */
private boolean _closed; private boolean _closed;
// the actual Document /** the actual Document */
private POIFSDocument _document; private POIFSDocument _document;
// buffer used to read one byte at a time /** the data block containing the current stream pointer */
private byte[] _tiny_buffer; private DataInputBlock _currentBlock;
// returned by read operations if we're at end of document
static private final int EOD = -1;
/** /**
* Create an InputStream from the specified DocumentEntry * Create an InputStream from the specified DocumentEntry
* *
* @param document the DocumentEntry to be read * @param document the DocumentEntry to be read
* *
* @exception IOException if the DocumentEntry cannot be opened * @exception IOException if the DocumentEntry cannot be opened (like, maybe it has
* (like, maybe it has been deleted?) * been deleted?)
*/ */
public DocumentInputStream(DocumentEntry document) throws IOException {
public DocumentInputStream(final DocumentEntry document) if (!(document instanceof DocumentNode)) {
throws IOException throw new IOException("Cannot open internal document storage");
{ }
_current_offset = 0; _current_offset = 0;
_marked_offset = 0; _marked_offset = 0;
_document_size = document.getSize(); _document_size = document.getSize();
_closed = false; _closed = false;
_tiny_buffer = null; _document = ((DocumentNode) document).getDocument();
if (document instanceof DocumentNode) _currentBlock = getDataInputBlock(0);
{
_document = (( DocumentNode ) document).getDocument();
}
else
{
throw new IOException("Cannot open internal document storage");
}
} }
/** /**
* Create an InputStream from the specified Document * Create an InputStream from the specified Document
* *
* @param document the Document to be read * @param document the Document to be read
*
* @exception IOException if the DocumentEntry cannot be opened
* (like, maybe it has been deleted?)
*/ */
public DocumentInputStream(POIFSDocument document) {
public DocumentInputStream(final POIFSDocument document)
throws IOException
{
_current_offset = 0; _current_offset = 0;
_marked_offset = 0; _marked_offset = 0;
_document_size = document.getSize(); _document_size = document.getSize();
_closed = false; _closed = false;
_tiny_buffer = null;
_document = document; _document = document;
_currentBlock = getDataInputBlock(0);
} }
/** public int available() {
* Returns the number of bytes that can be read (or skipped over) if (_closed) {
* from this input stream without blocking by the next caller of a throw new IllegalStateException("cannot perform requested operation on a closed stream");
* 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; return _document_size - _current_offset;
} }
/** public void close() {
* Closes this input stream and releases any system resources
* associated with the stream.
*
* @exception IOException
*/
public void close()
throws IOException
{
_closed = true; _closed = true;
} }
/** public void mark(int ignoredReadlimit) {
* 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.
* <p>
* 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.
* <p>
* 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; _marked_offset = _current_offset;
} }
/** /**
* Tests if this input stream supports the mark and reset methods. * Tests if this input stream supports the mark and reset methods.
* *
* @return true * @return <code>true</code> always
*/ */
public boolean markSupported() {
public boolean markSupported()
{
return true; return true;
} }
/** private DataInputBlock getDataInputBlock(int offset) {
* Reads the next byte of data from the input stream. The value return _document.getDataInputBlock(offset);
* 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() public int read() throws IOException {
throws IOException
{
dieIfClosed(); dieIfClosed();
if (atEOD()) if (atEOD()) {
{ return EOF;
return EOD;
} }
if (_tiny_buffer == null) int result = _currentBlock.readUByte();
{ _current_offset++;
_tiny_buffer = new byte[ 1 ]; if (_currentBlock.available() < 1) {
_currentBlock = getDataInputBlock(_current_offset);
} }
_document.read(_tiny_buffer, _current_offset++); return result;
return ((int)_tiny_buffer[ 0 ]) & 0x000000FF;
} }
/** public int read(byte[] b) throws IOException {
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* The read(b) method for class InputStream has the same effect as:
* <p>
* <code>read(b, 0, b.length)</code>
*
* @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); return read(b, 0, b.length);
} }
/** public int read(byte[] b, int off, int len) throws IOException {
* 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.
* <p>
* The definition of this method in java.io.InputStream allows it
* to block, but it won't.
* <p>
* If b is null, a NullPointerException is thrown.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* In every case, elements b[0] through b[off] and elements
* b[off+len] through b[b.length-1] are unaffected.
* <p>
* 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(); dieIfClosed();
if (b == null) if (b == null) {
{ throw new IllegalArgumentException("buffer must not be null");
throw new NullPointerException("buffer is null");
} }
if ((off < 0) || (len < 0) || (b.length < (off + len))) if (off < 0 || len < 0 || b.length < off + len) {
{ throw new IndexOutOfBoundsException("can't read past buffer boundaries");
throw new IndexOutOfBoundsException(
"can't read past buffer boundaries");
} }
if (len == 0) if (len == 0) {
{
return 0; return 0;
} }
if (atEOD()) if (atEOD()) {
{ return EOF;
return EOD;
} }
int limit = Math.min(available(), len); int limit = Math.min(available(), len);
readFully(b, off, limit);
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; return limit;
} }
/** /**
* Repositions this stream to the position at the time the mark * Repositions this stream to the position at the time the mark() method was
* method was last called on this input stream. * last called on this input stream. If mark() has not been called this
* <p> * method repositions the stream to its beginning.
* The general contract of reset is:
* <p>
* <ul>
* <li>
* If the method markSupported returns true, then:
* <ul>
* <li>
* If the method mark has not been called since the
* stream was created, or the number of bytes read
* from the stream since mark was last called is
* larger than the argument to mark at that last
* call, then an IOException might be thrown.
* </li>
* <li>
* If such an IOException is not thrown, then the
* stream is reset to a state such that all the
* bytes read since the most recent call to mark
* (or since the start of the file, if mark has not
* been called) will be resupplied to subsequent
* callers of the read method, followed by any
* bytes that otherwise would have been the next
* input data as of the time of the call to reset.
* </li>
* </ul>
* </li>
* <li>
* If the method markSupported returns false, then:
* <ul>
* <li>
* The call to reset may throw an IOException.
* </li>
* <li>
* If an IOException is not thrown, then the
* stream is reset to a fixed state that depends
* on the particular type of the input and how it
* was created. The bytes that will be supplied to
* subsequent callers of the read method depend on
* the particular type of the input stream.
* </li>
* </ul>
* </li>
* </ul>
* <p>
* 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() {
public void reset()
{
_current_offset = _marked_offset; _current_offset = _marked_offset;
_currentBlock = getDataInputBlock(_current_offset);
} }
/** public long skip(long n) throws IOException {
* 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(); dieIfClosed();
if (n < 0) if (n < 0) {
{
return 0; return 0;
} }
int new_offset = _current_offset + ( int ) n; int new_offset = _current_offset + (int) n;
if (new_offset < _current_offset) if (new_offset < _current_offset) {
{
// wrap around in converting a VERY large long to an int // wrap around in converting a VERY large long to an int
new_offset = _document_size; new_offset = _document_size;
} } else if (new_offset > _document_size) {
else if (new_offset > _document_size)
{
new_offset = _document_size; new_offset = _document_size;
} }
long rval = new_offset - _current_offset; long rval = new_offset - _current_offset;
_current_offset = new_offset; _current_offset = new_offset;
_currentBlock = getDataInputBlock(_current_offset);
return rval; return rval;
} }
private void dieIfClosed() private void dieIfClosed() throws IOException {
throws IOException if (_closed) {
{ throw new IOException("cannot perform requested operation on a closed stream");
if (_closed)
{
throw new IOException(
"cannot perform requested operation on a closed stream");
} }
} }
private boolean atEOD() private boolean atEOD() {
{
return _current_offset == _document_size; return _current_offset == _document_size;
} }
} // end public class DocumentInputStream
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;
}
}

View File

@ -1,4 +1,3 @@
/* ==================================================================== /* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
@ -16,20 +15,25 @@
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.poifs.filesystem; package org.apache.poi.poifs.filesystem;
import java.io.*; import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.*; 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.common.POIFSConstants;
import org.apache.poi.poifs.dev.POIFSViewable; import org.apache.poi.poifs.dev.POIFSViewable;
import org.apache.poi.poifs.property.DocumentProperty; import org.apache.poi.poifs.property.DocumentProperty;
import org.apache.poi.poifs.property.Property; import org.apache.poi.poifs.property.Property;
import org.apache.poi.poifs.storage.BlockWritable; 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.DocumentBlock;
import org.apache.poi.poifs.storage.ListManagedBlock;
import org.apache.poi.poifs.storage.RawDataBlock; import org.apache.poi.poifs.storage.RawDataBlock;
import org.apache.poi.poifs.storage.SmallDocumentBlock; import org.apache.poi.poifs.storage.SmallDocumentBlock;
import org.apache.poi.util.HexDump; import org.apache.poi.util.HexDump;
@ -39,10 +43,9 @@ import org.apache.poi.util.HexDump;
* *
* @author Marc Johnson (mjohnson at apache dot org) * @author Marc Johnson (mjohnson at apache dot org)
*/ */
public final class POIFSDocument implements BATManaged, BlockWritable, POIFSViewable {
public class POIFSDocument private static final DocumentBlock[] EMPTY_BIG_BLOCK_ARRAY = { };
implements BATManaged, BlockWritable, POIFSViewable private static final SmallDocumentBlock[] EMPTY_SMALL_BLOCK_ARRAY = { };
{
private DocumentProperty _property; private DocumentProperty _property;
private int _size; private int _size;
@ -56,21 +59,32 @@ public class POIFSDocument
* @param name the name of the POIFSDocument * @param name the name of the POIFSDocument
* @param blocks the big blocks making up the POIFSDocument * @param blocks the big blocks making up the POIFSDocument
* @param length the actual length of the POIFSDocument * @param length the actual length of the POIFSDocument
*
* @exception IOException
*/ */
public POIFSDocument(String name, RawDataBlock[] blocks, int length) throws IOException {
public POIFSDocument(final String name, final RawDataBlock [] blocks,
final int length)
throws IOException
{
_size = length; _size = length;
_big_store = new BigBlockStore(blocks); _big_store = new BigBlockStore(convertRawBlocksToBigBlocks(blocks));
_property = new DocumentProperty(name, _size); _property = new DocumentProperty(name, _size);
_small_store = new SmallBlockStore(new BlockWritable[ 0 ]); _small_store = new SmallBlockStore(EMPTY_SMALL_BLOCK_ARRAY);
_property.setDocument(this); _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 * Constructor from small blocks
* *
@ -78,20 +92,9 @@ public class POIFSDocument
* @param blocks the small blocks making up the POIFSDocument * @param blocks the small blocks making up the POIFSDocument
* @param length the actual length of the POIFSDocument * @param length the actual length of the POIFSDocument
*/ */
public POIFSDocument(String name, SmallDocumentBlock[] blocks, int length) {
public POIFSDocument(final String name,
final SmallDocumentBlock [] blocks, final int length)
{
_size = length; _size = length;
try _big_store = new BigBlockStore(EMPTY_BIG_BLOCK_ARRAY);
{
_big_store = new BigBlockStore(new RawDataBlock[ 0 ]);
}
catch (IOException ignored)
{
// can't happen with that constructor
}
_property = new DocumentProperty(name, _size); _property = new DocumentProperty(name, _size);
_small_store = new SmallBlockStore(blocks); _small_store = new SmallBlockStore(blocks);
_property.setDocument(this); _property.setDocument(this);
@ -103,26 +106,17 @@ public class POIFSDocument
* @param name the name of the POIFSDocument * @param name the name of the POIFSDocument
* @param blocks the small blocks making up the POIFSDocument * @param blocks the small blocks making up the POIFSDocument
* @param length the actual length of the POIFSDocument * @param length the actual length of the POIFSDocument
*
* @exception IOException
*/ */
public POIFSDocument(String name, ListManagedBlock[] blocks, int length) throws IOException {
public POIFSDocument(final String name, final ListManagedBlock [] blocks,
final int length)
throws IOException
{
_size = length; _size = length;
_property = new DocumentProperty(name, _size); _property = new DocumentProperty(name, _size);
_property.setDocument(this); _property.setDocument(this);
if (Property.isSmall(_size)) if (Property.isSmall(_size)) {
{ _big_store = new BigBlockStore(EMPTY_BIG_BLOCK_ARRAY);
_big_store = new BigBlockStore(new RawDataBlock[ 0 ]); _small_store = new SmallBlockStore(convertRawBlocksToSmallBlocks(blocks));
_small_store = new SmallBlockStore(blocks); } else {
} _big_store = new BigBlockStore(convertRawBlocksToBigBlocks(blocks));
else _small_store = new SmallBlockStore(EMPTY_SMALL_BLOCK_ARRAY);
{
_big_store = new BigBlockStore(blocks);
_small_store = new SmallBlockStore(new BlockWritable[ 0 ]);
} }
} }
@ -131,47 +125,33 @@ public class POIFSDocument
* *
* @param name the name of the POIFSDocument * @param name the name of the POIFSDocument
* @param stream the InputStream we read data from * @param stream the InputStream we read data from
*
* @exception IOException thrown on read errors
*/ */
public POIFSDocument(String name, InputStream stream) throws IOException {
public POIFSDocument(final String name, final InputStream stream)
throws IOException
{
List blocks = new ArrayList(); List blocks = new ArrayList();
_size = 0; _size = 0;
while (true) while (true) {
{
DocumentBlock block = new DocumentBlock(stream); DocumentBlock block = new DocumentBlock(stream);
int blockSize = block.size(); int blockSize = block.size();
if (blockSize > 0) if (blockSize > 0) {
{
blocks.add(block); blocks.add(block);
_size += blockSize; _size += blockSize;
} }
if (block.partiallyRead()) if (block.partiallyRead()) {
{
break; break;
} }
} }
DocumentBlock[] bigBlocks = DocumentBlock[] bigBlocks = (DocumentBlock[]) blocks.toArray(new DocumentBlock[blocks.size()]);
( DocumentBlock [] ) blocks.toArray(new DocumentBlock[ 0 ]);
_big_store = new BigBlockStore(bigBlocks); _big_store = new BigBlockStore(bigBlocks);
_property = new DocumentProperty(name, _size); _property = new DocumentProperty(name, _size);
_property.setDocument(this); _property.setDocument(this);
if (_property.shouldUseSmallBlocks()) if (_property.shouldUseSmallBlocks()) {
{ _small_store = new SmallBlockStore(SmallDocumentBlock.convert(bigBlocks, _size));
_small_store = _big_store = new BigBlockStore(new DocumentBlock[0]);
new SmallBlockStore(SmallDocumentBlock.convert(bigBlocks, } else {
_size)); _small_store = new SmallBlockStore(EMPTY_SMALL_BLOCK_ARRAY);
_big_store = new BigBlockStore(new DocumentBlock[ 0 ]);
}
else
{
_small_store = new SmallBlockStore(new BlockWritable[ 0 ]);
} }
} }
@ -181,49 +161,32 @@ public class POIFSDocument
* @param name the name of the POIFSDocument * @param name the name of the POIFSDocument
* @param size the length of the POIFSDocument * @param size the length of the POIFSDocument
* @param path the path of the POIFSDocument * @param path the path of the POIFSDocument
* @param writer the writer who will eventually write the document * @param writer the writer who will eventually write the document contents
* contents
*
* @exception IOException thrown on read errors
*/ */
public POIFSDocument(String name, int size, POIFSDocumentPath path, POIFSWriterListener writer) {
public POIFSDocument(final String name, final int size,
final POIFSDocumentPath path,
final POIFSWriterListener writer)
throws IOException
{
_size = size; _size = size;
_property = new DocumentProperty(name, _size); _property = new DocumentProperty(name, _size);
_property.setDocument(this); _property.setDocument(this);
if (_property.shouldUseSmallBlocks()) if (_property.shouldUseSmallBlocks()) {
{
_small_store = new SmallBlockStore(path, name, size, writer); _small_store = new SmallBlockStore(path, name, size, writer);
_big_store = new BigBlockStore(new Object[ 0 ]); _big_store = new BigBlockStore(EMPTY_BIG_BLOCK_ARRAY);
} } else {
else _small_store = new SmallBlockStore(EMPTY_SMALL_BLOCK_ARRAY);
{
_small_store = new SmallBlockStore(new BlockWritable[ 0 ]);
_big_store = new BigBlockStore(path, name, size, writer); _big_store = new BigBlockStore(path, name, size, writer);
} }
} }
/** /**
* return the array of SmallDocumentBlocks used
*
* @return array of SmallDocumentBlocks; may be empty, cannot be null * @return array of SmallDocumentBlocks; may be empty, cannot be null
*/ */
public BlockWritable[] getSmallBlocks() {
public BlockWritable [] getSmallBlocks()
{
return _small_store.getBlocks(); return _small_store.getBlocks();
} }
/** /**
* @return size of the document * @return size of the document
*/ */
public int getSize() {
public int getSize()
{
return _size; return _size;
} }
@ -232,28 +195,70 @@ public class POIFSDocument
* *
* @param buffer the buffer to write to * @param buffer the buffer to write to
* @param offset the offset into our storage to read from * @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;
void read(final byte [] buffer, final int offset) DataInputBlock currentBlock = getDataInputBlock(offset);
{
if (_property.shouldUseSmallBlocks()) int blockAvailable = currentBlock.available();
{ if (blockAvailable > len) {
SmallDocumentBlock.read(_small_store.getBlocks(), buffer, offset); 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();
} }
else
{
DocumentBlock.read(_big_store.getBlocks(), buffer, offset);
} }
} }
/** /**
* Get the DocumentProperty * @return <code>null</code> if <tt>offset</tt> 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 * @return the instance's DocumentProperty
*/ */
DocumentProperty getDocumentProperty() DocumentProperty getDocumentProperty() {
{
return _property; return _property;
} }
@ -262,16 +267,9 @@ public class POIFSDocument
/** /**
* Write the storage to an OutputStream * Write the storage to an OutputStream
* *
* @param stream the OutputStream to which the stored data should * @param stream the OutputStream to which the stored data should be written
* be written
*
* @exception IOException on problems writing to the specified
* stream
*/ */
public void writeBlocks(OutputStream stream) throws IOException {
public void writeBlocks(final OutputStream stream)
throws IOException
{
_big_store.writeBlocks(stream); _big_store.writeBlocks(stream);
} }
@ -283,21 +281,16 @@ public class POIFSDocument
* *
* @return count of BigBlock instances * @return count of BigBlock instances
*/ */
public int countBlocks() {
public int countBlocks()
{
return _big_store.countBlocks(); return _big_store.countBlocks();
} }
/** /**
* Set the start block for this instance * Set the start block for this instance
* *
* @param index index into the array of blocks making up the * @param index index into the array of blocks making up the filesystem
* filesystem
*/ */
public void setStartBlock(int index) {
public void setStartBlock(final int index)
{
_property.setStartBlock(index); _property.setStartBlock(index);
} }
@ -305,41 +298,31 @@ public class POIFSDocument
/* ********** START begin implementation of POIFSViewable ********** */ /* ********** START begin implementation of POIFSViewable ********** */
/** /**
* Get an array of objects, some of which may implement * Get an array of objects, some of which may implement POIFSViewable
* POIFSViewable
* *
* @return an array of Object; may not be null, but may be empty * @return an array of Object; may not be null, but may be empty
*/ */
public Object[] getViewableArray() {
public Object [] getViewableArray() Object[] results = new Object[1];
{
Object[] results = new Object[ 1 ];
String result; String result;
try try {
{
ByteArrayOutputStream output = new ByteArrayOutputStream(); ByteArrayOutputStream output = new ByteArrayOutputStream();
BlockWritable[] blocks = null; BlockWritable[] blocks = null;
if (_big_store.isValid()) if (_big_store.isValid()) {
{
blocks = _big_store.getBlocks(); blocks = _big_store.getBlocks();
} } else if (_small_store.isValid()) {
else if (_small_store.isValid())
{
blocks = _small_store.getBlocks(); blocks = _small_store.getBlocks();
} }
if (blocks != null) if (blocks != null) {
{ for (int k = 0; k < blocks.length; k++) {
for (int k = 0; k < blocks.length; k++) blocks[k].writeBlocks(output);
{
blocks[ k ].writeBlocks(output);
} }
byte[] data = output.toByteArray(); byte[] data = output.toByteArray();
if (data.length > _property.getSize()) if (data.length > _property.getSize()) {
{ byte[] tmp = new byte[_property.getSize()];
byte[] tmp = new byte[ _property.getSize() ];
System.arraycopy(data, 0, tmp, 0, tmp.length); System.arraycopy(data, 0, tmp, 0, tmp.length);
data = tmp; data = tmp;
@ -347,30 +330,23 @@ public class POIFSDocument
output = new ByteArrayOutputStream(); output = new ByteArrayOutputStream();
HexDump.dump(data, 0, output, 0); HexDump.dump(data, 0, output, 0);
result = output.toString(); result = output.toString();
} } else {
else
{
result = "<NO DATA>"; result = "<NO DATA>";
} }
} } catch (IOException e) {
catch (IOException e)
{
result = e.getMessage(); result = e.getMessage();
} }
results[ 0 ] = result; results[0] = result;
return results; return results;
} }
/** /**
* Get an Iterator of objects, some of which may implement * Get an Iterator of objects, some of which may implement POIFSViewable
* POIFSViewable
* *
* @return an Iterator; may not be null, but may have an empty * @return an Iterator; may not be null, but may have an empty back end
* back end store * store
*/ */
public Iterator getViewableIterator() {
public Iterator getViewableIterator()
{
return Collections.EMPTY_LIST.iterator(); return Collections.EMPTY_LIST.iterator();
} }
@ -378,12 +354,10 @@ public class POIFSDocument
* Give viewers a hint as to whether to call getViewableArray or * Give viewers a hint as to whether to call getViewableArray or
* getViewableIterator * getViewableIterator
* *
* @return true if a viewer should call getViewableArray, false if * @return <code>true</code> if a viewer should call getViewableArray,
* a viewer should call getViewableIterator * <code>false</code> if a viewer should call getViewableIterator
*/ */
public boolean preferArray() {
public boolean preferArray()
{
return true; return true;
} }
@ -393,39 +367,29 @@ public class POIFSDocument
* *
* @return short description * @return short description
*/ */
public String getShortDescription() {
public String getShortDescription()
{
StringBuffer buffer = new StringBuffer(); StringBuffer buffer = new StringBuffer();
buffer.append("Document: \"").append(_property.getName()) buffer.append("Document: \"").append(_property.getName()).append("\"");
.append("\"");
buffer.append(" size = ").append(getSize()); buffer.append(" size = ").append(getSize());
return buffer.toString(); return buffer.toString();
} }
/* ********** END begin implementation of POIFSViewable ********** */ /* ********** END begin implementation of POIFSViewable ********** */
private class SmallBlockStore private static final class SmallBlockStore {
{
private SmallDocumentBlock[] smallBlocks; private SmallDocumentBlock[] smallBlocks;
private POIFSDocumentPath path; private final POIFSDocumentPath path;
private String name; private final String name;
private int size; private final int size;
private POIFSWriterListener writer; private final POIFSWriterListener writer;
/** /**
* Constructor * Constructor
* *
* @param blocks blocks to construct the store from * @param blocks blocks to construct the store from
*/ */
SmallBlockStore(SmallDocumentBlock[] blocks) {
SmallBlockStore(final Object [] blocks) smallBlocks = (SmallDocumentBlock[]) blocks.clone();
{
smallBlocks = new SmallDocumentBlock[ blocks.length ];
for (int j = 0; j < blocks.length; j++)
{
smallBlocks[ j ] = ( SmallDocumentBlock ) blocks[ j ];
}
this.path = null; this.path = null;
this.name = null; this.name = null;
this.size = -1; this.size = -1;
@ -433,19 +397,15 @@ public class POIFSDocument
} }
/** /**
* Constructor for a small block store that will be written * Constructor for a small block store that will be written later
* later
* *
* @param path path of the document * @param path path of the document
* @param name name of the document * @param name name of the document
* @param size length of the document * @param size length of the document
* @param writer the object that will eventually write the document * @param writer the object that will eventually write the document
*/ */
SmallBlockStore(POIFSDocumentPath path, String name, int size, POIFSWriterListener writer) {
SmallBlockStore(final POIFSDocumentPath path, final String name, smallBlocks = new SmallDocumentBlock[0];
final int size, final POIFSWriterListener writer)
{
smallBlocks = new SmallDocumentBlock[ 0 ];
this.path = path; this.path = path;
this.name = name; this.name = name;
this.size = size; this.size = size;
@ -453,68 +413,41 @@ public class POIFSDocument
} }
/** /**
* @return true if this store is a valid source of data * @return <code>true</code> if this store is a valid source of data
*/ */
boolean isValid() {
boolean isValid() return smallBlocks.length > 0 || writer != null;
{
return ((smallBlocks.length > 0) || (writer != null));
} }
/** /**
* @return the SmallDocumentBlocks * @return the SmallDocumentBlocks
*/ */
SmallDocumentBlock[] getBlocks() {
if (isValid() && writer != null) {
ByteArrayOutputStream stream = new ByteArrayOutputStream(size);
DocumentOutputStream dstream = new DocumentOutputStream(stream, size);
BlockWritable [] getBlocks() writer.processPOIFSWriterEvent(new POIFSWriterEvent(dstream, path, name, size));
{ smallBlocks = SmallDocumentBlock.convert(stream.toByteArray(), size);
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; return smallBlocks;
} }
} // end private class SmallBlockStore } // end private class SmallBlockStore
private class BigBlockStore private static final class BigBlockStore {
{
private DocumentBlock[] bigBlocks; private DocumentBlock[] bigBlocks;
private POIFSDocumentPath path; private final POIFSDocumentPath path;
private String name; private final String name;
private int size; private final int size;
private POIFSWriterListener writer; private final POIFSWriterListener writer;
/** /**
* Constructor * Constructor
* *
* @param blocks the blocks making up the store * @param blocks the blocks making up the store
*
* @exception IOException on I/O error
*/ */
BigBlockStore(DocumentBlock[] blocks) {
BigBlockStore(final Object [] blocks) bigBlocks = (DocumentBlock[]) blocks.clone();
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.path = null;
this.name = null; this.name = null;
this.size = -1; this.size = -1;
@ -522,20 +455,15 @@ public class POIFSDocument
} }
/** /**
* Constructor for a big block store that will be written * Constructor for a big block store that will be written later
* later
* *
* @param path path of the document * @param path path of the document
* @param name name of the document * @param name name of the document
* @param size length of the document * @param size length of the document
* @param writer the object that will eventually write the * @param writer the object that will eventually write the document
* document
*/ */
BigBlockStore(POIFSDocumentPath path, String name, int size, POIFSWriterListener writer) {
BigBlockStore(final POIFSDocumentPath path, final String name, bigBlocks = new DocumentBlock[0];
final int size, final POIFSWriterListener writer)
{
bigBlocks = new DocumentBlock[ 0 ];
this.path = path; this.path = path;
this.name = name; this.name = name;
this.size = size; this.size = size;
@ -543,29 +471,21 @@ public class POIFSDocument
} }
/** /**
* @return true if this store is a valid source of data * @return <code>true</code> if this store is a valid source of data
*/ */
boolean isValid() {
boolean isValid() return bigBlocks.length > 0 || writer != null;
{
return ((bigBlocks.length > 0) || (writer != null));
} }
/** /**
* @return the DocumentBlocks * @return the DocumentBlocks
*/ */
DocumentBlock[] getBlocks() {
if (isValid() && writer != null) {
ByteArrayOutputStream stream = new ByteArrayOutputStream(size);
DocumentOutputStream dstream = new DocumentOutputStream(stream, size);
DocumentBlock [] getBlocks() writer.processPOIFSWriterEvent(new POIFSWriterEvent(dstream, path, name, size));
{
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); bigBlocks = DocumentBlock.convert(stream.toByteArray(), size);
} }
return bigBlocks; return bigBlocks;
@ -575,32 +495,18 @@ public class POIFSDocument
* write the blocks to a stream * write the blocks to a stream
* *
* @param stream the stream to which the data is to be written * @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);
void writeBlocks(OutputStream stream) writer.processPOIFSWriterEvent(new POIFSWriterEvent(dstream, path, name, size));
throws IOException dstream.writeFiller(countBlocks() * POIFSConstants.BIG_BLOCK_SIZE,
{ DocumentBlock.getFillByte());
if (isValid()) } else {
{ for (int k = 0; k < bigBlocks.length; k++) {
if (writer != null) bigBlocks[k].writeBlocks(stream);
{
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);
} }
} }
} }
@ -609,25 +515,16 @@ public class POIFSDocument
/** /**
* @return number of big blocks making up this document * @return number of big blocks making up this document
*/ */
int countBlocks() {
int countBlocks() if (isValid()) {
{ if (writer == null) {
int rval = 0; return bigBlocks.length;
}
if (isValid()) return (size + POIFSConstants.BIG_BLOCK_SIZE - 1)
{
if (writer != null)
{
rval = (size + POIFSConstants.BIG_BLOCK_SIZE - 1)
/ POIFSConstants.BIG_BLOCK_SIZE; / POIFSConstants.BIG_BLOCK_SIZE;
} }
else return 0;
{
rval = bigBlocks.length;
}
}
return rval;
} }
} // end private class BigBlockStore } // end private class BigBlockStore
} // end class POIFSDocument }

View File

@ -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 <tt>byte</tt> 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.<p/>
*
* 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 .<p/>
*
* 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 <tt>byte</tt> 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 <tt>short</tt> which was encoded in <em>little endian</em> 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 <tt>short</tt> which spans the end of <tt>prevBlock</tt> 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 <tt>int</tt> which was encoded in <em>little endian</em> 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 <tt>int</tt> which spans the end of <tt>prevBlock</tt> 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 <tt>long</tt> which was encoded in <em>little endian</em> 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 <tt>long</tt> which spans the end of <tt>prevBlock</tt> 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 <tt>len</tt> 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;
}
}

View File

@ -1,4 +1,3 @@
/* ==================================================================== /* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
@ -16,30 +15,26 @@
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.poifs.storage; package org.apache.poi.poifs.storage;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Arrays; import java.util.Arrays;
import org.apache.poi.poifs.common.POIFSConstants; import org.apache.poi.poifs.common.POIFSConstants;
import org.apache.poi.util.IOUtils; 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. * A block of document data.
* *
* @author Marc Johnson (mjohnson at apache dot org) * @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 static final byte _default_value = ( byte ) 0xFF;
private byte[] _data; private byte[] _data;
private int _bytes_read; private int _bytes_read;
@ -161,45 +156,10 @@ public class DocumentBlock
return rval; return rval;
} }
/** public static DataInputBlock getDataInputBlock(DocumentBlock[] blocks, int offset) {
* read data from an array of DocumentBlocks int firstBlockIndex = offset >> BLOCK_SHIFT;
* int firstBlockOffset= offset & BLOCK_MASK;
* @param blocks the blocks to read from return new DataInputBlock(blocks[firstBlockIndex]._data, firstBlockOffset);
* @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);
}
} }
/* ********** START extension of BigBlock ********** */ /* ********** START extension of BigBlock ********** */

View File

@ -1,4 +1,3 @@
/* ==================================================================== /* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
@ -16,12 +15,14 @@
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.poifs.storage; package org.apache.poi.poifs.storage;
import java.io.*; import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.*; import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.poi.poifs.common.POIFSConstants; 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) * @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 byte[] _data;
private static final byte _default_fill = ( byte ) 0xff; 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 = private static final int _blocks_per_big_block =
POIFSConstants.BIG_BLOCK_SIZE / _block_size; POIFSConstants.BIG_BLOCK_SIZE / _block_size;
@ -178,46 +180,10 @@ public class SmallDocumentBlock
return sdbs; return sdbs;
} }
/** public static DataInputBlock getDataInputBlock(SmallDocumentBlock[] blocks, int offset) {
* read data from an array of SmallDocumentBlocks int firstBlockIndex = offset >> BLOCK_SHIFT;
* int firstBlockOffset= offset & BLOCK_MASK;
* @param blocks the blocks to read from return new DataInputBlock(blocks[firstBlockIndex]._data, firstBlockOffset);
* @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);
}
} }
/** /**

View File

@ -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:
* <ul>
* <li> if possible, operations designated 'optional' in the List
* interface are attempted
* <li> wherever the List interface refers to an Object, substitute
* double
* <li> wherever the List interface refers to a Collection or List,
* substitute DoubleList
* </ul>
*
* the mimicry is not perfect, however:
* <ul>
* <li> operations involving Iterators or ListIterators are not
* supported
* <li> remove(Object) becomes removeValue to distinguish it from
* remove(int index)
* <li> subList is not supported
* </ul>
*
* @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:
*
* <code>
* hashCode = 1;
* Iterator i = list.iterator();
* while (i.hasNext()) {
* Object obj = i.next();
* hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode());
* }
* </code>
*
* 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

View File

@ -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);
}
}

View File

@ -28,13 +28,12 @@ import java.io.InputStream;
*@author Marc Johnson (mjohnson at apache dot org) *@author Marc Johnson (mjohnson at apache dot org)
*@author Andrew Oliver (acoliver 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() { private LittleEndian() {
// no instances of this class // no instances of this class
} }
/** /**
* get a short value from a byte array * 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 *@param offset a starting offset into the byte array
*@return the short (16-bit) value *@return the short (16-bit) value
*/ */
public static short getShort(byte[] data, int offset) {
public static short getShort(final byte[] data, final int offset) { int b0 = data[offset] & 0xFF;
return (short) getNumber(data, offset, SHORT_SIZE); int b1 = data[offset+1] & 0xFF;
return (short) ((b1 << 8) + (b0 << 0));
} }
@ -55,49 +55,11 @@ public final class LittleEndian implements LittleEndianConsts {
*@param offset a starting offset into the byte array *@param offset a starting offset into the byte array
*@return the unsigned short (16-bit) value in an integer *@return the unsigned short (16-bit) value in an integer
*/ */
public static int getUShort(final byte[] data, final int offset) { public static int getUShort(byte[] data, int offset) {
short num = (short) getNumber(data, offset, SHORT_SIZE); int b0 = data[offset] & 0xFF;
int retNum; int b1 = data[offset+1] & 0xFF;
if (num < 0) { return (b1 << 8) + (b0 << 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;
}
/** /**
* get a short value from the beginning of a byte array * get a short value from the beginning of a byte array
@ -105,23 +67,20 @@ public final class LittleEndian implements LittleEndianConsts {
*@param data the byte array *@param data the byte array
*@return the short (16-bit) value *@return the short (16-bit) value
*/ */
public static short getShort(byte[] data) {
public static short getShort(final byte[] data) {
return getShort(data, 0); return getShort(data, 0);
} }
/** /**
* get an unsigned short value from the beginning of a byte array * get an unsigned short value from the beginning of a byte array
* *
*@param data the byte array *@param data the byte array
*@return the unsigned short (16-bit) value in an int *@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); return getUShort(data, 0);
} }
/** /**
* get an int value from a byte array * 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 *@param offset a starting offset into the byte array
*@return the int (32-bit) value *@return the int (32-bit) value
*/ */
public static int getInt(byte[] data, int offset) {
public static int getInt(final byte[] data, final int offset) { int i=offset;
return (int) getNumber(data, offset, INT_SIZE); 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);
} }
@ -141,8 +104,7 @@ public final class LittleEndian implements LittleEndianConsts {
*@param data the byte array *@param data the byte array
*@return the int (32-bit) value *@return the int (32-bit) value
*/ */
public static int getInt(byte[] data) {
public static int getInt(final byte[] data) {
return getInt(data, 0); return getInt(data, 0);
} }
@ -154,15 +116,9 @@ public final class LittleEndian implements LittleEndianConsts {
*@param offset a starting offset into the byte array *@param offset a starting offset into the byte array
*@return the unsigned int (32-bit) value in a long *@return the unsigned int (32-bit) value in a long
*/ */
public static long getUInt(final byte[] data, final int offset) { public static long getUInt(byte[] data, int offset) {
int num = (int) getNumber(data, offset, INT_SIZE); long retNum = getInt(data, offset);
long retNum; return retNum & 0x00FFFFFFFF;
if (num < 0) {
retNum = ((long) Integer.MAX_VALUE + 1) * 2 + num;
} else {
retNum = num;
}
return retNum;
} }
/** /**
@ -171,7 +127,7 @@ public final class LittleEndian implements LittleEndianConsts {
*@param data the byte array *@param data the byte array
*@return the unsigned int (32-bit) value in a long *@return the unsigned int (32-bit) value in a long
*/ */
public static long getUInt(final byte[] data) { public static long getUInt(byte[] data) {
return getUInt(data,0); return getUInt(data,0);
} }
@ -182,24 +138,16 @@ public final class LittleEndian implements LittleEndianConsts {
*@param offset a starting offset into the byte array *@param offset a starting offset into the byte array
*@return the long (64-bit) value *@return the long (64-bit) value
*/ */
public static long getLong(byte[] data, int offset) {
long result = 0;
public static long getLong(final byte[] data, final int offset) { for (int j = offset + LONG_SIZE - 1; j >= offset; j--) {
return getNumber(data, offset, LONG_SIZE); result <<= 8;
result |= 0xff & data[j];
} }
return result;
/**
* 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);
} }
/** /**
* get a double value from a byte array, reads it in little endian format * get a double value from a byte array, reads it in little endian format
* then converts the resulting revolting IEEE 754 (curse them) floating * 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 *@param offset a starting offset into the byte array
*@return the double (64-bit) value *@return the double (64-bit) value
*/ */
public static double getDouble(byte[] data, int offset) {
public static double getDouble(final byte[] data, final int offset) { return Double.longBitsToDouble(getLong(data, 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);
}
/** /**
* put a short value into a byte array * 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 offset a starting offset into the byte array
*@param value the short (16-bit) value *@param value the short (16-bit) value
*/ */
public static void putShort(final byte[] data, final int offset, public static void putShort(byte[] data, int offset, short value) {
final short value) { int i = offset;
putNumber(data, offset, value, SHORT_SIZE); 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 * Added for consistency with other put~() methods
*/ */
public static void putByte(byte[] data, int offset, int value) { 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 * @exception ArrayIndexOutOfBoundsException may be thrown
*/ */
public static void putUShort(final byte[] data, final int offset, public static void putUShort(byte[] data, int offset, int value) {
final int value) int i = offset;
{ data[i++] = (byte)((value >>> 0) & 0xFF);
putNumber(data, offset, value, SHORT_SIZE); data[i++] = (byte)((value >>> 8) & 0xFF);
} }
/** /**
@ -271,8 +206,7 @@ public final class LittleEndian implements LittleEndianConsts {
*@param data the byte array *@param data the byte array
*@param value the short (16-bit) value *@param value the short (16-bit) value
*/ */
public static void putShort(byte[] data, short value) {
public static void putShort(final byte[] data, final short value) {
putShort(data, 0, 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 offset a starting offset into the byte array
*@param value the int (32-bit) value *@param value the int (32-bit) value
*/ */
public static void putInt(byte[] data, int offset, int value) {
public static void putInt(final byte[] data, final int offset, int i = offset;
final int value) { data[i++] = (byte)((value >>> 0) & 0xFF);
putNumber(data, offset, value, INT_SIZE); 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 data the byte array
*@param value the int (32-bit) value *@param value the int (32-bit) value
*/ */
public static void putInt(byte[] data, int value) {
public static void putInt(final byte[] data, final int value) {
putInt(data, 0, 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 offset a starting offset into the byte array
*@param value the long (64-bit) value *@param value the long (64-bit) value
*/ */
public static void putLong(byte[] data, int offset, long value) {
int limit = LONG_SIZE + offset;
long v = value;
public static void putLong(final byte[] data, final int offset, for (int j = offset; j < limit; j++) {
final long value) { data[j] = (byte) (v & 0xFF);
putNumber(data, offset, value, LONG_SIZE); v >>= 8;
} }
/**
* 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);
} }
@ -336,26 +263,13 @@ public final class LittleEndian implements LittleEndianConsts {
*@param offset a starting offset into the byte array *@param offset a starting offset into the byte array
*@param value the double (64-bit) value *@param value the double (64-bit) value
*/ */
public static void putDouble(byte[] data, int offset, double value) {
public static void putDouble(final byte[] data, final int offset,
final double value) {
// Excel likes NaN to be a specific value. // Excel likes NaN to be a specific value.
if (Double.isNaN(value)) if (Double.isNaN(value)) {
putNumber(data, offset, -276939487313920L, DOUBLE_SIZE); putLong(data, offset, -276939487313920L);
else } else {
putNumber(data, offset, Double.doubleToLongBits(value), DOUBLE_SIZE); putLong(data, offset, Double.doubleToLongBits(value));
} }
/**
* 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);
} }
@ -364,7 +278,6 @@ public final class LittleEndian implements LittleEndianConsts {
* *
*@author Marc Johnson (mjohnson at apache dot org) *@author Marc Johnson (mjohnson at apache dot org)
*/ */
public static final class BufferUnderrunException extends IOException { public static final class BufferUnderrunException extends IOException {
BufferUnderrunException() { BufferUnderrunException() {
@ -376,12 +289,10 @@ public final class LittleEndian implements LittleEndianConsts {
/** /**
* get a short value from an InputStream * get a short value from an InputStream
* *
*@param stream the InputStream from which the short *@param stream the InputStream from which the short is to be read
* is to be read
*@return the short (16-bit) value *@return the short (16-bit) value
*@exception IOException will be propagated back to the caller *@exception IOException will be propagated back to the caller
*@exception BufferUnderrunException if the stream cannot provide enough *@exception BufferUnderrunException if the stream cannot provide enough bytes
* bytes
*/ */
public static short readShort(InputStream stream) throws IOException, BufferUnderrunException { public static short readShort(InputStream stream) throws IOException, BufferUnderrunException {
@ -395,21 +306,19 @@ public final class LittleEndian implements LittleEndianConsts {
if ((ch1 | ch2) < 0) { if ((ch1 | ch2) < 0) {
throw new BufferUnderrunException(); throw new BufferUnderrunException();
} }
return ((ch2 << 8) + (ch1 << 0)); return (ch2 << 8) + (ch1 << 0);
} }
/** /**
* get an int value from an InputStream * get an int value from an InputStream
* *
*@param stream the InputStream from which the int is *@param stream the InputStream from which the int is to be read
* to be read * @return the int (32-bit) value
*@return the int (32-bit) value * @exception IOException will be propagated back to the caller
*@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 int readInt(final InputStream stream) public static int readInt(InputStream stream)
throws IOException, BufferUnderrunException { throws IOException, BufferUnderrunException {
int ch1 = stream.read(); int ch1 = stream.read();
int ch2 = stream.read(); int ch2 = stream.read();
@ -418,22 +327,19 @@ public final class LittleEndian implements LittleEndianConsts {
if ((ch1 | ch2 | ch3 | ch4) < 0) { if ((ch1 | ch2 | ch3 | ch4) < 0) {
throw new BufferUnderrunException(); throw new BufferUnderrunException();
} }
return ((ch4 << 24) + (ch3<<16) + (ch2 << 8) + (ch1 << 0)); return (ch4 << 24) + (ch3<<16) + (ch2 << 8) + (ch1 << 0);
} }
/** /**
* get a long value from an InputStream * get a long value from an InputStream
* *
*@param stream the InputStream from which the long * @param stream the InputStream from which the long is to be read
* is to be read * @return the long (64-bit) value
*@return the long (64-bit) value * @exception IOException will be propagated back to the caller
*@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 long readLong(InputStream stream)
public static long readLong(final InputStream stream)
throws IOException, BufferUnderrunException { throws IOException, BufferUnderrunException {
int ch1 = stream.read(); int ch1 = stream.read();
int ch2 = stream.read(); int ch2 = stream.read();
@ -458,114 +364,44 @@ public final class LittleEndian implements LittleEndianConsts {
(ch1 << 0); (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 * Convert an 'unsigned' byte to an integer. ie, don't carry across the
* sign. * sign.
* *
*@param b Description of the Parameter * @param b Description of the Parameter
*@return Description of the Return Value * @return Description of the Return Value
*/ */
public static int ubyteToInt(byte b) { 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. * get the unsigned value of a byte.
* *
*@param data the byte array. * @param data the byte array.
*@param offset a starting offset into the byte array. * @param offset a starting offset into the byte array.
*@return the unsigned value of the byte as a 32 bit integer * @return the unsigned value of the byte as a 32 bit integer
*/ */
public static int getUnsignedByte(final byte[] data, final int offset) { public static int getUnsignedByte(byte[] data, int offset) {
return (int) getNumber(data, offset, BYTE_SIZE); return data[offset] & 0xFF;
}
/**
* 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);
} }
/** /**
* Copy a portion of a byte array * Copy a portion of a byte array
* *
*@param data the original byte array * @param data the original byte array
*@param offset Where to start copying from. * @param offset Where to start copying from.
*@param size Number of bytes to copy. * @param size Number of bytes to copy.
*@return The byteArray value * @return The byteArray value
*@throws IndexOutOfBoundsException - if copying would cause access of * @throws IndexOutOfBoundsException - if copying would cause access of
* data outside array bounds. * 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]; byte[] copy = new byte[size];
System.arraycopy(data, offset, copy, 0, size); System.arraycopy(data, offset, copy, 0, size);
return copy; return copy;
} }
/**
* <p>Gets an unsigned int value (8 bytes) from a byte array.</p>
*
* @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;
}
} }

View File

@ -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());
}
}

View File

@ -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;
}
}

View File

@ -21,6 +21,7 @@ package org.apache.poi.util;
* @author Josh Micich * @author Josh Micich
*/ */
public interface LittleEndianInput { public interface LittleEndianInput {
int available();
byte readByte(); byte readByte();
int readUByte(); int readUByte();
short readShort(); short readShort();
@ -30,6 +31,4 @@ public interface LittleEndianInput {
double readDouble(); double readDouble();
void readFully(byte[] buf); void readFully(byte[] buf);
void readFully(byte[] buf, int off, int len); void readFully(byte[] buf, int off, int len);
String readUnicodeLEString(int nChars);
String readCompressedUnicode(int nChars);
} }

View File

@ -22,6 +22,10 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
/** /**
* Wraps an {@link InputStream} providing {@link LittleEndianInput}<p/>
*
* 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 * @author Josh Micich
*/ */
@ -29,7 +33,13 @@ public class LittleEndianInputStream extends FilterInputStream implements Little
public LittleEndianInputStream(InputStream is) { public LittleEndianInputStream(InputStream is) {
super(is); super(is);
} }
public int available() {
try {
return super.available();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public byte readByte() { public byte readByte() {
return (byte)readUByte(); return (byte)readUByte();
} }
@ -131,34 +141,4 @@ public class LittleEndianInputStream extends FilterInputStream implements Little
buf[i] = (byte) ch; 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);
}
} }

View File

@ -20,9 +20,14 @@ package org.apache.poi.util;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.text.FieldPosition; import java.text.FieldPosition;
import java.text.NumberFormat; 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<p/>
* *
* 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 Andrew C. Oliver
*@author Sergei Kozello (sergeikozello at mail.ru) *@author Sergei Kozello (sergeikozello at mail.ru)
@ -84,64 +89,11 @@ public class StringUtil {
* @param string the byte array to be converted * @param string the byte array to be converted
* @return the converted string * @return the converted string
*/ */
public static String getFromUnicodeLE(final byte[] string) { public static String getFromUnicodeLE(byte[] string) {
if(string.length == 0) { return ""; } if(string.length == 0) { return ""; }
return getFromUnicodeLE(string, 0, string.length / 2); 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 * Read 8 bit data (in ISO-8859-1 codepage) into a (unicode) Java
* String and return. * String and return.
@ -163,6 +115,52 @@ public class StringUtil {
throw new RuntimeException(e); 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 <tt>in</tt> is expected to contain:
* <ol>
* <li>ushort nChars</li>
* <li>byte is16BitFlag</li>
* <li>byte[]/char[] characterData</li>
* </ol>
* 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 <tt>out</tt> will get:
* <ol>
* <li>ushort nChars</li>
* <li>byte is16BitFlag</li>
* <li>byte[]/char[] characterData</li>
* </ol>
* 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 * Takes a unicode (java) string, and returns it as 8 bit data (in ISO-8859-1
@ -221,6 +219,14 @@ public class StringUtil {
out.write(bytes); 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. * Apply printf() like formatting to a string.
* Primarily used for logging. * Primarily used for logging.

View File

@ -100,6 +100,7 @@ public final class AllRecordTests {
result.addTestSuite(TestSheetPropertiesRecord.class); result.addTestSuite(TestSheetPropertiesRecord.class);
result.addTestSuite(TestSharedFormulaRecord.class); result.addTestSuite(TestSharedFormulaRecord.class);
result.addTestSuite(TestStringRecord.class); result.addTestSuite(TestStringRecord.class);
result.addTestSuite(TestStyleRecord.class);
result.addTestSuite(TestSubRecord.class); result.addTestSuite(TestSubRecord.class);
result.addTestSuite(TestSupBookRecord.class); result.addTestSuite(TestSupBookRecord.class);
result.addTestSuite(TestTableRecord.class); result.addTestSuite(TestTableRecord.class);

View File

@ -37,7 +37,7 @@ public final class TestCommonObjectDataSubRecord extends TestCase {
}; };
public void testLoad() { 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( CommonObjectDataSubRecord.OBJECT_TYPE_LIST_BOX, record.getObjectType());
assertEquals((short) 1, record.getObjectId()); assertEquals((short) 1, record.getObjectId());

View File

@ -14,11 +14,12 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.hssf.record; package org.apache.poi.hssf.record;
import java.io.ByteArrayInputStream;
import java.util.Arrays; import java.util.Arrays;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.util.HexRead; import org.apache.poi.util.HexRead;
@ -37,33 +38,43 @@ public final class TestEmbeddedObjectRefSubRecord extends TestCase {
public void testStore() { public void testStore() {
byte[] src = hr(data1); 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)); RecordInputStream in = TestcaseRecordInputStream.create(EmbeddedObjectRefSubRecord.sid, src);
in.nextRecord();
EmbeddedObjectRefSubRecord record1 = new EmbeddedObjectRefSubRecord(in, src.length-4); EmbeddedObjectRefSubRecord record1 = new EmbeddedObjectRefSubRecord(in, src.length);
byte[] ser = record1.serialize(); byte[] ser = record1.serialize();
RecordInputStream in2 = new RecordInputStream(new ByteArrayInputStream(ser)); RecordInputStream in2 = TestcaseRecordInputStream.create(ser);
in2.nextRecord();
EmbeddedObjectRefSubRecord record2 = new EmbeddedObjectRefSubRecord(in2, ser.length-4); EmbeddedObjectRefSubRecord record2 = new EmbeddedObjectRefSubRecord(in2, ser.length-4);
assertTrue(Arrays.equals(src, ser)); confirmData(src, ser);
assertEquals(record1.getOLEClassName(), record2.getOLEClassName()); assertEquals(record1.getOLEClassName(), record2.getOLEClassName());
byte[] ser2 = record1.serialize(); byte[] ser2 = record1.serialize();
assertTrue(Arrays.equals(ser, ser2)); 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() { public void testCreate() {
EmbeddedObjectRefSubRecord record1 = new EmbeddedObjectRefSubRecord(); EmbeddedObjectRefSubRecord record1 = new EmbeddedObjectRefSubRecord();
byte[] ser = record1.serialize(); byte[] ser = record1.serialize();
RecordInputStream in2 = new RecordInputStream(new ByteArrayInputStream(ser)); RecordInputStream in2 = TestcaseRecordInputStream.create(ser);
in2.nextRecord();
EmbeddedObjectRefSubRecord record2 = new EmbeddedObjectRefSubRecord(in2, ser.length-4); EmbeddedObjectRefSubRecord record2 = new EmbeddedObjectRefSubRecord(in2, ser.length-4);
assertEquals(record1.getOLEClassName(), record2.getOLEClassName()); 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). * taken from ftPictFmla sub-record in attachment 22645 (offset 0x40AB).
*/ */
private static final byte[] data45912 = hr( private static final byte[] data45912 = hr(
"09 00 14 00 " +
"12 00 0B 00 F8 02 88 04 3B 00 " + "12 00 0B 00 F8 02 88 04 3B 00 " +
"00 00 00 01 00 00 00 01 " + "00 00 00 01 00 00 00 01 " +
"00 00"); "00 00");
public void testCameraTool_bug45912() { public void testCameraTool_bug45912() {
byte[] data = data45912; byte[] data = data45912;
RecordInputStream in = new RecordInputStream(new ByteArrayInputStream(data)); RecordInputStream in = TestcaseRecordInputStream.create(EmbeddedObjectRefSubRecord.sid, data);
in.nextRecord();
EmbeddedObjectRefSubRecord rec = new EmbeddedObjectRefSubRecord(in, data.length-4); EmbeddedObjectRefSubRecord rec = new EmbeddedObjectRefSubRecord(in, data.length);
byte[] ser2 = rec.serialize(); byte[] ser2 = rec.serialize();
assertTrue(Arrays.equals(data, ser2)); confirmData(data, ser2);
} }
private static byte[] hr(String string) { private static byte[] hr(String string) {

View File

@ -119,7 +119,6 @@ public final class TestFormulaRecord extends TestCase {
public void testWithConcat() { public void testWithConcat() {
// =CHOOSE(2,A2,A3,A4) // =CHOOSE(2,A2,A3,A4)
byte[] data = { byte[] data = {
6, 0, 68, 0,
1, 0, 1, 0, 15, 0, 0, 0, 0, 0, 0, 0, 57, 1, 0, 1, 0, 15, 0, 0, 0, 0, 0, 0, 0, 57,
64, 0, 0, 12, 0, 12, -4, 46, 0, 64, 0, 0, 12, 0, 12, -4, 46, 0,
30, 2, 0, // Int - 2 30, 2, 0, // Int - 2
@ -134,8 +133,7 @@ public final class TestFormulaRecord extends TestCase {
25, 8, 3, 0, // Attr 25, 8, 3, 0, // Attr
66, 4, 100, 0 // CHOOSE 66, 4, 100, 0 // CHOOSE
}; };
RecordInputStream inp = new RecordInputStream( new ByteArrayInputStream(data)); RecordInputStream inp = TestcaseRecordInputStream.create(FormatRecord.sid, data);
inp.nextRecord();
FormulaRecord fr = new FormulaRecord(inp); FormulaRecord fr = new FormulaRecord(inp);

View File

@ -239,8 +239,7 @@ public final class TestHyperlinkRecord extends TestCase {
RecordInputStream is = TestcaseRecordInputStream.create(HyperlinkRecord.sid, data); RecordInputStream is = TestcaseRecordInputStream.create(HyperlinkRecord.sid, data);
HyperlinkRecord link = new HyperlinkRecord(is); HyperlinkRecord link = new HyperlinkRecord(is);
byte[] bytes1 = link.serialize(); byte[] bytes1 = link.serialize();
is = new RecordInputStream(new ByteArrayInputStream(bytes1)); is = TestcaseRecordInputStream.create(bytes1);
is.nextRecord();
link = new HyperlinkRecord(is); link = new HyperlinkRecord(is);
byte[] bytes2 = link.serialize(); byte[] bytes2 = link.serialize();
assertEquals(bytes1.length, bytes2.length); assertEquals(bytes1.length, bytes2.length);

View File

@ -23,6 +23,7 @@ import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.Ptg; import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.RefPtg; import org.apache.poi.hssf.record.formula.RefPtg;
import org.apache.poi.util.LittleEndianInput;
/** /**
* @author Josh Micich * @author Josh Micich
@ -59,7 +60,7 @@ public final class TestSharedFormulaRecord extends TestCase {
*/ */
public void testConvertSharedFormulasOperandClasses_bug45123() { 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(); int encodedLen = in.readUShort();
Ptg[] sharedFormula = Ptg.readTokens(encodedLen, in); Ptg[] sharedFormula = Ptg.readTokens(encodedLen, in);

View File

@ -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()); // "<Conventional>_Sheet1"
}
}

View File

@ -55,11 +55,9 @@ public final class TestTextObjectBaseRecord extends TestCase {
public void testLoad() { public void testLoad() {
RecordInputStream in = new RecordInputStream(new ByteArrayInputStream(data)); RecordInputStream in = TestcaseRecordInputStream.create(data);
in.nextRecord();
TextObjectRecord record = new TextObjectRecord(in); TextObjectRecord record = new TextObjectRecord(in);
assertEquals(TextObjectRecord.HORIZONTAL_TEXT_ALIGNMENT_CENTERED, record.getHorizontalTextAlignment()); assertEquals(TextObjectRecord.HORIZONTAL_TEXT_ALIGNMENT_CENTERED, record.getHorizontalTextAlignment());
assertEquals(TextObjectRecord.VERTICAL_TEXT_ALIGNMENT_JUSTIFY, record.getVerticalTextAlignment()); assertEquals(TextObjectRecord.VERTICAL_TEXT_ALIGNMENT_JUSTIFY, record.getVerticalTextAlignment());
assertEquals(true, record.isTextLocked()); assertEquals(true, record.isTextLocked());

View File

@ -52,8 +52,7 @@ public final class TestTextObjectRecord extends TestCase {
public void testRead() { public void testRead() {
RecordInputStream is = new RecordInputStream(new ByteArrayInputStream(simpleData)); RecordInputStream is =TestcaseRecordInputStream.create(simpleData);
is.nextRecord();
TextObjectRecord record = new TextObjectRecord(is); TextObjectRecord record = new TextObjectRecord(is);
assertEquals(TextObjectRecord.sid, record.getSid()); assertEquals(TextObjectRecord.sid, record.getSid());
@ -79,8 +78,7 @@ public final class TestTextObjectRecord extends TestCase {
assertTrue(Arrays.equals(simpleData, ser)); assertTrue(Arrays.equals(simpleData, ser));
//read again //read again
RecordInputStream is = new RecordInputStream(new ByteArrayInputStream(simpleData)); RecordInputStream is = TestcaseRecordInputStream.create(simpleData);
is.nextRecord();
record = new TextObjectRecord(is); record = new TextObjectRecord(is);
} }
@ -101,8 +99,7 @@ public final class TestTextObjectRecord extends TestCase {
assertEquals(22, ser.length); // just the TXO record assertEquals(22, ser.length); // just the TXO record
//read again //read again
RecordInputStream is = new RecordInputStream(new ByteArrayInputStream(ser)); RecordInputStream is = TestcaseRecordInputStream.create(ser);
is.nextRecord();
record = new TextObjectRecord(is); record = new TextObjectRecord(is);
assertEquals(0, record.getStr().length()); assertEquals(0, record.getStr().length());
} }

View File

@ -23,6 +23,8 @@ import java.io.InputStream;
import junit.framework.Assert; import junit.framework.Assert;
import org.apache.poi.util.LittleEndian; 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 * 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 * Prepends a mock record identifier to the supplied data and opens a record input stream
*/ */
public static RecordInputStream createWithFakeSid(byte[] data) { public static LittleEndianInput createLittleEndian(byte[] data) {
return create(-5555, data); return new LittleEndianByteArrayInputStream(data);
} }
public static RecordInputStream create(int sid, byte[] data) { public static RecordInputStream create(int sid, byte[] data) {

View File

@ -21,11 +21,12 @@ import java.util.Arrays;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.hssf.record.TestcaseRecordInputStream; import org.apache.poi.hssf.record.TestcaseRecordInputStream;
import org.apache.poi.hssf.record.UnicodeString; import org.apache.poi.hssf.record.UnicodeString;
import org.apache.poi.hssf.usermodel.HSSFErrorConstants; import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
import org.apache.poi.util.HexRead; import org.apache.poi.util.HexRead;
import org.apache.poi.util.LittleEndianByteArrayOutputStream;
import org.apache.poi.util.LittleEndianInput;
/** /**
* *
* @author Josh Micich * @author Josh Micich
@ -52,14 +53,15 @@ public final class TestConstantValueParser extends TestCase {
public void testEncode() { public void testEncode() {
int size = ConstantValueParser.getEncodedSize(SAMPLE_VALUES); int size = ConstantValueParser.getEncodedSize(SAMPLE_VALUES);
byte[] data = new byte[size]; 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)) { if (!Arrays.equals(data, SAMPLE_ENCODING)) {
fail("Encoding differs"); fail("Encoding differs");
} }
} }
public void testDecode() { public void testDecode() {
RecordInputStream in = TestcaseRecordInputStream.createWithFakeSid(SAMPLE_ENCODING); LittleEndianInput in = TestcaseRecordInputStream.createLittleEndian(SAMPLE_ENCODING);
Object[] values = ConstantValueParser.parse(in, 4); Object[] values = ConstantValueParser.parse(in, 4);
for (int i = 0; i < values.length; i++) { for (int i = 0; i < values.length; i++) {

View File

@ -24,6 +24,8 @@ import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.hssf.record.TestcaseRecordInputStream; import org.apache.poi.hssf.record.TestcaseRecordInputStream;
import org.apache.poi.hssf.record.UnicodeString; import org.apache.poi.hssf.record.UnicodeString;
import org.apache.poi.hssf.usermodel.HSSFWorkbook; 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.AssertionFailedError;
import junit.framework.TestCase; import junit.framework.TestCase;
@ -54,9 +56,9 @@ public final class TestArrayPtg extends TestCase {
*/ */
public void testReadWriteTokenValueBytes() { 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(3, ptg.getColumnCount());
assertEquals(2, ptg.getRowCount()); assertEquals(2, ptg.getRowCount());
Object[][] values = ptg.getTokenArrayValues(); Object[][] values = ptg.getTokenArrayValues();
@ -70,7 +72,7 @@ public final class TestArrayPtg extends TestCase {
assertEquals(new UnicodeString("FG"), values[1][2]); assertEquals(new UnicodeString("FG"), values[1][2]);
byte[] outBuf = new byte[ENCODED_CONSTANT_DATA.length]; byte[] outBuf = new byte[ENCODED_CONSTANT_DATA.length];
ptg.writeTokenValueBytes(outBuf, 0); ptg.writeTokenValueBytes(new LittleEndianByteArrayOutputStream(outBuf, 0));
if(outBuf[0] == 4) { if(outBuf[0] == 4) {
throw new AssertionFailedError("Identified bug 42564b"); 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. * Excel stores array elements column by column. This test makes sure POI does the same.
*/ */
public void testElementOrdering() { public void testElementOrdering() {
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(3, ptg.getColumnCount());
assertEquals(2, ptg.getRowCount()); assertEquals(2, ptg.getRowCount());
@ -113,9 +115,9 @@ public final class TestArrayPtg extends TestCase {
} }
public void testToFormulaString() { 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; String actualFormula;
try { try {
@ -146,7 +148,7 @@ public final class TestArrayPtg extends TestCase {
// Force encoded operand class for tArray // Force encoded operand class for tArray
fullData[0] = (byte) (ArrayPtg.sid + operandClass); 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); Ptg[] ptgs = Ptg.readTokens(ENCODED_PTG_DATA.length, in);
assertEquals(1, ptgs.length); assertEquals(1, ptgs.length);

View File

@ -21,9 +21,9 @@ import java.util.Arrays;
import junit.framework.AssertionFailedError; import junit.framework.AssertionFailedError;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.hssf.record.TestcaseRecordInputStream; import org.apache.poi.hssf.record.TestcaseRecordInputStream;
import org.apache.poi.util.HexRead; import org.apache.poi.util.HexRead;
import org.apache.poi.util.LittleEndianInput;
/** /**
* Tests for {@link AttrPtg}. * Tests for {@link AttrPtg}.
@ -37,7 +37,7 @@ public final class TestAttrPtg extends AbstractPtgTestCase {
*/ */
public void testReserializeAttrChoose() { public void testReserializeAttrChoose() {
byte[] data = HexRead.readFromString("19, 04, 03, 00, 08, 00, 11, 00, 1A, 00, 23, 00"); 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); Ptg[] ptgs = Ptg.readTokens(data.length, in);
byte[] data2 = new byte[data.length]; byte[] data2 = new byte[data.length];
try { try {

View File

@ -34,7 +34,7 @@ public final class TestFuncPtg extends TestCase {
0, 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( "Len formula index is not 32(20H)", 0x20, ptg.getFunctionIndex() );
assertEquals( "Number of operands in the len formula", 1, ptg.getNumberOfOperands() ); assertEquals( "Number of operands in the len formula", 1, ptg.getNumberOfOperands() );
assertEquals( "Function Name", "LEN", ptg.getName() ); assertEquals( "Function Name", "LEN", ptg.getName() );

View File

@ -23,10 +23,10 @@ import junit.framework.AssertionFailedError;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.hssf.HSSFTestDataSamples; 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.record.TestcaseRecordInputStream;
import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.util.LittleEndianInput;
/** /**
* Tests for {@link RefPtg}. * Tests for {@link RefPtg}.
@ -94,7 +94,7 @@ public final class TestReferencePtg extends TestCase {
0x2C, 33, 44, 55, 66, 0x2C, 33, 44, 55, 66,
}; };
public void testReadWrite_tRefN_bug45091() { 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); Ptg[] ptgs = Ptg.readTokens(tRefN_data.length, in);
byte[] outData = new byte[5]; byte[] outData = new byte[5];
Ptg.serializePtgs(ptgs, outData, 0); Ptg.serializePtgs(ptgs, outData, 0);

View File

@ -1,4 +1,3 @@
/* ==================================================================== /* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
@ -16,17 +15,15 @@
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.poifs.filesystem; package org.apache.poi.poifs.filesystem;
import java.io.*; import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.*; import junit.framework.TestCase;
import junit.framework.*;
import org.apache.poi.poifs.property.DirectoryProperty; import org.apache.poi.poifs.property.DirectoryProperty;
import org.apache.poi.poifs.property.DocumentProperty;
import org.apache.poi.poifs.storage.RawDataBlock; import org.apache.poi.poifs.storage.RawDataBlock;
/** /**
@ -35,22 +32,9 @@ import org.apache.poi.poifs.storage.RawDataBlock;
* @author Marc Johnson * @author Marc Johnson
*/ */
public class TestDocumentInputStream public final class TestDocumentInputStream extends TestCase {
extends TestCase
{
/** protected void setUp() throws Exception {
* Constructor TestDocumentInputStream
*
* @param name
*
* @exception IOException
*/
public TestDocumentInputStream(String name)
throws IOException
{
super(name);
int blocks = (_workbook_size + 511) / 512; int blocks = (_workbook_size + 511) / 512;
_workbook_data = new byte[ 512 * blocks ]; _workbook_data = new byte[ 512 * blocks ];
@ -86,13 +70,8 @@ public class TestDocumentInputStream
/** /**
* test constructor * test constructor
*
* @exception IOException
*/ */
public void testConstructor() throws IOException {
public void testConstructor()
throws IOException
{
DocumentInputStream stream = new DocumentInputStream(_workbook); DocumentInputStream stream = new DocumentInputStream(_workbook);
assertEquals(_workbook_size, stream.available()); assertEquals(_workbook_size, stream.available());
@ -100,13 +79,8 @@ public class TestDocumentInputStream
/** /**
* test available() behavior * test available() behavior
*
* @exception IOException
*/ */
public void testAvailable() throws IOException {
public void testAvailable()
throws IOException
{
DocumentInputStream stream = new DocumentInputStream(_workbook); DocumentInputStream stream = new DocumentInputStream(_workbook);
assertEquals(_workbook_size, stream.available()); assertEquals(_workbook_size, stream.available());
@ -115,9 +89,7 @@ public class TestDocumentInputStream
{ {
stream.available(); stream.available();
fail("Should have caught IOException"); fail("Should have caught IOException");
} } catch (IllegalStateException ignored) {
catch (IOException ignored)
{
// as expected // as expected
} }
@ -125,13 +97,8 @@ public class TestDocumentInputStream
/** /**
* test mark/reset/markSupported. * test mark/reset/markSupported.
*
* @exception IOException
*/ */
public void testMarkFunctions() throws IOException {
public void testMarkFunctions()
throws IOException
{
DocumentInputStream stream = new DocumentInputStream(_workbook); DocumentInputStream stream = new DocumentInputStream(_workbook);
byte[] buffer = new byte[ _workbook_size / 5 ]; byte[] buffer = new byte[ _workbook_size / 5 ];
@ -169,13 +136,8 @@ public class TestDocumentInputStream
/** /**
* test simple read method * test simple read method
*
* @exception IOException
*/ */
public void testReadSingleByte() throws IOException {
public void testReadSingleByte()
throws IOException
{
DocumentInputStream stream = new DocumentInputStream(_workbook); DocumentInputStream stream = new DocumentInputStream(_workbook);
int remaining = _workbook_size; int remaining = _workbook_size;
@ -205,13 +167,8 @@ public class TestDocumentInputStream
/** /**
* Test buffered read * Test buffered read
*
* @exception IOException
*/ */
public void testBufferRead() throws IOException {
public void testBufferRead()
throws IOException
{
DocumentInputStream stream = new DocumentInputStream(_workbook); DocumentInputStream stream = new DocumentInputStream(_workbook);
try try
@ -275,23 +232,14 @@ public class TestDocumentInputStream
/** /**
* Test complex buffered read * Test complex buffered read
*
* @exception IOException
*/ */
public void testComplexBufferRead() throws IOException {
public void testComplexBufferRead()
throws IOException
{
DocumentInputStream stream = new DocumentInputStream(_workbook); DocumentInputStream stream = new DocumentInputStream(_workbook);
try try {
{
stream.read(null, 0, 1); stream.read(null, 0, 1);
fail("Should have caught NullPointerException"); fail("Should have caught NullPointerException");
} } catch (IllegalArgumentException ignored) {
catch (NullPointerException ignored)
{
// as expected // as expected
} }
@ -391,13 +339,8 @@ public class TestDocumentInputStream
/** /**
* test skip * test skip
*
* @exception IOException
*/ */
public void testSkip() throws IOException {
public void testSkip()
throws IOException
{
DocumentInputStream stream = new DocumentInputStream(_workbook); DocumentInputStream stream = new DocumentInputStream(_workbook);
assertEquals(_workbook_size, stream.available()); assertEquals(_workbook_size, stream.available());
@ -422,17 +365,4 @@ public class TestDocumentInputStream
stream.skip(2 + ( long ) Integer.MAX_VALUE)); stream.skip(2 + ( long ) Integer.MAX_VALUE));
assertEquals(0, stream.available()); 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);
}
} }

View File

@ -27,7 +27,7 @@ import junit.framework.TestSuite;
public final class AllPOIFSStorageTests { public final class AllPOIFSStorageTests {
public static Test suite() { public static Test suite() {
TestSuite result = new TestSuite("Tests for org.apache.poi.poifs.storage"); TestSuite result = new TestSuite(AllPOIFSStorageTests.class.getName());
result.addTestSuite(TestBATBlock.class); result.addTestSuite(TestBATBlock.class);
result.addTestSuite(TestBlockAllocationTableReader.class); result.addTestSuite(TestBlockAllocationTableReader.class);
result.addTestSuite(TestBlockAllocationTableWriter.class); result.addTestSuite(TestBlockAllocationTableWriter.class);

View File

@ -1,4 +1,3 @@
/* ==================================================================== /* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
@ -16,24 +15,20 @@
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.poifs.storage; package org.apache.poi.poifs.storage;
import java.io.*; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.*; import junit.framework.TestCase;
import junit.framework.*;
/** /**
* Class to test DocumentBlock functionality * Class to test DocumentBlock functionality
* *
* @author Marc Johnson * @author Marc Johnson
*/ */
public final class TestDocumentBlock extends TestCase {
public class TestDocumentBlock
extends TestCase
{
static final private byte[] _testdata; static final private byte[] _testdata;
static static
@ -44,25 +39,10 @@ public class TestDocumentBlock
_testdata[ j ] = ( byte ) j; _testdata[ j ] = ( byte ) j;
} }
} }
;
/**
* Constructor TestDocumentBlock
*
* @param name
*/
public TestDocumentBlock(String name)
{
super(name);
}
/** /**
* Test the writing DocumentBlock constructor. * Test the writing DocumentBlock constructor.
*
* @exception IOException
*/ */
public void testConstructor() public void testConstructor()
throws IOException throws IOException
{ {
@ -88,46 +68,10 @@ public class TestDocumentBlock
assertEquals(_testdata.length, size); 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 * Test 'reading' constructor
*
* @exception IOException
*/ */
public void testReadingConstructor() public void testReadingConstructor()
throws IOException throws IOException
{ {
@ -164,17 +108,4 @@ public class TestDocumentBlock
assertEquals(( byte ) 0xFF, copy[ j ]); 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);
}
} }

View File

@ -1,4 +1,3 @@
/* ==================================================================== /* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
@ -16,24 +15,23 @@
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.poifs.storage; package org.apache.poi.poifs.storage;
import java.io.*; 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 java.util.*; import junit.framework.TestCase;
import junit.framework.*;
/** /**
* Class to test SmallDocumentBlock functionality * Class to test SmallDocumentBlock functionality
* *
* @author Marc Johnson * @author Marc Johnson
*/ */
public final class TestSmallDocumentBlock extends TestCase {
public class TestSmallDocumentBlock
extends TestCase
{
static final private byte[] _testdata; static final private byte[] _testdata;
static final private int _testdata_size = 2999; static final private int _testdata_size = 2999;
@ -45,25 +43,10 @@ public class TestSmallDocumentBlock
_testdata[ j ] = ( byte ) j; _testdata[ j ] = ( byte ) j;
} }
} }
;
/**
* constructor
*
* @param name
*/
public TestSmallDocumentBlock(String name)
{
super(name);
}
/** /**
* Test conversion from DocumentBlocks * Test conversion from DocumentBlocks
*
* @exception IOException
*/ */
public void testConvert1() public void testConvert1()
throws IOException throws IOException
{ {
@ -113,12 +96,7 @@ public class TestSmallDocumentBlock
/** /**
* Test conversion from byte array * Test conversion from byte array
*
* @exception IOException;
*
* @exception IOException
*/ */
public void testConvert2() public void testConvert2()
throws IOException 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 * test fill
*
* @exception IOException
*/ */
public void testFill() public void testFill()
throws IOException 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);
}
} }

View File

@ -27,12 +27,11 @@ import junit.framework.TestSuite;
public final class AllPOIUtilTests { public final class AllPOIUtilTests {
public static Test suite() { 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(TestArrayUtil.class);
result.addTestSuite(TestBinaryTree.class); result.addTestSuite(TestBinaryTree.class);
result.addTestSuite(TestBitField.class); result.addTestSuite(TestBitField.class);
result.addTestSuite(TestByteField.class); result.addTestSuite(TestByteField.class);
result.addTestSuite(TestDoubleList2d.class);
result.addTestSuite(TestHexDump.class); result.addTestSuite(TestHexDump.class);
result.addTestSuite(TestIntegerField.class); result.addTestSuite(TestIntegerField.class);
result.addTestSuite(TestIntList.class); result.addTestSuite(TestIntList.class);

View File

@ -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
}
}
}

View File

@ -97,13 +97,13 @@ public final class TestLittleEndian extends TestCase {
* test the getDouble() method * test the getDouble() method
*/ */
public void testGetDouble() { 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); 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]; byte[] data = new byte[8];
LittleEndian.putDouble(data, nan); LittleEndian.putDouble(data, 0, nan);
for ( int i = 0; i < data.length; i++ ) { for ( int i = 0; i < data.length; i++ ) {
assertEquals(data[i], _nan_double_array[i]); assertEquals(data[i], _nan_double_array[i]);
} }
@ -144,7 +144,7 @@ public final class TestLittleEndian extends TestCase {
(byte) 0x02, (byte) 0x02,
}; };
assertEquals(0xFFFFFFFFFFFFFF01L, LittleEndian.getLong(testdata)); assertEquals(0xFFFFFFFFFFFFFF01L, LittleEndian.getLong(testdata, 0));
assertEquals(0x02FFFFFFFFFFFFFFL, LittleEndian.getLong(testdata, 1)); assertEquals(0x02FFFFFFFFFFFFFFL, LittleEndian.getLong(testdata, 1));
} }
@ -194,7 +194,7 @@ public final class TestLittleEndian extends TestCase {
public void testPutDouble() { public void testPutDouble() {
byte[] received = new byte[ LittleEndian.DOUBLE_SIZE + 1 ]; 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)); assertTrue(compareByteArrays(received, _double_array, 0, LittleEndian.DOUBLE_SIZE));
LittleEndian.putDouble(received, 1, _doubles[1]); LittleEndian.putDouble(received, 1, _doubles[1]);
byte[] expected = new byte[ LittleEndian.DOUBLE_SIZE + 1 ]; byte[] expected = new byte[ LittleEndian.DOUBLE_SIZE + 1 ];
@ -224,7 +224,7 @@ public final class TestLittleEndian extends TestCase {
long testdata0 = 0xFFFFFFFFFFFFFF01L; long testdata0 = 0xFFFFFFFFFFFFFF01L;
long testdata1 = 0x02FFFFFFFFFFFFFFL; long testdata1 = 0x02FFFFFFFFFFFFFFL;
LittleEndian.putLong(received, testdata0); LittleEndian.putLong(received, 0, testdata0);
assertTrue(compareByteArrays(received, expected, 0, LittleEndian.LONG_SIZE)); assertTrue(compareByteArrays(received, expected, 0, LittleEndian.LONG_SIZE));
LittleEndian.putLong(received, 1, testdata1); LittleEndian.putLong(received, 1, testdata1);
assertTrue(compareByteArrays(received, expected, 1, LittleEndian.LONG_SIZE)); assertTrue(compareByteArrays(received, expected, 1, LittleEndian.LONG_SIZE));

View File

@ -42,43 +42,7 @@ public class TestStringUtil
super( name ); 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 * test getFromUnicodeHigh for symbols with code below and more 127
@ -101,62 +65,7 @@ public class TestStringUtil
StringUtil.getFromUnicodeLE( test_data ) ); 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 * Test putCompressedUnicode