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

View File

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

View File

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

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

View File

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

View File

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

View File

@ -19,184 +19,77 @@ package org.apache.poi.hssf.record;
import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.StringUtil;
/**
* Title: Style Record<P>
* Title: Style Record (0x0293)<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>
* @author Andrew C. Oliver (acoliver at apache dot org)
* @author aviks : string fixes for UserDefined Style
* @version 2.0-pre
*/
public final class StyleRecord extends Record {
public final static short sid = 0x0293;
private static final BitField fHighByte = BitFieldFactory.getInstance(0x01);
private static final BitField is16BitUnicodeFlag = BitFieldFactory.getInstance(0x01);
public final static short STYLE_USER_DEFINED = 0;
public final static short STYLE_BUILT_IN = 1;
private static final BitField styleIndexMask = BitFieldFactory.getInstance(0x0FFF);
private static final BitField isBuiltinFlag = BitFieldFactory.getInstance(0x8000);
// shared by both user defined and builtin styles
private short field_1_xf_index; // TODO: bitfield candidate
/** shared by both user defined and built-in styles */
private int field_1_xf_index;
// only for built in styles
private byte field_2_builtin_style;
private byte field_3_outline_style_level;
private int field_2_builtin_style;
private int field_3_outline_style_level;
// only for user defined styles
private short field_2_name_length; //OO doc says 16 bit length, so we believe
private byte field_3_string_options;
private int field_3_string_options;
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();
if (getType() == STYLE_BUILT_IN)
{
if (isBuiltin()) {
field_2_builtin_style = in.readByte();
field_3_outline_style_level = in.readByte();
}
else if (getType() == STYLE_USER_DEFINED)
{
field_2_name_length = in.readShort();
// Some files from Crystal Reports lack
// the remaining fields, which is naughty
if(in.remaining() > 0) {
field_3_string_options = in.readByte();
byte[] string = in.readRemainder();
if (fHighByte.isSet(field_3_string_options)) {
field_4_name= StringUtil.getFromUnicodeBE(string, 0, field_2_name_length);
} else {
field_4_name=StringUtil.getFromCompressedUnicode(string, 0, field_2_name_length);
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
* @see #setIndex(short)
* @param index of the xf record
* @param xfIndex of the xf record
*/
public void setXFIndex(short index)
{
field_1_xf_index = setField(field_1_xf_index, index, 0x1FFF, 0);
}
// end bitfields
// only for user defined records
/**
* if this is a user defined record set the length of the style name
* @param length of the style's name
* @see #setName(String)
*/
public void setNameLength(byte length)
{
field_2_name_length = length;
}
/**
* set the style's name
* @param name of the style
* @see #setNameLength(byte)
*/
public void setName(String name)
{
field_4_name = name;
// Fix up the length
field_2_name_length = (short)name.length();
//TODO set name string options
}
// end user defined
// only for buildin records
/**
* if this is a builtin style set teh number of the built in style
* @param builtin style number (0-7)
*
*/
public void setBuiltin(byte builtin)
{
field_2_builtin_style = builtin;
}
/**
* set the row or column level of the style (if builtin 1||2)
*/
public void setOutlineStyleLevel(byte level)
{
field_3_outline_style_level = level;
}
// end builtin records
// field 1
/**
* get the entire index field (including the type) (see bit getters that reference this method)
* @return bitmask
*/
public short getIndex()
{
return field_1_xf_index;
}
// bitfields for field 1
/**
* get the type of the style (builtin or user-defined)
* @see #STYLE_USER_DEFINED
* @see #STYLE_BUILT_IN
* @return type of style (userdefined/builtin)
* @see #getIndex()
*/
public short getType()
{
return ( short ) ((field_1_xf_index & 0x8000) >> 15);
public void setXFIndex(int xfIndex) {
field_1_xf_index = styleIndexMask.setValue(field_1_xf_index, xfIndex);
}
/**
@ -204,143 +97,99 @@ public final class StyleRecord extends Record {
* @see #getIndex()
* @return index of the xf record
*/
public short getXFIndex()
{
return ( short ) (field_1_xf_index & 0x1FFF);
public int getXFIndex() {
return styleIndexMask.getValue(field_1_xf_index);
}
// 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
* @return length of the style's name
* @see #getName()
* if this is a builtin style set the number of the built in style
* @param builtinStyleId style number (0-7)
*
*/
public void setBuiltinStyle(int builtinStyleId) {
field_1_xf_index = isBuiltinFlag.set(field_1_xf_index);
field_2_builtin_style = builtinStyleId;
}
public short getNameLength()
{
return field_2_name_length;
/**
* set the row or column level of the style (if builtin 1||2)
*/
public void setOutlineStyleLevel(int level) {
field_3_outline_style_level = level & 0x00FF;
}
public boolean isBuiltin(){
return isBuiltinFlag.isSet(field_1_xf_index);
}
/**
* get the style's name
* @return name of the style
* @see #getNameLength()
*/
public String getName()
{
public String getName() {
return field_4_name;
}
// end user defined
// only for buildin records
public String toString() {
StringBuffer sb = new StringBuffer();
/**
* if this is a builtin style get the number of the built in style
* @return builtin style number (0-7)
*
*/
public byte getBuiltin()
{
return field_2_builtin_style;
sb.append("[STYLE]\n");
sb.append(" .xf_index_raw =").append(HexDump.shortToHex(field_1_xf_index)).append("\n");
sb.append(" .type =").append(isBuiltin() ? "built-in" : "user-defined").append("\n");
sb.append(" .xf_index =").append(HexDump.shortToHex(getXFIndex())).append("\n");
if (isBuiltin()){
sb.append(" .builtin_style=").append(HexDump.byteToHex(field_2_builtin_style)).append("\n");
sb.append(" .outline_level=").append(HexDump.byteToHex(field_3_outline_style_level)).append("\n");
} else {
sb.append(" .name =").append(getName()).append("\n");
}
sb.append("[/STYLE]\n");
return sb.toString();
}
/**
* get the row or column level of the style (if builtin 1||2)
*/
public byte getOutlineStyleLevel()
{
return field_3_outline_style_level;
private int getDataSize() {
if (isBuiltin()) {
return 4; // short, byte, byte
}
int size = 2 + 3; // short
if (is16BitUnicodeFlag.isSet(field_3_string_options)) {
size += 2 * field_4_name.length();
} else {
size += field_4_name.length();
}
return size;
}
// end builtin records
public String toString()
{
StringBuffer buffer = new StringBuffer();
buffer.append("[STYLE]\n");
buffer.append(" .xf_index_raw = ")
.append(Integer.toHexString(getIndex())).append("\n");
buffer.append(" .type = ")
.append(Integer.toHexString(getType())).append("\n");
buffer.append(" .xf_index = ")
.append(Integer.toHexString(getXFIndex())).append("\n");
if (getType() == STYLE_BUILT_IN)
{
buffer.append(" .builtin_style = ")
.append(Integer.toHexString(getBuiltin())).append("\n");
buffer.append(" .outline_level = ")
.append(Integer.toHexString(getOutlineStyleLevel()))
.append("\n");
}
else if (getType() == STYLE_USER_DEFINED)
{
buffer.append(" .name_length = ")
.append(Integer.toHexString(getNameLength())).append("\n");
buffer.append(" .name = ").append(getName())
.append("\n");
}
buffer.append("[/STYLE]\n");
return buffer.toString();
}
private short setField(int fieldValue, int new_value, int mask,
int shiftLeft)
{
return ( short ) ((fieldValue & ~mask)
| ((new_value << shiftLeft) & mask));
}
public int serialize(int offset, byte [] data)
{
public int serialize(int offset, byte [] data) {
int dataSize = getDataSize();
LittleEndian.putShort(data, 0 + offset, sid);
if (getType() == STYLE_BUILT_IN)
{
LittleEndian.putShort(data, 2 + offset,
(( short ) 0x04)); // 4 bytes (8 total)
}
else
{
LittleEndian.putShort(data, 2 + offset,
(( short ) (getRecordSize()-4)));
}
LittleEndian.putShort(data, 4 + offset, getIndex());
if (getType() == STYLE_BUILT_IN)
{
data[ 6 + offset ] = getBuiltin();
data[ 7 + offset ] = getOutlineStyleLevel();
}
else
{
LittleEndian.putShort(data, 6 + offset , getNameLength());
data[8+offset]=this.field_3_string_options;
LittleEndian.putUShort(data, 2 + offset, dataSize);
LittleEndian.putUShort(data, 4 + offset, field_1_xf_index);
if (isBuiltin()) {
LittleEndian.putByte(data, 6 + offset, field_2_builtin_style);
LittleEndian.putByte(data, 7 + offset, field_3_outline_style_level);
} else {
LittleEndian.putUShort(data, 6 + offset, field_4_name.length());
LittleEndian.putByte(data, 8 + offset, field_3_string_options);
StringUtil.putCompressedUnicode(getName(), data, 9 + offset);
}
return getRecordSize();
return 4+dataSize;
}
public int getRecordSize()
{
int retval;
if (getType() == STYLE_BUILT_IN)
{
retval = 8;
}
else
{
if (fHighByte.isSet(field_3_string_options)) {
retval= 9+2*getNameLength();
}else {
retval = 9 + getNameLength();
}
}
return retval;
public int getRecordSize() {
return 4 + getDataSize();
}
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.util.HexDump;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.LittleEndianByteArrayInputStream;
import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndianOutput;
import org.apache.poi.util.LittleEndianOutputStream;
@ -209,12 +210,7 @@ public abstract class SubRecord {
out.writeShort(_unknownShort13);
}
private static Ptg readRefPtg(byte[] formulaRawBytes) {
byte[] data = new byte[formulaRawBytes.length + 4];
LittleEndian.putUShort(data, 0, -5555);
LittleEndian.putUShort(data, 2, formulaRawBytes.length);
System.arraycopy(formulaRawBytes, 0, data, 4, formulaRawBytes.length);
RecordInputStream in = new RecordInputStream(new ByteArrayInputStream(data));
in.nextRecord();
LittleEndianInput in = new LittleEndianByteArrayInputStream(formulaRawBytes);
byte ptgSid = in.readByte();
switch(ptgSid) {
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)
*/
public SupBookRecord(RecordInputStream in) {
int recLen = in.remaining();
field_1_number_of_sheets = in.readShort();
if(in.getLength() > SMALL_RECORD_SIZE) {
if(recLen > SMALL_RECORD_SIZE) {
// 5.38.1 External References
_isAddInFunctions = false;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -17,7 +17,7 @@
package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.util.LittleEndianInput;
/**
* Specifies a rectangular area of cells A1:A4 for instance.
@ -26,7 +26,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
public final class AreaNPtg extends Area2DPtgBase {
public final static short sid = 0x2D;
public AreaNPtg(RecordInputStream in) {
public AreaNPtg(LittleEndianInput in) {
super(in);
}

View File

@ -17,7 +17,7 @@
package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.util.LittleEndianInput;
/**
* Specifies a rectangular area of cells A1:A4 for instance.
@ -29,7 +29,7 @@ public final class AreaPtg extends Area2DPtgBase {
public AreaPtg(int firstRow, int lastRow, int firstColumn, int lastColumn, boolean firstRowRelative, boolean lastRowRelative, boolean firstColRelative, boolean lastColRelative) {
super(firstRow, lastRow, firstColumn, lastColumn, firstRowRelative, lastRowRelative, firstColRelative, lastColRelative);
}
public AreaPtg(RecordInputStream in) {
public AreaPtg(LittleEndianInput in) {
super(in);
}
public AreaPtg(String arearef) {

View File

@ -17,12 +17,12 @@
package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.ss.util.AreaReference;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndianOutput;
/**
* Specifies a rectangular area of cells A1:A4 for instance.
@ -113,17 +113,17 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI {
}
}
protected final void readCoordinates(RecordInputStream in) {
protected final void readCoordinates(LittleEndianInput in) {
field_1_first_row = in.readUShort();
field_2_last_row = in.readUShort();
field_3_first_column = in.readUShort();
field_4_last_column = in.readUShort();
}
protected final void writeCoordinates(byte[] array, int offset) {
LittleEndian.putUShort(array, offset + 0, field_1_first_row);
LittleEndian.putUShort(array, offset + 2, field_2_last_row);
LittleEndian.putUShort(array, offset + 4, field_3_first_column);
LittleEndian.putUShort(array, offset + 6, field_4_last_column);
protected final void writeCoordinates(LittleEndianOutput out) {
out.writeShort(field_1_first_row);
out.writeShort(field_2_last_row);
out.writeShort(field_3_first_column);
out.writeShort(field_4_last_column);
}
/**

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -17,10 +17,10 @@
package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.ss.formula.WorkbookDependentFormula;
import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.ss.formula.WorkbookDependentFormula;
import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndianOutput;
/**
*
@ -43,7 +43,7 @@ public final class NamePtg extends OperandPtg implements WorkbookDependentFormul
/** Creates new NamePtg */
public NamePtg(RecordInputStream in) {
public NamePtg(LittleEndianInput in) {
field_1_label_index = in.readShort();
field_2_zero = in.readShort();
}
@ -55,20 +55,20 @@ public final class NamePtg extends OperandPtg implements WorkbookDependentFormul
return field_1_label_index - 1; // convert to zero based
}
public void writeBytes(byte [] array, int offset) {
LittleEndian.putByte(array, offset + 0, sid + getPtgClass());
LittleEndian.putUShort(array, offset + 1, field_1_label_index);
LittleEndian.putUShort(array, offset + 3, field_2_zero);
public void write(LittleEndianOutput out) {
out.writeByte(sid + getPtgClass());
out.writeShort(field_1_label_index);
out.writeShort(field_2_zero);
}
public int getSize() {
return SIZE;
}
public String toFormulaString(FormulaRenderingWorkbook book)
{
public String toFormulaString(FormulaRenderingWorkbook book) {
return book.getNameText(this);
}
public String toFormulaString() {
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;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.ss.formula.WorkbookDependentFormula;
import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.ss.formula.WorkbookDependentFormula;
import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndianOutput;
/**
*
@ -51,15 +51,15 @@ public final class NameXPtg extends OperandPtg implements WorkbookDependentFormu
this(sheetRefIndex, nameIndex + 1, 0);
}
public NameXPtg(RecordInputStream in) {
public NameXPtg(LittleEndianInput in) {
this(in.readUShort(), in.readUShort(), in.readUShort());
}
public void writeBytes(byte[] array, int offset) {
LittleEndian.putByte(array, offset + 0, sid + getPtgClass());
LittleEndian.putUShort(array, offset + 1, _sheetRefIndex);
LittleEndian.putUShort(array, offset + 3, _nameNumber);
LittleEndian.putUShort(array, offset + 5, _reserved);
public void write(LittleEndianOutput out) {
out.writeByte(sid + getPtgClass());
out.writeShort(_sheetRefIndex);
out.writeShort(_nameNumber);
out.writeShort(_reserved);
}
public int getSize() {

View File

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

View File

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

View File

@ -20,8 +20,8 @@ package org.apache.poi.hssf.record.formula;
import java.util.ArrayList;
import java.util.List;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndianByteArrayOutputStream;
import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndianOutput;
/**
@ -48,7 +48,7 @@ public abstract class Ptg implements Cloneable {
* 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.
*/
public static Ptg[] readTokens(int size, RecordInputStream in) {
public static Ptg[] readTokens(int size, LittleEndianInput in) {
List temp = new ArrayList(4 + size / 2);
int pos = 0;
List arrayPtgs = null;
@ -77,7 +77,7 @@ public abstract class Ptg implements Cloneable {
return toPtgArray(temp);
}
public static Ptg createPtg(RecordInputStream in) {
public static Ptg createPtg(LittleEndianInput in) {
byte id = in.readByte();
if (id < 0x20) {
@ -97,7 +97,7 @@ public abstract class Ptg implements Cloneable {
return retval;
}
private static Ptg createClassifiedPtg(byte id, RecordInputStream in) {
private static Ptg createClassifiedPtg(byte id, LittleEndianInput in) {
int baseId = id & 0x1F | 0x20;
@ -126,9 +126,9 @@ public abstract class Ptg implements Cloneable {
Integer.toHexString(id) + " (" + ( int ) id + ")");
}
private static Ptg createBasePtg(byte id, RecordInputStream in) {
private static Ptg createBasePtg(byte id, LittleEndianInput in) {
switch(id) {
case 0x00: return new UnknownPtg(); // TODO - not a real Ptg
case 0x00: return new UnknownPtg(id); // TODO - not a real Ptg
case ExpPtg.sid: return new ExpPtg(in); // 0x01
case TblPtg.sid: return new TblPtg(in); // 0x02
case AddPtg.sid: return AddPtg.instance; // 0x03
@ -228,32 +228,30 @@ public abstract class Ptg implements Cloneable {
* @return number of bytes written
*/
public static int serializePtgs(Ptg[] ptgs, byte[] array, int offset) {
int pos = 0;
int size = ptgs.length;
int nTokens = ptgs.length;
LittleEndianByteArrayOutputStream out = new LittleEndianByteArrayOutputStream(array, offset);
List arrayPtgs = null;
for (int k = 0; k < size; k++) {
for (int k = 0; k < nTokens; k++) {
Ptg ptg = ptgs[k];
ptg.writeBytes(array, pos + offset);
ptg.write(out);
if (ptg instanceof ArrayPtg) {
if (arrayPtgs == null) {
arrayPtgs = new ArrayList(5);
}
arrayPtgs.add(ptg);
pos += ArrayPtg.PLAIN_TOKEN_SIZE;
} else {
pos += ptg.getSize();
}
}
if (arrayPtgs != null) {
for (int i=0;i<arrayPtgs.size();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 final byte[] getBytes()
{
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
}
public abstract void write(LittleEndianOutput out);
/**
* return a string representation of this token alone
*/
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.
* This helps get rid of gratuitous diffs when comparing two dumps

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -17,14 +17,15 @@
package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.hssf.util.CellReference;
import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndianOutput;
/**
* ReferencePtgBase - handles references (such as A1, A2, IA4)
*
* @author Andrew C. Oliver (acoliver@apache.org)
* @author 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 */
private int field_1_row;
/** Field 2
* - lower 8 bits is the zero based unsigned byte column index
* - bit 16 - isRowRelative
* - bit 15 - isColumnRelative
/**
* Field 2 - lower 8 bits is the zero based unsigned byte column index - bit
* 16 - isRowRelative - bit 15 - isColumnRelative
*/
private int field_2_col;
private static final BitField rowRelative = BitFieldFactory.getInstance(0x8000);
@ -67,20 +67,21 @@ public abstract class RefPtgBase extends OperandPtg {
setColRelative(isColumnRelative);
}
protected final void readCoordinates(RecordInputStream in) {
protected final void readCoordinates(LittleEndianInput in) {
field_1_row = in.readUShort();
field_2_col = in.readUShort();
}
protected final void writeCoordinates(byte[] array, int offset) {
LittleEndian.putUShort(array, offset + 0, field_1_row);
LittleEndian.putUShort(array, offset + 2, field_2_col);
protected final void writeCoordinates(LittleEndianOutput out) {
out.writeShort(field_1_row);
out.writeShort(field_2_col);
}
public final void setRow(int row) {
if(row < 0 || row >= MAX_ROW_NUMBER) {
throw new IllegalArgumentException("The row number, when specified as an integer, must be between 0 and " + MAX_ROW_NUMBER);
public final void setRow(int rowIndex) {
if (rowIndex < 0 || rowIndex >= MAX_ROW_NUMBER) {
throw new IllegalArgumentException("rowIndex must be between 0 and " + MAX_ROW_NUMBER);
}
field_1_row = row;
field_1_row = rowIndex;
}
/**
@ -116,6 +117,7 @@ public abstract class RefPtgBase extends OperandPtg {
public final int getColumn() {
return column.getValue(field_2_col);
}
protected final String formatReferenceAsString() {
// Only make cell references as needed. Memory is an issue
CellReference cr = new CellReference(getRow(), getColumn(), !isRowRelative(), !isColRelative());

View File

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

View File

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

View File

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

View File

@ -16,7 +16,7 @@
==================================================================== */
package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.util.LittleEndianOutput;
/**
*
@ -25,22 +25,17 @@ import org.apache.poi.hssf.record.RecordInputStream;
*/
public class UnknownPtg extends Ptg {
private short size = 1;
private final int _sid;
/** Creates new UnknownPtg */
public UnknownPtg()
{
}
public UnknownPtg(RecordInputStream in) {
// doesn't need anything
public UnknownPtg(int sid) {
_sid = sid;
}
public boolean isBaseToken() {
return true;
}
public void writeBytes(byte [] array, int offset)
{
public void write(LittleEndianOutput out) {
out.writeByte(_sid);
}
public int getSize()
@ -55,8 +50,6 @@ public class UnknownPtg extends Ptg {
public byte getDefaultOperandClass() {return Ptg.CLASS_VALUE;}
public Object clone() {
return new UnknownPtg();
return this;
}
}

View File

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

View File

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

View File

@ -1,4 +1,3 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@ -16,436 +15,313 @@
limitations under the License.
==================================================================== */
package org.apache.poi.poifs.filesystem;
import java.io.*;
import java.io.IOException;
import java.io.InputStream;
import org.apache.poi.poifs.storage.DataInputBlock;
import org.apache.poi.util.LittleEndianInput;
/**
* This class provides methods to read a DocumentEntry managed by a
* Filesystem instance.
* {@link POIFSFileSystem} instance.
*
* @author Marc Johnson (mjohnson at apache dot org)
*/
public 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
extends InputStream
{
private static final int SIZE_SHORT = 2;
private static final int SIZE_INT = 4;
private static final int SIZE_LONG = 8;
// current offset into the Document
/** current offset into the Document */
private int _current_offset;
// current marked offset into the Document (used by mark and
// reset)
/** current marked offset into the Document (used by mark and reset) */
private int _marked_offset;
// the Document's size
/** the Document's size */
private int _document_size;
// have we been closed?
/** have we been closed? */
private boolean _closed;
// the actual Document
/** the actual Document */
private POIFSDocument _document;
// buffer used to read one byte at a time
private byte[] _tiny_buffer;
// returned by read operations if we're at end of document
static private final int EOD = -1;
/** the data block containing the current stream pointer */
private DataInputBlock _currentBlock;
/**
* Create an InputStream from the specified DocumentEntry
*
* @param document the DocumentEntry to be read
*
* @exception IOException if the DocumentEntry cannot be opened
* (like, maybe it has been deleted?)
* @exception IOException if the DocumentEntry cannot be opened (like, maybe it has
* been deleted?)
*/
public DocumentInputStream(final DocumentEntry document)
throws IOException
{
public DocumentInputStream(DocumentEntry document) throws IOException {
if (!(document instanceof DocumentNode)) {
throw new IOException("Cannot open internal document storage");
}
_current_offset = 0;
_marked_offset = 0;
_document_size = document.getSize();
_closed = false;
_tiny_buffer = null;
if (document instanceof DocumentNode)
{
_document = ((DocumentNode) document).getDocument();
}
else
{
throw new IOException("Cannot open internal document storage");
}
_currentBlock = getDataInputBlock(0);
}
/**
* Create an InputStream from the specified Document
*
* @param document the Document to be read
*
* @exception IOException if the DocumentEntry cannot be opened
* (like, maybe it has been deleted?)
*/
public DocumentInputStream(final POIFSDocument document)
throws IOException
{
public DocumentInputStream(POIFSDocument document) {
_current_offset = 0;
_marked_offset = 0;
_document_size = document.getSize();
_closed = false;
_tiny_buffer = null;
_document = document;
_currentBlock = getDataInputBlock(0);
}
/**
* Returns the number of bytes that can be read (or skipped over)
* from this input stream without blocking by the next caller of a
* method for this input stream. The next caller might be the same
* thread or or another thread.
*
* @return the number of bytes that can be read from this input
* stream without blocking.
*
* @exception IOException on error (such as the stream has been
* closed)
*/
public int available()
throws IOException
{
dieIfClosed();
public int available() {
if (_closed) {
throw new IllegalStateException("cannot perform requested operation on a closed stream");
}
return _document_size - _current_offset;
}
/**
* Closes this input stream and releases any system resources
* associated with the stream.
*
* @exception IOException
*/
public void close()
throws IOException
{
public void close() {
_closed = true;
}
/**
* Marks the current position in this input stream. A subsequent
* call to the reset method repositions this stream at the last
* marked position so that subsequent reads re-read the same
* bytes.
* <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)
{
public void mark(int ignoredReadlimit) {
_marked_offset = _current_offset;
}
/**
* 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;
}
/**
* Reads the next byte of data from the input stream. The value
* byte is returned as an int in the range 0 to 255. If no byte is
* available because the end of the stream has been reached, the
* value -1 is returned. The definition of this method in
* java.io.InputStream allows this method to block, but it won't.
*
* @return the next byte of data, or -1 if the end of the stream
* is reached.
*
* @exception IOException
*/
private DataInputBlock getDataInputBlock(int offset) {
return _document.getDataInputBlock(offset);
}
public int read()
throws IOException
{
public int read() throws IOException {
dieIfClosed();
if (atEOD())
{
return EOD;
if (atEOD()) {
return EOF;
}
if (_tiny_buffer == null)
{
_tiny_buffer = new byte[ 1 ];
int result = _currentBlock.readUByte();
_current_offset++;
if (_currentBlock.available() < 1) {
_currentBlock = getDataInputBlock(_current_offset);
}
_document.read(_tiny_buffer, _current_offset++);
return ((int)_tiny_buffer[ 0 ]) & 0x000000FF;
return result;
}
/**
* 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
{
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
/**
* Reads up to len bytes of data from the input stream into an
* array of bytes. An attempt is made to read as many as len
* bytes, but a smaller number may be read, possibly zero. The
* number of bytes actually read is returned as an integer.
* <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
{
public int read(byte[] b, int off, int len) throws IOException {
dieIfClosed();
if (b == null)
{
throw new NullPointerException("buffer is null");
if (b == null) {
throw new IllegalArgumentException("buffer must not be null");
}
if ((off < 0) || (len < 0) || (b.length < (off + len)))
{
throw new IndexOutOfBoundsException(
"can't read past buffer boundaries");
if (off < 0 || len < 0 || b.length < off + len) {
throw new IndexOutOfBoundsException("can't read past buffer boundaries");
}
if (len == 0)
{
if (len == 0) {
return 0;
}
if (atEOD())
{
return EOD;
if (atEOD()) {
return EOF;
}
int limit = Math.min(available(), len);
if ((off == 0) && (limit == b.length))
{
_document.read(b, _current_offset);
}
else
{
byte[] buffer = new byte[ limit ];
_document.read(buffer, _current_offset);
System.arraycopy(buffer, 0, b, off, limit);
}
_current_offset += limit;
readFully(b, off, limit);
return limit;
}
/**
* Repositions this stream to the position at the time the mark
* method was last called on this input stream.
* <p>
* 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.
* Repositions this stream to the position at the time the mark() method was
* last called on this input stream. If mark() has not been called this
* method repositions the stream to its beginning.
*/
public void reset()
{
public void reset() {
_current_offset = _marked_offset;
_currentBlock = getDataInputBlock(_current_offset);
}
/**
* Skips over and discards n bytes of data from this input
* stream. The skip method may, for a variety of reasons, end up
* skipping over some smaller number of bytes, possibly 0. This
* may result from any of a number of conditions; reaching end of
* file before n bytes have been skipped is only one
* possibility. The actual number of bytes skipped is returned. If
* n is negative, no bytes are skipped.
*
* @param n the number of bytes to be skipped.
*
* @return the actual number of bytes skipped.
*
* @exception IOException
*/
public long skip(final long n)
throws IOException
{
public long skip(long n) throws IOException {
dieIfClosed();
if (n < 0)
{
if (n < 0) {
return 0;
}
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
new_offset = _document_size;
}
else if (new_offset > _document_size)
{
} else if (new_offset > _document_size) {
new_offset = _document_size;
}
long rval = new_offset - _current_offset;
_current_offset = new_offset;
_currentBlock = getDataInputBlock(_current_offset);
return rval;
}
private void dieIfClosed()
throws IOException
{
if (_closed)
{
throw new IOException(
"cannot perform requested operation on a closed stream");
private void dieIfClosed() throws IOException {
if (_closed) {
throw new IOException("cannot perform requested operation on a closed stream");
}
}
private boolean atEOD()
{
private boolean atEOD() {
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
contributor license agreements. See the NOTICE file distributed with
@ -16,20 +15,25 @@
limitations under the License.
==================================================================== */
package org.apache.poi.poifs.filesystem;
import java.io.*;
import java.util.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.apache.poi.poifs.common.POIFSConstants;
import org.apache.poi.poifs.dev.POIFSViewable;
import org.apache.poi.poifs.property.DocumentProperty;
import org.apache.poi.poifs.property.Property;
import org.apache.poi.poifs.storage.BlockWritable;
import org.apache.poi.poifs.storage.ListManagedBlock;
import org.apache.poi.poifs.storage.DataInputBlock;
import org.apache.poi.poifs.storage.DocumentBlock;
import org.apache.poi.poifs.storage.ListManagedBlock;
import org.apache.poi.poifs.storage.RawDataBlock;
import org.apache.poi.poifs.storage.SmallDocumentBlock;
import org.apache.poi.util.HexDump;
@ -39,10 +43,9 @@ import org.apache.poi.util.HexDump;
*
* @author Marc Johnson (mjohnson at apache dot org)
*/
public class POIFSDocument
implements BATManaged, BlockWritable, POIFSViewable
{
public final class POIFSDocument implements BATManaged, BlockWritable, POIFSViewable {
private static final DocumentBlock[] EMPTY_BIG_BLOCK_ARRAY = { };
private static final SmallDocumentBlock[] EMPTY_SMALL_BLOCK_ARRAY = { };
private DocumentProperty _property;
private int _size;
@ -56,21 +59,32 @@ public class POIFSDocument
* @param name the name of the POIFSDocument
* @param blocks the big blocks making up the POIFSDocument
* @param length the actual length of the POIFSDocument
*
* @exception IOException
*/
public POIFSDocument(final String name, final RawDataBlock [] blocks,
final int length)
throws IOException
{
public POIFSDocument(String name, RawDataBlock[] blocks, int length) throws IOException {
_size = length;
_big_store = new BigBlockStore(blocks);
_big_store = new BigBlockStore(convertRawBlocksToBigBlocks(blocks));
_property = new DocumentProperty(name, _size);
_small_store = new SmallBlockStore(new BlockWritable[ 0 ]);
_small_store = new SmallBlockStore(EMPTY_SMALL_BLOCK_ARRAY);
_property.setDocument(this);
}
// TODO - awkward typing going on here
private static DocumentBlock[] convertRawBlocksToBigBlocks(ListManagedBlock[] blocks) throws IOException {
DocumentBlock[] result = new DocumentBlock[blocks.length];
for (int i = 0; i < result.length; i++) {
result[i] = new DocumentBlock((RawDataBlock)blocks[i]);
}
return result;
}
private static SmallDocumentBlock[] convertRawBlocksToSmallBlocks(ListManagedBlock[] blocks) {
if (blocks instanceof SmallDocumentBlock[]) {
return (SmallDocumentBlock[]) blocks;
}
SmallDocumentBlock[] result = new SmallDocumentBlock[blocks.length];
System.arraycopy(blocks, 0, result, 0, blocks.length);
return result;
}
/**
* Constructor from small blocks
*
@ -78,20 +92,9 @@ public class POIFSDocument
* @param blocks the small blocks making up the POIFSDocument
* @param length the actual length of the POIFSDocument
*/
public POIFSDocument(final String name,
final SmallDocumentBlock [] blocks, final int length)
{
public POIFSDocument(String name, SmallDocumentBlock[] blocks, int length) {
_size = length;
try
{
_big_store = new BigBlockStore(new RawDataBlock[ 0 ]);
}
catch (IOException ignored)
{
// can't happen with that constructor
}
_big_store = new BigBlockStore(EMPTY_BIG_BLOCK_ARRAY);
_property = new DocumentProperty(name, _size);
_small_store = new SmallBlockStore(blocks);
_property.setDocument(this);
@ -103,26 +106,17 @@ public class POIFSDocument
* @param name the name of the POIFSDocument
* @param blocks the small blocks making up the POIFSDocument
* @param length the actual length of the POIFSDocument
*
* @exception IOException
*/
public POIFSDocument(final String name, final ListManagedBlock [] blocks,
final int length)
throws IOException
{
public POIFSDocument(String name, ListManagedBlock[] blocks, int length) throws IOException {
_size = length;
_property = new DocumentProperty(name, _size);
_property.setDocument(this);
if (Property.isSmall(_size))
{
_big_store = new BigBlockStore(new RawDataBlock[ 0 ]);
_small_store = new SmallBlockStore(blocks);
}
else
{
_big_store = new BigBlockStore(blocks);
_small_store = new SmallBlockStore(new BlockWritable[ 0 ]);
if (Property.isSmall(_size)) {
_big_store = new BigBlockStore(EMPTY_BIG_BLOCK_ARRAY);
_small_store = new SmallBlockStore(convertRawBlocksToSmallBlocks(blocks));
} else {
_big_store = new BigBlockStore(convertRawBlocksToBigBlocks(blocks));
_small_store = new SmallBlockStore(EMPTY_SMALL_BLOCK_ARRAY);
}
}
@ -131,47 +125,33 @@ public class POIFSDocument
*
* @param name the name of the POIFSDocument
* @param stream the InputStream we read data from
*
* @exception IOException thrown on read errors
*/
public POIFSDocument(final String name, final InputStream stream)
throws IOException
{
public POIFSDocument(String name, InputStream stream) throws IOException {
List blocks = new ArrayList();
_size = 0;
while (true)
{
while (true) {
DocumentBlock block = new DocumentBlock(stream);
int blockSize = block.size();
if (blockSize > 0)
{
if (blockSize > 0) {
blocks.add(block);
_size += blockSize;
}
if (block.partiallyRead())
{
if (block.partiallyRead()) {
break;
}
}
DocumentBlock[] bigBlocks =
( DocumentBlock [] ) blocks.toArray(new DocumentBlock[ 0 ]);
DocumentBlock[] bigBlocks = (DocumentBlock[]) blocks.toArray(new DocumentBlock[blocks.size()]);
_big_store = new BigBlockStore(bigBlocks);
_property = new DocumentProperty(name, _size);
_property.setDocument(this);
if (_property.shouldUseSmallBlocks())
{
_small_store =
new SmallBlockStore(SmallDocumentBlock.convert(bigBlocks,
_size));
if (_property.shouldUseSmallBlocks()) {
_small_store = new SmallBlockStore(SmallDocumentBlock.convert(bigBlocks, _size));
_big_store = new BigBlockStore(new DocumentBlock[0]);
}
else
{
_small_store = new SmallBlockStore(new BlockWritable[ 0 ]);
} else {
_small_store = new SmallBlockStore(EMPTY_SMALL_BLOCK_ARRAY);
}
}
@ -181,49 +161,32 @@ public class POIFSDocument
* @param name the name of the POIFSDocument
* @param size the length of the POIFSDocument
* @param path the path of the POIFSDocument
* @param writer the writer who will eventually write the document
* contents
*
* @exception IOException thrown on read errors
* @param writer the writer who will eventually write the document contents
*/
public POIFSDocument(final String name, final int size,
final POIFSDocumentPath path,
final POIFSWriterListener writer)
throws IOException
{
public POIFSDocument(String name, int size, POIFSDocumentPath path, POIFSWriterListener writer) {
_size = size;
_property = new DocumentProperty(name, _size);
_property.setDocument(this);
if (_property.shouldUseSmallBlocks())
{
if (_property.shouldUseSmallBlocks()) {
_small_store = new SmallBlockStore(path, name, size, writer);
_big_store = new BigBlockStore(new Object[ 0 ]);
}
else
{
_small_store = new SmallBlockStore(new BlockWritable[ 0 ]);
_big_store = new BigBlockStore(EMPTY_BIG_BLOCK_ARRAY);
} else {
_small_store = new SmallBlockStore(EMPTY_SMALL_BLOCK_ARRAY);
_big_store = new BigBlockStore(path, name, size, writer);
}
}
/**
* return the array of SmallDocumentBlocks used
*
* @return array of SmallDocumentBlocks; may be empty, cannot be null
*/
public BlockWritable [] getSmallBlocks()
{
public BlockWritable[] getSmallBlocks() {
return _small_store.getBlocks();
}
/**
* @return size of the document
*/
public int getSize()
{
public int getSize() {
return _size;
}
@ -232,28 +195,70 @@ public class POIFSDocument
*
* @param buffer the buffer to write to
* @param offset the offset into our storage to read from
* This method is currently (Oct 2008) only used by test code. Perhaps it can be deleted
*/
void read(byte[] buffer, int offset) {
int len = buffer.length;
void read(final byte [] buffer, final int offset)
{
if (_property.shouldUseSmallBlocks())
{
SmallDocumentBlock.read(_small_store.getBlocks(), buffer, offset);
DataInputBlock currentBlock = getDataInputBlock(offset);
int blockAvailable = currentBlock.available();
if (blockAvailable > len) {
currentBlock.readFully(buffer, 0, len);
return;
}
// else read big amount in chunks
int remaining = len;
int writePos = 0;
int currentOffset = offset;
while (remaining > 0) {
boolean blockIsExpiring = remaining >= blockAvailable;
int reqSize;
if (blockIsExpiring) {
reqSize = blockAvailable;
} else {
reqSize = remaining;
}
currentBlock.readFully(buffer, writePos, reqSize);
remaining-=reqSize;
writePos+=reqSize;
currentOffset += reqSize;
if (blockIsExpiring) {
if (currentOffset == _size) {
if (remaining > 0) {
throw new IllegalStateException("reached end of document stream unexpectedly");
}
currentBlock = null;
break;
}
currentBlock = getDataInputBlock(currentOffset);
blockAvailable = currentBlock.available();
}
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
*/
DocumentProperty getDocumentProperty()
{
DocumentProperty getDocumentProperty() {
return _property;
}
@ -262,16 +267,9 @@ public class POIFSDocument
/**
* Write the storage to an OutputStream
*
* @param stream the OutputStream to which the stored data should
* be written
*
* @exception IOException on problems writing to the specified
* stream
* @param stream the OutputStream to which the stored data should be written
*/
public void writeBlocks(final OutputStream stream)
throws IOException
{
public void writeBlocks(OutputStream stream) throws IOException {
_big_store.writeBlocks(stream);
}
@ -283,21 +281,16 @@ public class POIFSDocument
*
* @return count of BigBlock instances
*/
public int countBlocks()
{
public int countBlocks() {
return _big_store.countBlocks();
}
/**
* Set the start block for this instance
*
* @param index index into the array of blocks making up the
* filesystem
* @param index index into the array of blocks making up the filesystem
*/
public void setStartBlock(final int index)
{
public void setStartBlock(int index) {
_property.setStartBlock(index);
}
@ -305,40 +298,30 @@ public class POIFSDocument
/* ********** START begin implementation of POIFSViewable ********** */
/**
* Get an array of objects, some of which may implement
* POIFSViewable
* Get an array of objects, some of which may implement POIFSViewable
*
* @return an array of Object; may not be null, but may be empty
*/
public Object [] getViewableArray()
{
public Object[] getViewableArray() {
Object[] results = new Object[1];
String result;
try
{
try {
ByteArrayOutputStream output = new ByteArrayOutputStream();
BlockWritable[] blocks = null;
if (_big_store.isValid())
{
if (_big_store.isValid()) {
blocks = _big_store.getBlocks();
}
else if (_small_store.isValid())
{
} else if (_small_store.isValid()) {
blocks = _small_store.getBlocks();
}
if (blocks != null)
{
for (int k = 0; k < blocks.length; k++)
{
if (blocks != null) {
for (int k = 0; k < blocks.length; k++) {
blocks[k].writeBlocks(output);
}
byte[] data = output.toByteArray();
if (data.length > _property.getSize())
{
if (data.length > _property.getSize()) {
byte[] tmp = new byte[_property.getSize()];
System.arraycopy(data, 0, tmp, 0, tmp.length);
@ -347,14 +330,10 @@ public class POIFSDocument
output = new ByteArrayOutputStream();
HexDump.dump(data, 0, output, 0);
result = output.toString();
}
else
{
} else {
result = "<NO DATA>";
}
}
catch (IOException e)
{
} catch (IOException e) {
result = e.getMessage();
}
results[0] = result;
@ -362,15 +341,12 @@ public class POIFSDocument
}
/**
* Get an Iterator of objects, some of which may implement
* POIFSViewable
* Get an Iterator of objects, some of which may implement POIFSViewable
*
* @return an Iterator; may not be null, but may have an empty
* back end store
* @return an Iterator; may not be null, but may have an empty back end
* store
*/
public Iterator getViewableIterator()
{
public Iterator getViewableIterator() {
return Collections.EMPTY_LIST.iterator();
}
@ -378,12 +354,10 @@ public class POIFSDocument
* Give viewers a hint as to whether to call getViewableArray or
* getViewableIterator
*
* @return true if a viewer should call getViewableArray, false if
* a viewer should call getViewableIterator
* @return <code>true</code> if a viewer should call getViewableArray,
* <code>false</code> if a viewer should call getViewableIterator
*/
public boolean preferArray()
{
public boolean preferArray() {
return true;
}
@ -393,39 +367,29 @@ public class POIFSDocument
*
* @return short description
*/
public String getShortDescription()
{
public String getShortDescription() {
StringBuffer buffer = new StringBuffer();
buffer.append("Document: \"").append(_property.getName())
.append("\"");
buffer.append("Document: \"").append(_property.getName()).append("\"");
buffer.append(" size = ").append(getSize());
return buffer.toString();
}
/* ********** END begin implementation of POIFSViewable ********** */
private class SmallBlockStore
{
private static final class SmallBlockStore {
private SmallDocumentBlock[] smallBlocks;
private POIFSDocumentPath path;
private String name;
private int size;
private POIFSWriterListener writer;
private final POIFSDocumentPath path;
private final String name;
private final int size;
private final POIFSWriterListener writer;
/**
* Constructor
*
* @param blocks blocks to construct the store from
*/
SmallBlockStore(final Object [] blocks)
{
smallBlocks = new SmallDocumentBlock[ blocks.length ];
for (int j = 0; j < blocks.length; j++)
{
smallBlocks[ j ] = ( SmallDocumentBlock ) blocks[ j ];
}
SmallBlockStore(SmallDocumentBlock[] blocks) {
smallBlocks = (SmallDocumentBlock[]) blocks.clone();
this.path = null;
this.name = null;
this.size = -1;
@ -433,18 +397,14 @@ public class POIFSDocument
}
/**
* Constructor for a small block store that will be written
* later
* Constructor for a small block store that will be written later
*
* @param path path of the document
* @param name name of the document
* @param size length of the document
* @param writer the object that will eventually write the document
*/
SmallBlockStore(final POIFSDocumentPath path, final String name,
final int size, final POIFSWriterListener writer)
{
SmallBlockStore(POIFSDocumentPath path, String name, int size, POIFSWriterListener writer) {
smallBlocks = new SmallDocumentBlock[0];
this.path = path;
this.name = name;
@ -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()
{
return ((smallBlocks.length > 0) || (writer != null));
boolean isValid() {
return smallBlocks.length > 0 || writer != null;
}
/**
* @return the SmallDocumentBlocks
*/
SmallDocumentBlock[] getBlocks() {
if (isValid() && writer != null) {
ByteArrayOutputStream stream = new ByteArrayOutputStream(size);
DocumentOutputStream dstream = new DocumentOutputStream(stream, size);
BlockWritable [] getBlocks()
{
if (isValid() && (writer != null))
{
ByteArrayOutputStream stream =
new ByteArrayOutputStream(size);
DocumentOutputStream dstream =
new DocumentOutputStream(stream, size);
writer.processPOIFSWriterEvent(new POIFSWriterEvent(dstream,
path, name, size));
smallBlocks = SmallDocumentBlock.convert(stream.toByteArray(),
size);
writer.processPOIFSWriterEvent(new POIFSWriterEvent(dstream, path, name, size));
smallBlocks = SmallDocumentBlock.convert(stream.toByteArray(), size);
}
return smallBlocks;
}
} // end private class SmallBlockStore
private class BigBlockStore
{
private static final class BigBlockStore {
private DocumentBlock[] bigBlocks;
private POIFSDocumentPath path;
private String name;
private int size;
private POIFSWriterListener writer;
private final POIFSDocumentPath path;
private final String name;
private final int size;
private final POIFSWriterListener writer;
/**
* Constructor
*
* @param blocks the blocks making up the store
*
* @exception IOException on I/O error
*/
BigBlockStore(final Object [] blocks)
throws IOException
{
bigBlocks = new DocumentBlock[ blocks.length ];
for (int j = 0; j < blocks.length; j++)
{
if (blocks[ j ] instanceof DocumentBlock)
{
bigBlocks[ j ] = ( DocumentBlock ) blocks[ j ];
}
else
{
bigBlocks[ j ] =
new DocumentBlock(( RawDataBlock ) blocks[ j ]);
}
}
BigBlockStore(DocumentBlock[] blocks) {
bigBlocks = (DocumentBlock[]) blocks.clone();
this.path = null;
this.name = null;
this.size = -1;
@ -522,19 +455,14 @@ public class POIFSDocument
}
/**
* Constructor for a big block store that will be written
* later
* Constructor for a big block store that will be written later
*
* @param path path of the document
* @param name name of the document
* @param size length of the document
* @param writer the object that will eventually write the
* document
* @param writer the object that will eventually write the document
*/
BigBlockStore(final POIFSDocumentPath path, final String name,
final int size, final POIFSWriterListener writer)
{
BigBlockStore(POIFSDocumentPath path, String name, int size, POIFSWriterListener writer) {
bigBlocks = new DocumentBlock[0];
this.path = path;
this.name = name;
@ -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()
{
return ((bigBlocks.length > 0) || (writer != null));
boolean isValid() {
return bigBlocks.length > 0 || writer != null;
}
/**
* @return the DocumentBlocks
*/
DocumentBlock[] getBlocks() {
if (isValid() && writer != null) {
ByteArrayOutputStream stream = new ByteArrayOutputStream(size);
DocumentOutputStream dstream = new DocumentOutputStream(stream, size);
DocumentBlock [] getBlocks()
{
if (isValid() && (writer != null))
{
ByteArrayOutputStream stream =
new ByteArrayOutputStream(size);
DocumentOutputStream dstream =
new DocumentOutputStream(stream, size);
writer.processPOIFSWriterEvent(new POIFSWriterEvent(dstream,
path, name, size));
writer.processPOIFSWriterEvent(new POIFSWriterEvent(dstream, path, name, size));
bigBlocks = DocumentBlock.convert(stream.toByteArray(), size);
}
return bigBlocks;
@ -575,31 +495,17 @@ public class POIFSDocument
* write the blocks to a stream
*
* @param stream the stream to which the data is to be written
*
* @exception IOException on error
*/
void writeBlocks(OutputStream stream) throws IOException {
if (isValid()) {
if (writer != null) {
DocumentOutputStream dstream = new DocumentOutputStream(stream, size);
void writeBlocks(OutputStream stream)
throws IOException
{
if (isValid())
{
if (writer != null)
{
DocumentOutputStream dstream =
new DocumentOutputStream(stream, size);
writer.processPOIFSWriterEvent(
new POIFSWriterEvent(dstream, path, name, size));
dstream.writeFiller(countBlocks()
* POIFSConstants
.BIG_BLOCK_SIZE, DocumentBlock
.getFillByte());
}
else
{
for (int k = 0; k < bigBlocks.length; k++)
{
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
*/
int countBlocks() {
int countBlocks()
{
int rval = 0;
if (isValid())
{
if (writer != null)
{
rval = (size + POIFSConstants.BIG_BLOCK_SIZE - 1)
if (isValid()) {
if (writer == null) {
return bigBlocks.length;
}
return (size + POIFSConstants.BIG_BLOCK_SIZE - 1)
/ POIFSConstants.BIG_BLOCK_SIZE;
}
else
{
rval = bigBlocks.length;
}
}
return rval;
return 0;
}
} // 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
contributor license agreements. See the NOTICE file distributed with
@ -16,30 +15,26 @@
limitations under the License.
==================================================================== */
package org.apache.poi.poifs.storage;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import org.apache.poi.poifs.common.POIFSConstants;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.IntegerField;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.LittleEndianConsts;
/**
* A block of document data.
*
* @author Marc Johnson (mjohnson at apache dot org)
*/
public final class DocumentBlock extends BigBlock {
private static final int BLOCK_SHIFT = 9;
private static final int BLOCK_SIZE = 1 << BLOCK_SHIFT;
private static final int BLOCK_MASK = BLOCK_SIZE-1;
public class DocumentBlock
extends BigBlock
{
private static final byte _default_value = ( byte ) 0xFF;
private byte[] _data;
private int _bytes_read;
@ -161,45 +156,10 @@ public class DocumentBlock
return rval;
}
/**
* read data from an array of DocumentBlocks
*
* @param blocks the blocks to read from
* @param buffer the buffer to write the data into
* @param offset the offset into the array of blocks to read from
*/
public static void read(final DocumentBlock [] blocks,
final byte [] buffer, final int offset)
{
int firstBlockIndex = offset / POIFSConstants.BIG_BLOCK_SIZE;
int firstBlockOffset = offset % POIFSConstants.BIG_BLOCK_SIZE;
int lastBlockIndex = (offset + buffer.length - 1)
/ POIFSConstants.BIG_BLOCK_SIZE;
if (firstBlockIndex == lastBlockIndex)
{
System.arraycopy(blocks[ firstBlockIndex ]._data,
firstBlockOffset, buffer, 0, buffer.length);
}
else
{
int buffer_offset = 0;
System.arraycopy(blocks[ firstBlockIndex ]._data,
firstBlockOffset, buffer, buffer_offset,
POIFSConstants.BIG_BLOCK_SIZE
- firstBlockOffset);
buffer_offset += POIFSConstants.BIG_BLOCK_SIZE - firstBlockOffset;
for (int j = firstBlockIndex + 1; j < lastBlockIndex; j++)
{
System.arraycopy(blocks[ j ]._data, 0, buffer, buffer_offset,
POIFSConstants.BIG_BLOCK_SIZE);
buffer_offset += POIFSConstants.BIG_BLOCK_SIZE;
}
System.arraycopy(blocks[ lastBlockIndex ]._data, 0, buffer,
buffer_offset, buffer.length - buffer_offset);
}
public static DataInputBlock getDataInputBlock(DocumentBlock[] blocks, int offset) {
int firstBlockIndex = offset >> BLOCK_SHIFT;
int firstBlockOffset= offset & BLOCK_MASK;
return new DataInputBlock(blocks[firstBlockIndex]._data, firstBlockOffset);
}
/* ********** START extension of BigBlock ********** */

View File

@ -1,4 +1,3 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@ -16,12 +15,14 @@
limitations under the License.
==================================================================== */
package org.apache.poi.poifs.storage;
import java.io.*;
import java.util.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.poi.poifs.common.POIFSConstants;
@ -31,13 +32,14 @@ import org.apache.poi.poifs.common.POIFSConstants;
*
* @author Marc Johnson (mjohnson at apache dot org)
*/
public final class SmallDocumentBlock implements BlockWritable, ListManagedBlock {
private static final int BLOCK_SHIFT = 6;
public class SmallDocumentBlock
implements BlockWritable, ListManagedBlock
{
private byte[] _data;
private static final byte _default_fill = ( byte ) 0xff;
private static final int _block_size = 64;
private static final int _block_size = 1 << BLOCK_SHIFT;
private static final int BLOCK_MASK = _block_size-1;
private static final int _blocks_per_big_block =
POIFSConstants.BIG_BLOCK_SIZE / _block_size;
@ -178,46 +180,10 @@ public class SmallDocumentBlock
return sdbs;
}
/**
* read data from an array of SmallDocumentBlocks
*
* @param blocks the blocks to read from
* @param buffer the buffer to write the data into
* @param offset the offset into the array of blocks to read from
*/
public static void read(final BlockWritable [] blocks,
final byte [] buffer, final int offset)
{
int firstBlockIndex = offset / _block_size;
int firstBlockOffset = offset % _block_size;
int lastBlockIndex = (offset + buffer.length - 1) / _block_size;
if (firstBlockIndex == lastBlockIndex)
{
System.arraycopy(
(( SmallDocumentBlock ) blocks[ firstBlockIndex ])._data,
firstBlockOffset, buffer, 0, buffer.length);
}
else
{
int buffer_offset = 0;
System.arraycopy(
(( SmallDocumentBlock ) blocks[ firstBlockIndex ])._data,
firstBlockOffset, buffer, buffer_offset,
_block_size - firstBlockOffset);
buffer_offset += _block_size - firstBlockOffset;
for (int j = firstBlockIndex + 1; j < lastBlockIndex; j++)
{
System.arraycopy((( SmallDocumentBlock ) blocks[ j ])._data,
0, buffer, buffer_offset, _block_size);
buffer_offset += _block_size;
}
System.arraycopy(
(( SmallDocumentBlock ) blocks[ lastBlockIndex ])._data, 0,
buffer, buffer_offset, buffer.length - buffer_offset);
}
public static DataInputBlock getDataInputBlock(SmallDocumentBlock[] blocks, int offset) {
int firstBlockIndex = offset >> BLOCK_SHIFT;
int firstBlockOffset= offset & BLOCK_MASK;
return new DataInputBlock(blocks[firstBlockIndex]._data, firstBlockOffset);
}
/**

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 Andrew Oliver (acoliver at apache dot org)
*/
public final class LittleEndian implements LittleEndianConsts {
public class LittleEndian implements LittleEndianConsts {
private LittleEndian() {
// no instances of this class
}
/**
* get a short value from a byte array
*
@ -42,9 +41,10 @@ public final class LittleEndian implements LittleEndianConsts {
*@param offset a starting offset into the byte array
*@return the short (16-bit) value
*/
public static short getShort(final byte[] data, final int offset) {
return (short) getNumber(data, offset, SHORT_SIZE);
public static short getShort(byte[] data, int offset) {
int b0 = data[offset] & 0xFF;
int b1 = data[offset+1] & 0xFF;
return (short) ((b1 << 8) + (b0 << 0));
}
@ -55,49 +55,11 @@ public final class LittleEndian implements LittleEndianConsts {
*@param offset a starting offset into the byte array
*@return the unsigned short (16-bit) value in an integer
*/
public static int getUShort(final byte[] data, final int offset) {
short num = (short) getNumber(data, offset, SHORT_SIZE);
int retNum;
if (num < 0) {
retNum = (Short.MAX_VALUE + 1) * 2 + num;
} else {
retNum = num;
public static int getUShort(byte[] data, int offset) {
int b0 = data[offset] & 0xFF;
int b1 = data[offset+1] & 0xFF;
return (b1 << 8) + (b0 << 0);
}
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
@ -105,23 +67,20 @@ public final class LittleEndian implements LittleEndianConsts {
*@param data the byte array
*@return the short (16-bit) value
*/
public static short getShort(final byte[] data) {
public static short getShort(byte[] data) {
return getShort(data, 0);
}
/**
* get an unsigned short value from the beginning of a byte array
*
*@param data the byte array
*@return the unsigned short (16-bit) value in an int
*/
public static int getUShort(final byte[] data) {
public static int getUShort(byte[] data) {
return getUShort(data, 0);
}
/**
* get an int value from a byte array
*
@ -129,9 +88,13 @@ public final class LittleEndian implements LittleEndianConsts {
*@param offset a starting offset into the byte array
*@return the int (32-bit) value
*/
public static int getInt(final byte[] data, final int offset) {
return (int) getNumber(data, offset, INT_SIZE);
public static int getInt(byte[] data, int offset) {
int i=offset;
int b0 = data[i++] & 0xFF;
int b1 = data[i++] & 0xFF;
int b2 = data[i++] & 0xFF;
int b3 = data[i++] & 0xFF;
return (b3 << 24) + (b2 << 16) + (b1 << 8) + (b0 << 0);
}
@ -141,8 +104,7 @@ public final class LittleEndian implements LittleEndianConsts {
*@param data the byte array
*@return the int (32-bit) value
*/
public static int getInt(final byte[] data) {
public static int getInt(byte[] data) {
return getInt(data, 0);
}
@ -154,15 +116,9 @@ public final class LittleEndian implements LittleEndianConsts {
*@param offset a starting offset into the byte array
*@return the unsigned int (32-bit) value in a long
*/
public static long getUInt(final byte[] data, final int offset) {
int num = (int) getNumber(data, offset, INT_SIZE);
long retNum;
if (num < 0) {
retNum = ((long) Integer.MAX_VALUE + 1) * 2 + num;
} else {
retNum = num;
}
return retNum;
public static long getUInt(byte[] data, int offset) {
long retNum = getInt(data, offset);
return retNum & 0x00FFFFFFFF;
}
/**
@ -171,7 +127,7 @@ public final class LittleEndian implements LittleEndianConsts {
*@param data the byte array
*@return the unsigned int (32-bit) value in a long
*/
public static long getUInt(final byte[] data) {
public static long getUInt(byte[] data) {
return getUInt(data,0);
}
@ -182,24 +138,16 @@ public final class LittleEndian implements LittleEndianConsts {
*@param offset a starting offset into the byte array
*@return the long (64-bit) value
*/
public static long getLong(byte[] data, int offset) {
long result = 0;
public static long getLong(final byte[] data, final int offset) {
return getNumber(data, offset, LONG_SIZE);
for (int j = offset + LONG_SIZE - 1; j >= offset; j--) {
result <<= 8;
result |= 0xff & data[j];
}
/**
* 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);
return result;
}
/**
* get a double value from a byte array, reads it in little endian format
* then converts the resulting revolting IEEE 754 (curse them) floating
@ -209,24 +157,10 @@ public final class LittleEndian implements LittleEndianConsts {
*@param offset a starting offset into the byte array
*@return the double (64-bit) value
*/
public static double getDouble(final byte[] data, final int offset) {
return Double.longBitsToDouble(getNumber(data, offset, DOUBLE_SIZE));
public static double getDouble(byte[] data, int offset) {
return Double.longBitsToDouble(getLong(data, offset));
}
/**
* 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
*
@ -234,9 +168,10 @@ public final class LittleEndian implements LittleEndianConsts {
*@param offset a starting offset into the byte array
*@param value the short (16-bit) value
*/
public static void putShort(final byte[] data, final int offset,
final short value) {
putNumber(data, offset, value, SHORT_SIZE);
public static void putShort(byte[] data, int offset, short value) {
int i = offset;
data[i++] = (byte)((value >>> 0) & 0xFF);
data[i++] = (byte)((value >>> 8) & 0xFF);
}
/**
@ -247,7 +182,7 @@ public final class LittleEndian implements LittleEndianConsts {
* Added for consistency with other put~() methods
*/
public static void putByte(byte[] data, int offset, int value) {
putNumber(data, offset, value, LittleEndianConsts.BYTE_SIZE);
data[offset] = (byte) value;
}
/**
@ -259,10 +194,10 @@ public final class LittleEndian implements LittleEndianConsts {
*
* @exception ArrayIndexOutOfBoundsException may be thrown
*/
public static void putUShort(final byte[] data, final int offset,
final int value)
{
putNumber(data, offset, value, SHORT_SIZE);
public static void putUShort(byte[] data, int offset, int value) {
int i = offset;
data[i++] = (byte)((value >>> 0) & 0xFF);
data[i++] = (byte)((value >>> 8) & 0xFF);
}
/**
@ -271,8 +206,7 @@ public final class LittleEndian implements LittleEndianConsts {
*@param data the byte array
*@param value the short (16-bit) value
*/
public static void putShort(final byte[] data, final short value) {
public static void putShort(byte[] data, short value) {
putShort(data, 0, value);
}
@ -284,10 +218,12 @@ public final class LittleEndian implements LittleEndianConsts {
*@param offset a starting offset into the byte array
*@param value the int (32-bit) value
*/
public static void putInt(final byte[] data, final int offset,
final int value) {
putNumber(data, offset, value, INT_SIZE);
public static void putInt(byte[] data, int offset, int value) {
int i = offset;
data[i++] = (byte)((value >>> 0) & 0xFF);
data[i++] = (byte)((value >>> 8) & 0xFF);
data[i++] = (byte)((value >>> 16) & 0xFF);
data[i++] = (byte)((value >>> 24) & 0xFF);
}
@ -297,8 +233,7 @@ public final class LittleEndian implements LittleEndianConsts {
*@param data the byte array
*@param value the int (32-bit) value
*/
public static void putInt(final byte[] data, final int value) {
public static void putInt(byte[] data, int value) {
putInt(data, 0, value);
}
@ -310,22 +245,14 @@ public final class LittleEndian implements LittleEndianConsts {
*@param offset a starting offset into the byte array
*@param value the long (64-bit) value
*/
public static void putLong(byte[] data, int offset, long value) {
int limit = LONG_SIZE + offset;
long v = value;
public static void putLong(final byte[] data, final int offset,
final long value) {
putNumber(data, offset, value, LONG_SIZE);
for (int j = offset; j < limit; j++) {
data[j] = (byte) (v & 0xFF);
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 value the double (64-bit) value
*/
public static void putDouble(final byte[] data, final int offset,
final double value) {
public static void putDouble(byte[] data, int offset, double value) {
// Excel likes NaN to be a specific value.
if (Double.isNaN(value))
putNumber(data, offset, -276939487313920L, DOUBLE_SIZE);
else
putNumber(data, offset, Double.doubleToLongBits(value), DOUBLE_SIZE);
if (Double.isNaN(value)) {
putLong(data, offset, -276939487313920L);
} else {
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)
*/
public static final class BufferUnderrunException extends IOException {
BufferUnderrunException() {
@ -376,12 +289,10 @@ public final class LittleEndian implements LittleEndianConsts {
/**
* get a short value from an InputStream
*
*@param stream the InputStream from which the short
* is to be read
*@param stream the InputStream from which the short is to be read
*@return the short (16-bit) value
*@exception IOException will be propagated back to the caller
*@exception BufferUnderrunException if the stream cannot provide enough
* bytes
*@exception BufferUnderrunException if the stream cannot provide enough bytes
*/
public static short readShort(InputStream stream) throws IOException, BufferUnderrunException {
@ -395,21 +306,19 @@ public final class LittleEndian implements LittleEndianConsts {
if ((ch1 | ch2) < 0) {
throw new BufferUnderrunException();
}
return ((ch2 << 8) + (ch1 << 0));
return (ch2 << 8) + (ch1 << 0);
}
/**
* get an int value from an InputStream
*
*@param stream the InputStream from which the int is
* to be read
*@param stream the InputStream from which the int is to be read
* @return the int (32-bit) value
* @exception IOException will be propagated back to the caller
*@exception BufferUnderrunException if the stream cannot provide enough
* bytes
* @exception BufferUnderrunException if the stream cannot provide enough bytes
*/
public static int readInt(final InputStream stream)
public static int readInt(InputStream stream)
throws IOException, BufferUnderrunException {
int ch1 = stream.read();
int ch2 = stream.read();
@ -418,22 +327,19 @@ public final class LittleEndian implements LittleEndianConsts {
if ((ch1 | ch2 | ch3 | ch4) < 0) {
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
*
*@param stream the InputStream from which the long
* is to be read
* @param stream the InputStream from which the long is to be read
* @return the long (64-bit) value
* @exception IOException will be propagated back to the caller
*@exception BufferUnderrunException if the stream cannot provide enough
* bytes
* @exception BufferUnderrunException if the stream cannot provide enough bytes
*/
public static long readLong(final InputStream stream)
public static long readLong(InputStream stream)
throws IOException, BufferUnderrunException {
int ch1 = stream.read();
int ch2 = stream.read();
@ -458,46 +364,6 @@ public final class LittleEndian implements LittleEndianConsts {
(ch1 << 0);
}
/**
* Gets the number attribute of the LittleEndian class
*
*@param data Description of the Parameter
*@param offset Description of the Parameter
*@param size Description of the Parameter
*@return The number value
*/
private static long getNumber(final byte[] data, final int offset,
final int size) {
long result = 0;
for (int j = offset + size - 1; j >= offset; j--) {
result <<= 8;
result |= 0xff & data[j];
}
return result;
}
/**
* Description of the Method
*
*@param data Description of the Parameter
*@param offset Description of the Parameter
*@param value Description of the Parameter
*@param size Description of the Parameter
*/
private static void putNumber(final byte[] data, final int offset,
final long value, final int size) {
int limit = size + offset;
long v = value;
for (int j = offset; j < limit; j++) {
data[j] = (byte) (v & 0xFF);
v >>= 8;
}
}
/**
* Convert an 'unsigned' byte to an integer. ie, don't carry across the
* sign.
@ -506,7 +372,7 @@ public final class LittleEndian implements LittleEndianConsts {
* @return Description of the Return Value
*/
public static int ubyteToInt(byte b) {
return ((b & 0x80) == 0 ? (int) b : (b & (byte) 0x7f) + 0x80);
return b & 0xFF;
}
@ -517,19 +383,8 @@ public final class LittleEndian implements LittleEndianConsts {
* @param offset a starting offset into the byte array.
* @return the unsigned value of the byte as a 32 bit integer
*/
public static int getUnsignedByte(final byte[] data, final int offset) {
return (int) getNumber(data, offset, BYTE_SIZE);
}
/**
* get the unsigned value of a byte.
*
*@param data the byte array
*@return the unsigned value of the byte as a 32 bit integer
*/
public static int getUnsignedByte(final byte[] data) {
return getUnsignedByte(data, 0);
public static int getUnsignedByte(byte[] data, int offset) {
return data[offset] & 0xFF;
}
@ -543,29 +398,10 @@ public final class LittleEndian implements LittleEndianConsts {
* @throws IndexOutOfBoundsException - if copying would cause access of
* data outside array bounds.
*/
public static byte[] getByteArray(final byte[] data, int offset, int size) {
public static byte[] getByteArray(byte[] data, int offset, int size) {
byte[] copy = new byte[size];
System.arraycopy(data, offset, copy, 0, size);
return copy;
}
/**
* <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
*/
public interface LittleEndianInput {
int available();
byte readByte();
int readUByte();
short readShort();
@ -30,6 +31,4 @@ public interface LittleEndianInput {
double readDouble();
void readFully(byte[] buf);
void readFully(byte[] buf, int off, int len);
String readUnicodeLEString(int nChars);
String readCompressedUnicode(int nChars);
}

View File

@ -22,6 +22,10 @@ import java.io.IOException;
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
*/
@ -29,7 +33,13 @@ public class LittleEndianInputStream extends FilterInputStream implements Little
public LittleEndianInputStream(InputStream is) {
super(is);
}
public int available() {
try {
return super.available();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public byte readByte() {
return (byte)readUByte();
}
@ -131,34 +141,4 @@ public class LittleEndianInputStream extends FilterInputStream implements Little
buf[i] = (byte) ch;
}
}
public String readCompressedUnicode(int nChars) {
char[] buf = new char[nChars];
for (int i = 0; i < buf.length; i++) {
int ch;
try {
ch = in.read();
} catch (IOException e) {
throw new RuntimeException(e);
}
checkEOF(ch);
buf[i] = (char) ch;
}
return new String(buf);
}
public String readUnicodeLEString(int nChars) {
char[] buf = new char[nChars];
for (int i = 0; i < buf.length; i++) {
int ch;
try {
ch = in.read();
} catch (IOException e) {
throw new RuntimeException(e);
}
checkEOF(ch);
buf[i] = (char) ch;
}
return new String(buf);
}
}

View File

@ -20,9 +20,14 @@ package org.apache.poi.util;
import java.io.UnsupportedEncodingException;
import java.text.FieldPosition;
import java.text.NumberFormat;
import org.apache.poi.hssf.record.RecordInputStream;
/**
* Title: String Utility Description: Collection of string handling utilities
* Title: String Utility Description: Collection of string handling utilities<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 Sergei Kozello (sergeikozello at mail.ru)
@ -84,64 +89,11 @@ public class StringUtil {
* @param string the byte array to be converted
* @return the converted string
*/
public static String getFromUnicodeLE(final byte[] string) {
public static String getFromUnicodeLE(byte[] string) {
if(string.length == 0) { return ""; }
return getFromUnicodeLE(string, 0, string.length / 2);
}
/**
* Given a byte array of 16-bit unicode characters in big endian
* format (most important byte first), return a Java String representation
* of it.
*
* { 0x00, 0x16 } -0x16
*
* @param string the byte array to be converted
* @param offset the initial offset into the
* byte array. it is assumed that string[ offset ] and string[ offset +
* 1 ] contain the first 16-bit unicode character
* @param len the length of the final string
* @return the converted string
* @exception ArrayIndexOutOfBoundsException if offset is out of bounds for
* the byte array (i.e., is negative or is greater than or equal to
* string.length)
* @exception IllegalArgumentException if len is too large (i.e.,
* there is not enough data in string to create a String of that
* length)
*/
public static String getFromUnicodeBE(
final byte[] string,
final int offset,
final int len)
throws ArrayIndexOutOfBoundsException, IllegalArgumentException {
if ((offset < 0) || (offset >= string.length)) {
throw new ArrayIndexOutOfBoundsException("Illegal offset");
}
if ((len < 0) || (((string.length - offset) / 2) < len)) {
throw new IllegalArgumentException("Illegal length");
}
try {
return new String(string, offset, len * 2, "UTF-16BE");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
/**
* Given a byte array of 16-bit unicode characters in big endian
* format (most important byte first), return a Java String representation
* of it.
*
* { 0x00, 0x16 } -0x16
*
* @param string the byte array to be converted
* @return the converted string
*/
public static String getFromUnicodeBE(final byte[] string) {
if(string.length == 0) { return ""; }
return getFromUnicodeBE(string, 0, string.length / 2);
}
/**
* Read 8 bit data (in ISO-8859-1 codepage) into a (unicode) Java
* String and return.
@ -163,6 +115,52 @@ public class StringUtil {
throw new RuntimeException(e);
}
}
public static String readCompressedUnicode(LittleEndianInput in, int nChars) {
char[] buf = new char[nChars];
for (int i = 0; i < buf.length; i++) {
buf[i] = (char) in.readUByte();
}
return new String(buf);
}
/**
* InputStream <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
@ -221,6 +219,14 @@ public class StringUtil {
out.write(bytes);
}
public static String readUnicodeLE(LittleEndianInput in, int nChars) {
char[] buf = new char[nChars];
for (int i = 0; i < buf.length; i++) {
buf[i] = (char) in.readUShort();
}
return new String(buf);
}
/**
* Apply printf() like formatting to a string.
* Primarily used for logging.

View File

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

View File

@ -37,7 +37,7 @@ public final class TestCommonObjectDataSubRecord extends TestCase {
};
public void testLoad() {
CommonObjectDataSubRecord record = new CommonObjectDataSubRecord(TestcaseRecordInputStream.createWithFakeSid(data), data.length);
CommonObjectDataSubRecord record = new CommonObjectDataSubRecord(TestcaseRecordInputStream.createLittleEndian(data), data.length);
assertEquals( CommonObjectDataSubRecord.OBJECT_TYPE_LIST_BOX, record.getObjectType());
assertEquals((short) 1, record.getObjectId());

View File

@ -14,11 +14,12 @@
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record;
import java.io.ByteArrayInputStream;
import java.util.Arrays;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import org.apache.poi.util.HexRead;
@ -37,33 +38,43 @@ public final class TestEmbeddedObjectRefSubRecord extends TestCase {
public void testStore() {
byte[] src = hr(data1);
src = TestcaseRecordInputStream.mergeDataAndSid(EmbeddedObjectRefSubRecord.sid, (short)src.length, src);
// src = TestcaseRecordInputStream.mergeDataAndSid(EmbeddedObjectRefSubRecord.sid, (short)src.length, src);
RecordInputStream in = new RecordInputStream(new ByteArrayInputStream(src));
in.nextRecord();
RecordInputStream in = TestcaseRecordInputStream.create(EmbeddedObjectRefSubRecord.sid, src);
EmbeddedObjectRefSubRecord record1 = new EmbeddedObjectRefSubRecord(in, src.length-4);
EmbeddedObjectRefSubRecord record1 = new EmbeddedObjectRefSubRecord(in, src.length);
byte[] ser = record1.serialize();
RecordInputStream in2 = new RecordInputStream(new ByteArrayInputStream(ser));
in2.nextRecord();
RecordInputStream in2 = TestcaseRecordInputStream.create(ser);
EmbeddedObjectRefSubRecord record2 = new EmbeddedObjectRefSubRecord(in2, ser.length-4);
assertTrue(Arrays.equals(src, ser));
confirmData(src, ser);
assertEquals(record1.getOLEClassName(), record2.getOLEClassName());
byte[] ser2 = record1.serialize();
assertTrue(Arrays.equals(ser, ser2));
}
/**
* @param expectedData does not include sid & size
* @param actualFullRecordData includes sid & size
*/
private static void confirmData(byte[] expectedData, byte[] actualFullRecordData) {
assertEquals(expectedData.length, actualFullRecordData.length-4);
for (int i = 0; i < expectedData.length; i++) {
if(expectedData[i] != actualFullRecordData[i+4]) {
throw new AssertionFailedError("Difference at offset (" + i + ")");
}
}
}
public void testCreate() {
EmbeddedObjectRefSubRecord record1 = new EmbeddedObjectRefSubRecord();
byte[] ser = record1.serialize();
RecordInputStream in2 = new RecordInputStream(new ByteArrayInputStream(ser));
in2.nextRecord();
RecordInputStream in2 = TestcaseRecordInputStream.create(ser);
EmbeddedObjectRefSubRecord record2 = new EmbeddedObjectRefSubRecord(in2, ser.length-4);
assertEquals(record1.getOLEClassName(), record2.getOLEClassName());
@ -78,21 +89,17 @@ public final class TestEmbeddedObjectRefSubRecord extends TestCase {
* taken from ftPictFmla sub-record in attachment 22645 (offset 0x40AB).
*/
private static final byte[] data45912 = hr(
"09 00 14 00 " +
"12 00 0B 00 F8 02 88 04 3B 00 " +
"00 00 00 01 00 00 00 01 " +
"00 00");
public void testCameraTool_bug45912() {
byte[] data = data45912;
RecordInputStream in = new RecordInputStream(new ByteArrayInputStream(data));
in.nextRecord();
RecordInputStream in = TestcaseRecordInputStream.create(EmbeddedObjectRefSubRecord.sid, data);
EmbeddedObjectRefSubRecord rec = new EmbeddedObjectRefSubRecord(in, data.length-4);
EmbeddedObjectRefSubRecord rec = new EmbeddedObjectRefSubRecord(in, data.length);
byte[] ser2 = rec.serialize();
assertTrue(Arrays.equals(data, ser2));
confirmData(data, ser2);
}
private static byte[] hr(String string) {

View File

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

View File

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

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

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() {
RecordInputStream in = new RecordInputStream(new ByteArrayInputStream(data));
in.nextRecord();
RecordInputStream in = TestcaseRecordInputStream.create(data);
TextObjectRecord record = new TextObjectRecord(in);
assertEquals(TextObjectRecord.HORIZONTAL_TEXT_ALIGNMENT_CENTERED, record.getHorizontalTextAlignment());
assertEquals(TextObjectRecord.VERTICAL_TEXT_ALIGNMENT_JUSTIFY, record.getVerticalTextAlignment());
assertEquals(true, record.isTextLocked());

View File

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

View File

@ -23,6 +23,8 @@ import java.io.InputStream;
import junit.framework.Assert;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.LittleEndianByteArrayInputStream;
import org.apache.poi.util.LittleEndianInput;
/**
* A Record Input Stream derivative that makes access to byte arrays used in the
@ -40,8 +42,8 @@ public final class TestcaseRecordInputStream {
/**
* Prepends a mock record identifier to the supplied data and opens a record input stream
*/
public static RecordInputStream createWithFakeSid(byte[] data) {
return create(-5555, data);
public static LittleEndianInput createLittleEndian(byte[] data) {
return new LittleEndianByteArrayInputStream(data);
}
public static RecordInputStream create(int sid, byte[] data) {

View File

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

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

View File

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

View File

@ -34,7 +34,7 @@ public final class TestFuncPtg extends TestCase {
0,
};
FuncPtg ptg = new FuncPtg(TestcaseRecordInputStream.createWithFakeSid(fakeData) );
FuncPtg ptg = new FuncPtg(TestcaseRecordInputStream.createLittleEndian(fakeData) );
assertEquals( "Len formula index is not 32(20H)", 0x20, ptg.getFunctionIndex() );
assertEquals( "Number of operands in the len formula", 1, ptg.getNumberOfOperands() );
assertEquals( "Function Name", "LEN", ptg.getName() );

View File

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

View File

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

View File

@ -27,7 +27,7 @@ import junit.framework.TestSuite;
public final class AllPOIFSStorageTests {
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(TestBlockAllocationTableReader.class);
result.addTestSuite(TestBlockAllocationTableWriter.class);

View File

@ -1,4 +1,3 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@ -16,24 +15,20 @@
limitations under the License.
==================================================================== */
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.*;
import junit.framework.TestCase;
/**
* Class to test DocumentBlock functionality
*
* @author Marc Johnson
*/
public class TestDocumentBlock
extends TestCase
{
public final class TestDocumentBlock extends TestCase {
static final private byte[] _testdata;
static
@ -44,25 +39,10 @@ public class TestDocumentBlock
_testdata[ j ] = ( byte ) j;
}
}
;
/**
* Constructor TestDocumentBlock
*
* @param name
*/
public TestDocumentBlock(String name)
{
super(name);
}
/**
* Test the writing DocumentBlock constructor.
*
* @exception IOException
*/
public void testConstructor()
throws IOException
{
@ -88,46 +68,10 @@ public class TestDocumentBlock
assertEquals(_testdata.length, size);
}
/**
* test static read method
*
* @exception IOException
*/
public void testRead()
throws IOException
{
DocumentBlock[] blocks = new DocumentBlock[ 4 ];
ByteArrayInputStream input = new ByteArrayInputStream(_testdata);
for (int j = 0; j < 4; j++)
{
blocks[ j ] = new DocumentBlock(input);
}
for (int j = 1; j <= 2000; j += 17)
{
byte[] buffer = new byte[ j ];
int offset = 0;
for (int k = 0; k < (2000 / j); k++)
{
DocumentBlock.read(blocks, buffer, offset);
for (int n = 0; n < buffer.length; n++)
{
assertEquals("checking byte " + (k * j) + n,
_testdata[ (k * j) + n ], buffer[ n ]);
}
offset += j;
}
}
}
/**
* Test 'reading' constructor
*
* @exception IOException
*/
public void testReadingConstructor()
throws IOException
{
@ -164,17 +108,4 @@ public class TestDocumentBlock
assertEquals(( byte ) 0xFF, copy[ j ]);
}
}
/**
* main method to run the unit tests
*
* @param ignored_args
*/
public static void main(String [] ignored_args)
{
System.out
.println("Testing org.apache.poi.poifs.storage.DocumentBlock");
junit.textui.TestRunner.run(TestDocumentBlock.class);
}
}

View File

@ -1,4 +1,3 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@ -16,24 +15,23 @@
limitations under the License.
==================================================================== */
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.*;
import junit.framework.TestCase;
/**
* Class to test SmallDocumentBlock functionality
*
* @author Marc Johnson
*/
public class TestSmallDocumentBlock
extends TestCase
{
public final class TestSmallDocumentBlock extends TestCase {
static final private byte[] _testdata;
static final private int _testdata_size = 2999;
@ -45,25 +43,10 @@ public class TestSmallDocumentBlock
_testdata[ j ] = ( byte ) j;
}
}
;
/**
* constructor
*
* @param name
*/
public TestSmallDocumentBlock(String name)
{
super(name);
}
/**
* Test conversion from DocumentBlocks
*
* @exception IOException
*/
public void testConvert1()
throws IOException
{
@ -113,12 +96,7 @@ public class TestSmallDocumentBlock
/**
* Test conversion from byte array
*
* @exception IOException;
*
* @exception IOException
*/
public void testConvert2()
throws IOException
{
@ -154,57 +132,9 @@ public class TestSmallDocumentBlock
}
}
/**
* Test read method
*
* @exception IOException
*/
public void testRead()
throws IOException
{
ByteArrayInputStream stream = new ByteArrayInputStream(_testdata);
List documents = new ArrayList();
while (true)
{
DocumentBlock block = new DocumentBlock(stream);
documents.add(block);
if (block.partiallyRead())
{
break;
}
}
SmallDocumentBlock[] blocks =
SmallDocumentBlock
.convert(( BlockWritable [] ) documents
.toArray(new DocumentBlock[ 0 ]), _testdata_size);
for (int j = 1; j <= _testdata_size; j += 38)
{
byte[] buffer = new byte[ j ];
int offset = 0;
for (int k = 0; k < (_testdata_size / j); k++)
{
SmallDocumentBlock.read(blocks, buffer, offset);
for (int n = 0; n < buffer.length; n++)
{
assertEquals("checking byte " + (k * j) + n,
_testdata[ (k * j) + n ], buffer[ n ]);
}
offset += j;
}
}
}
/**
* test fill
*
* @exception IOException
*/
public void testFill()
throws IOException
{
@ -294,17 +224,4 @@ public class TestSmallDocumentBlock
}
}
}
/**
* main method to run the unit tests
*
* @param ignored_args
*/
public static void main(String [] ignored_args)
{
System.out.println(
"Testing org.apache.poi.poifs.storage.SmallDocumentBlock");
junit.textui.TestRunner.run(TestSmallDocumentBlock.class);
}
}

View File

@ -27,12 +27,11 @@ import junit.framework.TestSuite;
public final class AllPOIUtilTests {
public static Test suite() {
TestSuite result = new TestSuite("Tests for org.apache.poi.util");
TestSuite result = new TestSuite(AllPOIUtilTests.class.getName());
result.addTestSuite(TestArrayUtil.class);
result.addTestSuite(TestBinaryTree.class);
result.addTestSuite(TestBitField.class);
result.addTestSuite(TestByteField.class);
result.addTestSuite(TestDoubleList2d.class);
result.addTestSuite(TestHexDump.class);
result.addTestSuite(TestIntegerField.class);
result.addTestSuite(TestIntList.class);

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

View File

@ -42,43 +42,7 @@ public class TestStringUtil
super( name );
}
/**
* test simple form of getFromUnicode
*/
public void testSimpleGetFromUnicode()
{
byte[] test_data = new byte[32];
int index = 0;
for ( int k = 0; k < 16; k++ )
{
test_data[index++] = (byte) 0;
test_data[index++] = (byte) ( 'a' + k );
}
assertEquals( "abcdefghijklmnop",
StringUtil.getFromUnicodeBE( test_data ) );
}
/**
* test simple form of getFromUnicode with symbols with code below and more 127
*/
public void testGetFromUnicodeSymbolsWithCodesMoreThan127()
{
byte[] test_data = new byte[]{0x04, 0x22,
0x04, 0x35,
0x04, 0x41,
0x04, 0x42,
0x00, 0x20,
0x00, 0x74,
0x00, 0x65,
0x00, 0x73,
0x00, 0x74,
};
assertEquals( "\u0422\u0435\u0441\u0442 test",
StringUtil.getFromUnicodeBE( test_data ) );
}
/**
* test getFromUnicodeHigh for symbols with code below and more 127
@ -101,62 +65,7 @@ public class TestStringUtil
StringUtil.getFromUnicodeLE( test_data ) );
}
/**
* Test more complex form of getFromUnicode
*/
public void testComplexGetFromUnicode()
{
byte[] test_data = new byte[32];
int index = 0;
for ( int k = 0; k < 16; k++ )
{
test_data[index++] = (byte) 0;
test_data[index++] = (byte) ( 'a' + k );
}
assertEquals( "abcdefghijklmno",
StringUtil.getFromUnicodeBE( test_data, 0, 15 ) );
assertEquals( "bcdefghijklmnop",
StringUtil.getFromUnicodeBE( test_data, 2, 15 ) );
try
{
StringUtil.getFromUnicodeBE( test_data, -1, 16 );
fail( "Should have caught ArrayIndexOutOfBoundsException" );
}
catch ( ArrayIndexOutOfBoundsException ignored )
{
// as expected
}
try
{
StringUtil.getFromUnicodeBE( test_data, 32, 16 );
fail( "Should have caught ArrayIndexOutOfBoundsException" );
}
catch ( ArrayIndexOutOfBoundsException ignored )
{
// as expected
}
try
{
StringUtil.getFromUnicodeBE( test_data, 1, 16 );
fail( "Should have caught IllegalArgumentException" );
}
catch ( IllegalArgumentException ignored )
{
// as expected
}
try
{
StringUtil.getFromUnicodeBE( test_data, 1, -1 );
fail( "Should have caught IllegalArgumentException" );
}
catch ( IllegalArgumentException ignored )
{
// as expected
}
}
/**
* Test putCompressedUnicode