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
tags/ooxml_20081107
@@ -1,4 +1,3 @@ | |||
/* ==================================================================== | |||
Licensed to the Apache Software Foundation (ASF) under one or more | |||
contributor license agreements. See the NOTICE file distributed with | |||
@@ -15,11 +14,9 @@ | |||
See the License for the specific language governing permissions and | |||
limitations under the License. | |||
==================================================================== */ | |||
package org.apache.poi.contrib.poibrowser; | |||
import java.io.IOException; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
@@ -160,17 +157,7 @@ public class TreeReaderListener implements POIFSReaderListener | |||
throw new RuntimeException(t.getMessage()); | |||
} | |||
try | |||
{ | |||
is.close(); | |||
} | |||
catch (IOException ex) | |||
{ | |||
System.err.println | |||
("Unexpected exception while closing " + | |||
event.getName() + " in " + event.getPath().toString()); | |||
ex.printStackTrace(System.err); | |||
} | |||
is.close(); | |||
final MutableTreeNode parentNode = getNode(d.path, filename, rootNode); | |||
final MutableTreeNode nameNode = new DefaultMutableTreeNode(d.name); |
@@ -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; | |||
} |
@@ -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() { |
@@ -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); |
@@ -51,7 +51,7 @@ public final class LinkedDataFormulaField { | |||
.append( "=" ) | |||
.append(ptg.toString() ) | |||
.append( "\n" ) | |||
.append(ptg.toDebugString() ) | |||
.append(ptg.toString()) | |||
.append( "\n" ); | |||
} | |||
} |
@@ -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,115 +34,147 @@ public final class RecordInputStream extends InputStream implements LittleEndian | |||
/** Maximum size of a single record (minus the 4 byte header) without a continue*/ | |||
public final static short MAX_RECORD_DATA_SIZE = 8224; | |||
private static final int INVALID_SID_VALUE = -1; | |||
/** | |||
* When {@link #_currentDataLength} has this value, it means that the previous BIFF record is | |||
* finished, the next sid has been properly read, but the data size field has not been read yet. | |||
*/ | |||
private static final int DATA_LEN_NEEDS_TO_BE_READ = -1; | |||
private static final byte[] EMPTY_BYTE_ARRAY = { }; | |||
/** {@link LittleEndianInput} facet of the wrapped {@link InputStream} */ | |||
private final LittleEndianInput _le; | |||
/** the record identifier of the BIFF record currently being read */ | |||
private int _currentSid; | |||
/** | |||
* Length of the data section of the current BIFF record (always 4 less than the total record size). | |||
* When uninitialised, this field is set to {@link #DATA_LEN_NEEDS_TO_BE_READ}. | |||
*/ | |||
private int _currentDataLength; | |||
/** | |||
* The BIFF record identifier for the next record is read when just as the current record | |||
* is finished. | |||
* This field is only really valid during the time that ({@link #_currentDataLength} == | |||
* {@link #DATA_LEN_NEEDS_TO_BE_READ}). At most other times its value is not really the | |||
* 'sid of the next record'. Wwhile mid-record, this field coincidentally holds the sid | |||
* of the current record. | |||
*/ | |||
private int _nextSid; | |||
/** | |||
* index within the data section of the current BIFF record | |||
*/ | |||
private int _currentDataOffset; | |||
public RecordInputStream(InputStream in) throws RecordFormatException { | |||
if (in instanceof LittleEndianInput) { | |||
// accessing directly is an optimisation | |||
_le = (LittleEndianInput) in; | |||
} else { | |||
// less optimal, but should work OK just the same. Often occurs in junit tests. | |||
_le = new LittleEndianInputStream(in); | |||
} | |||
_nextSid = readNextSid(); | |||
} | |||
/** | |||
* @returns the number of bytes available in the current BIFF record | |||
* @see #remaining() | |||
*/ | |||
public int available() { | |||
return remaining(); | |||
} | |||
private InputStream in; | |||
private short currentSid; | |||
private short currentLength = -1; | |||
private short nextSid; | |||
private final byte[] data = new byte[MAX_RECORD_DATA_SIZE]; | |||
private short recordOffset; | |||
private long pos; | |||
private boolean autoContinue = true; | |||
public RecordInputStream(InputStream in) throws RecordFormatException { | |||
this.in = in; | |||
try { | |||
nextSid = LittleEndian.readShort(in); | |||
//Don't increment the pos just yet (technically we are at the start of | |||
//the record stream until nextRecord is called). | |||
} catch (IOException ex) { | |||
throw new RecordFormatException("Error reading bytes", ex); | |||
} | |||
} | |||
/** This method will read a byte from the current record*/ | |||
public int read() { | |||
checkRecordPosition(LittleEndian.BYTE_SIZE); | |||
byte result = data[recordOffset]; | |||
recordOffset += LittleEndian.BYTE_SIZE; | |||
pos += LittleEndian.BYTE_SIZE; | |||
return result; | |||
_currentDataOffset += LittleEndian.BYTE_SIZE; | |||
return _le.readUByte(); | |||
} | |||
public int read(byte[] b, int off, int len) { | |||
int limit = Math.min(len, remaining()); | |||
if (limit == 0) { | |||
return 0; | |||
} | |||
readFully(b, off,limit); | |||
return limit; | |||
} | |||
public short getSid() { | |||
return currentSid; | |||
} | |||
public short getLength() { | |||
return currentLength; | |||
} | |||
public short getRecordOffset() { | |||
return recordOffset; | |||
} | |||
public long getPos() { | |||
return pos; | |||
} | |||
public boolean hasNextRecord() { | |||
return nextSid != INVALID_SID_VALUE; | |||
} | |||
public short getSid() { | |||
return (short) _currentSid; | |||
} | |||
/** Moves to the next record in the stream. | |||
* | |||
* <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)); | |||
} | |||
currentSid = nextSid; | |||
pos += LittleEndian.SHORT_SIZE; | |||
autoContinue = true; | |||
try { | |||
recordOffset = 0; | |||
currentLength = LittleEndian.readShort(in); | |||
if (currentLength > MAX_RECORD_DATA_SIZE) | |||
throw new RecordFormatException("The content of an excel record cannot exceed "+MAX_RECORD_DATA_SIZE+" bytes"); | |||
pos += LittleEndian.SHORT_SIZE; | |||
in.read(data, 0, currentLength); | |||
//Read the Sid of the next record | |||
if (in.available() < EOFRecord.ENCODED_SIZE) { | |||
if (in.available() > 0) { | |||
// some scrap left over? | |||
// ex45582-22397.xls has one extra byte after the last record | |||
// Excel reads that file OK | |||
} | |||
nextSid = INVALID_SID_VALUE; | |||
} else { | |||
nextSid = LittleEndian.readShort(in); | |||
if (nextSid == INVALID_SID_VALUE) { | |||
throw new RecordFormatException("Found sid " + nextSid + " after record with sid 0x" | |||
+ Integer.toHexString(currentSid).toUpperCase()); | |||
} | |||
} | |||
} catch (IOException ex) { | |||
throw new RecordFormatException("Error reading bytes", ex); | |||
} | |||
} | |||
/** | |||
* Note - this method is expected to be called only when completed reading the current BIFF record. | |||
* Calling this before reaching the end of the current record will cause all remaining data to be | |||
* discarded | |||
*/ | |||
public boolean hasNextRecord() { | |||
if (_currentDataLength != -1 && _currentDataLength != _currentDataOffset) { | |||
System.out.println("WARN. Unread "+remaining()+" bytes of record 0x"+Integer.toHexString(_currentSid)); | |||
// discard unread data | |||
while (_currentDataOffset < _currentDataLength) { | |||
readByte(); | |||
} | |||
} | |||
if (_currentDataLength != DATA_LEN_NEEDS_TO_BE_READ) { | |||
_nextSid = readNextSid(); | |||
} | |||
return _nextSid != INVALID_SID_VALUE; | |||
} | |||
public void setAutoContinue(boolean enable) { | |||
this.autoContinue = enable; | |||
} | |||
/** | |||
* | |||
* @return the sid of the next record or {@link #INVALID_SID_VALUE} if at end of stream | |||
*/ | |||
private int readNextSid() { | |||
int nAvailable = _le.available(); | |||
if (nAvailable < EOFRecord.ENCODED_SIZE) { | |||
if (nAvailable > 0) { | |||
// some scrap left over? | |||
// ex45582-22397.xls has one extra byte after the last record | |||
// Excel reads that file OK | |||
} | |||
return INVALID_SID_VALUE; | |||
} | |||
int result = _le.readUShort(); | |||
if (result == INVALID_SID_VALUE) { | |||
throw new RecordFormatException("Found invalid sid (" + result + ")"); | |||
} | |||
_currentDataLength = DATA_LEN_NEEDS_TO_BE_READ; | |||
return result; | |||
} | |||
public boolean getAutoContinue() { | |||
return autoContinue; | |||
} | |||
/** Moves to the next record in the stream. | |||
* | |||
* <i>Note: The auto continue flag is reset to true</i> | |||
*/ | |||
public void nextRecord() throws RecordFormatException { | |||
if (_nextSid == INVALID_SID_VALUE) { | |||
throw new IllegalStateException("EOF - next record not available"); | |||
} | |||
if (_currentDataLength != DATA_LEN_NEEDS_TO_BE_READ) { | |||
throw new IllegalStateException("Cannot call nextRecord() without checking hasNextRecord() first"); | |||
} | |||
_currentSid = _nextSid; | |||
_currentDataOffset = 0; | |||
_currentDataLength = _le.readUShort(); | |||
if (_currentDataLength > MAX_RECORD_DATA_SIZE) { | |||
throw new RecordFormatException("The content of an excel record cannot exceed " | |||
+ MAX_RECORD_DATA_SIZE + " bytes"); | |||
} | |||
} | |||
private void checkRecordPosition(int requiredByteCount) { | |||
if (remaining() < requiredByteCount) { | |||
if (isContinueNext() && autoContinue) { | |||
nextRecord(); | |||
} else { | |||
throw new ArrayIndexOutOfBoundsException(); | |||
} | |||
int nAvailable = remaining(); | |||
if (nAvailable >= requiredByteCount) { | |||
// all OK | |||
return; | |||
} | |||
if (nAvailable == 0 && isContinueNext()) { | |||
nextRecord(); | |||
return; | |||
} | |||
throw new RecordFormatException("Not enough data (" + nAvailable | |||
+ ") to read requested (" + requiredByteCount +") bytes"); | |||
} | |||
/** | |||
@@ -150,11 +182,8 @@ public final class RecordInputStream extends InputStream implements LittleEndian | |||
*/ | |||
public byte readByte() { | |||
checkRecordPosition(LittleEndian.BYTE_SIZE); | |||
byte result = data[recordOffset]; | |||
recordOffset += LittleEndian.BYTE_SIZE; | |||
pos += LittleEndian.BYTE_SIZE; | |||
return result; | |||
_currentDataOffset += LittleEndian.BYTE_SIZE; | |||
return _le.readByte(); | |||
} | |||
/** | |||
@@ -162,29 +191,20 @@ public final class RecordInputStream extends InputStream implements LittleEndian | |||
*/ | |||
public short readShort() { | |||
checkRecordPosition(LittleEndian.SHORT_SIZE); | |||
short result = LittleEndian.getShort(data, recordOffset); | |||
recordOffset += LittleEndian.SHORT_SIZE; | |||
pos += LittleEndian.SHORT_SIZE; | |||
return result; | |||
_currentDataOffset += LittleEndian.SHORT_SIZE; | |||
return _le.readShort(); | |||
} | |||
public int readInt() { | |||
checkRecordPosition(LittleEndian.INT_SIZE); | |||
int result = LittleEndian.getInt(data, recordOffset); | |||
recordOffset += LittleEndian.INT_SIZE; | |||
pos += LittleEndian.INT_SIZE; | |||
return result; | |||
_currentDataOffset += LittleEndian.INT_SIZE; | |||
return _le.readInt(); | |||
} | |||
public long readLong() { | |||
checkRecordPosition(LittleEndian.LONG_SIZE); | |||
long result = LittleEndian.getLong(data, recordOffset); | |||
recordOffset += LittleEndian.LONG_SIZE; | |||
pos += LittleEndian.LONG_SIZE; | |||
return result; | |||
_currentDataOffset += LittleEndian.LONG_SIZE; | |||
return _le.readLong(); | |||
} | |||
/** | |||
@@ -200,22 +220,18 @@ public final class RecordInputStream extends InputStream implements LittleEndian | |||
*/ | |||
public int readUShort() { | |||
checkRecordPosition(LittleEndian.SHORT_SIZE); | |||
int result = LittleEndian.getUShort(data, recordOffset); | |||
recordOffset += LittleEndian.SHORT_SIZE; | |||
pos += LittleEndian.SHORT_SIZE; | |||
return result; | |||
_currentDataOffset += LittleEndian.SHORT_SIZE; | |||
return _le.readUShort(); | |||
} | |||
public double readDouble() { | |||
checkRecordPosition(LittleEndian.DOUBLE_SIZE); | |||
long valueLongBits = LittleEndian.getLong(data, recordOffset); | |||
_currentDataOffset += LittleEndian.DOUBLE_SIZE; | |||
long valueLongBits = _le.readLong(); | |||
double result = Double.longBitsToDouble(valueLongBits); | |||
if (Double.isNaN(result)) { | |||
throw new RuntimeException("Did not expect to read NaN"); // (Because Excel typically doesn't write NaN | |||
} | |||
recordOffset += LittleEndian.DOUBLE_SIZE; | |||
pos += LittleEndian.DOUBLE_SIZE; | |||
return result; | |||
} | |||
public void readFully(byte[] buf) { | |||
@@ -224,9 +240,8 @@ public final class RecordInputStream extends InputStream implements LittleEndian | |||
public void readFully(byte[] buf, int off, int len) { | |||
checkRecordPosition(len); | |||
System.arraycopy(data, recordOffset, buf, off, len); | |||
recordOffset+=len; | |||
pos+=len; | |||
_le.readFully(buf, off, len); | |||
_currentDataOffset+=len; | |||
} | |||
public String readString() { | |||
@@ -315,18 +330,19 @@ public final class RecordInputStream extends InputStream implements LittleEndian | |||
return new UnicodeString(this); | |||
} | |||
/** Returns the remaining bytes for the current record. | |||
* | |||
* @return The remaining bytes of the current record. | |||
*/ | |||
public byte[] readRemainder() { | |||
int size = remaining(); | |||
byte[] result = new byte[size]; | |||
System.arraycopy(data, recordOffset, result, 0, size); | |||
recordOffset += size; | |||
pos += size; | |||
return result; | |||
} | |||
/** Returns the remaining bytes for the current record. | |||
* | |||
* @return The remaining bytes of the current record. | |||
*/ | |||
public byte[] readRemainder() { | |||
int size = remaining(); | |||
if (size ==0) { | |||
return EMPTY_BYTE_ARRAY; | |||
} | |||
byte[] result = new byte[size]; | |||
readFully(result); | |||
return result; | |||
} | |||
/** Reads all byte data for the current record, including any | |||
* that overlaps into any following continue records. | |||
@@ -350,19 +366,34 @@ public final class RecordInputStream extends InputStream implements LittleEndian | |||
return out.toByteArray(); | |||
} | |||
/** The remaining number of bytes in the <i>current</i> record. | |||
* | |||
* @return The number of bytes remaining in the current record | |||
*/ | |||
public int remaining() { | |||
return (currentLength - recordOffset); | |||
} | |||
/** The remaining number of bytes in the <i>current</i> record. | |||
* | |||
* @return The number of bytes remaining in the current record | |||
*/ | |||
public int remaining() { | |||
if (_currentDataLength == DATA_LEN_NEEDS_TO_BE_READ) { | |||
// already read sid of next record. so current one is finished | |||
return 0; | |||
} | |||
return _currentDataLength - _currentDataOffset; | |||
} | |||
/** Returns true iif a Continue record is next in the excel stream | |||
* | |||
* @return True when a ContinueRecord is next. | |||
*/ | |||
public boolean isContinueNext() { | |||
return (nextSid == ContinueRecord.sid); | |||
} | |||
/** | |||
* | |||
* @return <code>true</code> when a {@link ContinueRecord} is next. | |||
*/ | |||
private boolean isContinueNext() { | |||
if (_currentDataLength != DATA_LEN_NEEDS_TO_BE_READ && _currentDataOffset != _currentDataLength) { | |||
throw new IllegalStateException("Should never be called before end of current record"); | |||
} | |||
if (!hasNextRecord()) { | |||
return false; | |||
} | |||
// At what point are records continued? | |||
// - Often from within the char data of long strings (caller is within readStringCommon()). | |||
// - From UnicodeString construction (many different points - call via checkRecordPosition) | |||
// - During TextObjectRecord construction (just before the text, perhaps within the text, | |||
// and before the formatting run data) | |||
return _nextSid == ContinueRecord.sid; | |||
} | |||
} |
@@ -19,332 +19,181 @@ package org.apache.poi.hssf.record; | |||
import org.apache.poi.util.BitField; | |||
import org.apache.poi.util.BitFieldFactory; | |||
import org.apache.poi.util.HexDump; | |||
import org.apache.poi.util.LittleEndian; | |||
import org.apache.poi.util.StringUtil; | |||
/** | |||
* Title: Style Record<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); | |||
public final static short STYLE_USER_DEFINED = 0; | |||
public final static short STYLE_BUILT_IN = 1; | |||
// shared by both user defined and builtin styles | |||
private short field_1_xf_index; // TODO: bitfield candidate | |||
// only for built in styles | |||
private byte field_2_builtin_style; | |||
private byte field_3_outline_style_level; | |||
// only for user defined styles | |||
private short field_2_name_length; //OO doc says 16 bit length, so we believe | |||
private byte field_3_string_options; | |||
private String field_4_name; | |||
public StyleRecord() | |||
{ | |||
} | |||
public StyleRecord(RecordInputStream in) | |||
{ | |||
field_1_xf_index = in.readShort(); | |||
if (getType() == STYLE_BUILT_IN) | |||
{ | |||
field_2_builtin_style = in.readByte(); | |||
field_3_outline_style_level = in.readByte(); | |||
} | |||
else if (getType() == STYLE_USER_DEFINED) | |||
{ | |||
field_2_name_length = in.readShort(); | |||
// Some files from Crystal Reports lack | |||
// the remaining fields, which is naughty | |||
if(in.remaining() > 0) { | |||
field_3_string_options = in.readByte(); | |||
byte[] string = in.readRemainder(); | |||
if (fHighByte.isSet(field_3_string_options)) { | |||
field_4_name= StringUtil.getFromUnicodeBE(string, 0, field_2_name_length); | |||
} else { | |||
field_4_name=StringUtil.getFromCompressedUnicode(string, 0, field_2_name_length); | |||
} | |||
} | |||
} | |||
// todo sanity check exception to make sure we're one or the other | |||
} | |||
/** | |||
* set the entire index field (including the type) (see bit setters that reference this method) | |||
* @param index bitmask | |||
*/ | |||
public void setIndex(short index) | |||
{ | |||
field_1_xf_index = index; | |||
} | |||
// bitfields for field 1 | |||
/** | |||
* set the type of the style (builtin or user-defined) | |||
* @see #STYLE_USER_DEFINED | |||
* @see #STYLE_BUILT_IN | |||
* @param type of style (userdefined/builtin) | |||
* @see #setIndex(short) | |||
*/ | |||
public void setType(short type) | |||
{ | |||
field_1_xf_index = setField(field_1_xf_index, type, 0x8000, 15); | |||
} | |||
/** | |||
* set the actual index of the style extended format record | |||
* @see #setIndex(short) | |||
* @param index of the xf record | |||
*/ | |||
public void setXFIndex(short index) | |||
{ | |||
field_1_xf_index = setField(field_1_xf_index, index, 0x1FFF, 0); | |||
} | |||
// end bitfields | |||
// only for user defined records | |||
/** | |||
* if this is a user defined record set the length of the style name | |||
* @param length of the style's name | |||
* @see #setName(String) | |||
*/ | |||
public void setNameLength(byte length) | |||
{ | |||
field_2_name_length = length; | |||
} | |||
/** | |||
* set the style's name | |||
* @param name of the style | |||
* @see #setNameLength(byte) | |||
*/ | |||
public void setName(String name) | |||
{ | |||
field_4_name = name; | |||
// Fix up the length | |||
field_2_name_length = (short)name.length(); | |||
//TODO set name string options | |||
} | |||
// end user defined | |||
// only for buildin records | |||
/** | |||
* if this is a builtin style set teh number of the built in style | |||
* @param builtin style number (0-7) | |||
* | |||
*/ | |||
public void setBuiltin(byte builtin) | |||
{ | |||
field_2_builtin_style = builtin; | |||
} | |||
/** | |||
* set the row or column level of the style (if builtin 1||2) | |||
*/ | |||
public void setOutlineStyleLevel(byte level) | |||
{ | |||
field_3_outline_style_level = level; | |||
} | |||
// end builtin records | |||
// field 1 | |||
/** | |||
* get the entire index field (including the type) (see bit getters that reference this method) | |||
* @return bitmask | |||
*/ | |||
public short getIndex() | |||
{ | |||
return field_1_xf_index; | |||
} | |||
// bitfields for field 1 | |||
/** | |||
* get the type of the style (builtin or user-defined) | |||
* @see #STYLE_USER_DEFINED | |||
* @see #STYLE_BUILT_IN | |||
* @return type of style (userdefined/builtin) | |||
* @see #getIndex() | |||
*/ | |||
public short getType() | |||
{ | |||
return ( short ) ((field_1_xf_index & 0x8000) >> 15); | |||
} | |||
/** | |||
* get the actual index of the style extended format record | |||
* @see #getIndex() | |||
* @return index of the xf record | |||
*/ | |||
public short getXFIndex() | |||
{ | |||
return ( short ) (field_1_xf_index & 0x1FFF); | |||
} | |||
// end bitfields | |||
// only for user defined records | |||
/** | |||
* if this is a user defined record get the length of the style name | |||
* @return length of the style's name | |||
* @see #getName() | |||
*/ | |||
public short getNameLength() | |||
{ | |||
return field_2_name_length; | |||
} | |||
/** | |||
* get the style's name | |||
* @return name of the style | |||
* @see #getNameLength() | |||
*/ | |||
public String getName() | |||
{ | |||
return field_4_name; | |||
} | |||
// end user defined | |||
// only for buildin records | |||
/** | |||
* if this is a builtin style get the number of the built in style | |||
* @return builtin style number (0-7) | |||
* | |||
*/ | |||
public byte getBuiltin() | |||
{ | |||
return field_2_builtin_style; | |||
} | |||
/** | |||
* get the row or column level of the style (if builtin 1||2) | |||
*/ | |||
public byte getOutlineStyleLevel() | |||
{ | |||
return field_3_outline_style_level; | |||
} | |||
// end builtin records | |||
public String toString() | |||
{ | |||
StringBuffer buffer = new StringBuffer(); | |||
buffer.append("[STYLE]\n"); | |||
buffer.append(" .xf_index_raw = ") | |||
.append(Integer.toHexString(getIndex())).append("\n"); | |||
buffer.append(" .type = ") | |||
.append(Integer.toHexString(getType())).append("\n"); | |||
buffer.append(" .xf_index = ") | |||
.append(Integer.toHexString(getXFIndex())).append("\n"); | |||
if (getType() == STYLE_BUILT_IN) | |||
{ | |||
buffer.append(" .builtin_style = ") | |||
.append(Integer.toHexString(getBuiltin())).append("\n"); | |||
buffer.append(" .outline_level = ") | |||
.append(Integer.toHexString(getOutlineStyleLevel())) | |||
.append("\n"); | |||
} | |||
else if (getType() == STYLE_USER_DEFINED) | |||
{ | |||
buffer.append(" .name_length = ") | |||
.append(Integer.toHexString(getNameLength())).append("\n"); | |||
buffer.append(" .name = ").append(getName()) | |||
.append("\n"); | |||
} | |||
buffer.append("[/STYLE]\n"); | |||
return buffer.toString(); | |||
} | |||
private short setField(int fieldValue, int new_value, int mask, | |||
int shiftLeft) | |||
{ | |||
return ( short ) ((fieldValue & ~mask) | |||
| ((new_value << shiftLeft) & mask)); | |||
} | |||
public int serialize(int offset, byte [] data) | |||
{ | |||
LittleEndian.putShort(data, 0 + offset, sid); | |||
if (getType() == STYLE_BUILT_IN) | |||
{ | |||
LittleEndian.putShort(data, 2 + offset, | |||
(( short ) 0x04)); // 4 bytes (8 total) | |||
} | |||
else | |||
{ | |||
LittleEndian.putShort(data, 2 + offset, | |||
(( short ) (getRecordSize()-4))); | |||
} | |||
LittleEndian.putShort(data, 4 + offset, getIndex()); | |||
if (getType() == STYLE_BUILT_IN) | |||
{ | |||
data[ 6 + offset ] = getBuiltin(); | |||
data[ 7 + offset ] = getOutlineStyleLevel(); | |||
} | |||
else | |||
{ | |||
LittleEndian.putShort(data, 6 + offset , getNameLength()); | |||
data[8+offset]=this.field_3_string_options; | |||
StringUtil.putCompressedUnicode(getName(), data, 9 + offset); | |||
} | |||
return getRecordSize(); | |||
} | |||
public int getRecordSize() | |||
{ | |||
int retval; | |||
if (getType() == STYLE_BUILT_IN) | |||
{ | |||
retval = 8; | |||
} | |||
else | |||
{ | |||
if (fHighByte.isSet(field_3_string_options)) { | |||
retval= 9+2*getNameLength(); | |||
}else { | |||
retval = 9 + getNameLength(); | |||
} | |||
} | |||
return retval; | |||
} | |||
public short getSid() | |||
{ | |||
return sid; | |||
} | |||
public final static short sid = 0x0293; | |||
private static final BitField is16BitUnicodeFlag = BitFieldFactory.getInstance(0x01); | |||
private static final BitField styleIndexMask = BitFieldFactory.getInstance(0x0FFF); | |||
private static final BitField isBuiltinFlag = BitFieldFactory.getInstance(0x8000); | |||
/** shared by both user defined and built-in styles */ | |||
private int field_1_xf_index; | |||
// only for built in styles | |||
private int field_2_builtin_style; | |||
private int field_3_outline_style_level; | |||
// only for user defined styles | |||
private int field_3_string_options; | |||
private String field_4_name; | |||
/** | |||
* creates a new style record, initially set to 'built-in' | |||
*/ | |||
public StyleRecord() { | |||
field_1_xf_index = isBuiltinFlag.set(field_1_xf_index); | |||
} | |||
public StyleRecord(RecordInputStream in) { | |||
field_1_xf_index = in.readShort(); | |||
if (isBuiltin()) { | |||
field_2_builtin_style = in.readByte(); | |||
field_3_outline_style_level = in.readByte(); | |||
} else { | |||
int field_2_name_length = in.readShort(); | |||
if(in.remaining() < 1) { | |||
// Some files from Crystal Reports lack the is16BitUnicode byte | |||
// the remaining fields, which is naughty | |||
if (field_2_name_length != 0) { | |||
throw new RecordFormatException("Ran out of data reading style record"); | |||
} | |||
// guess this is OK if the string length is zero | |||
field_4_name = ""; | |||
} else { | |||
int is16BitUnicode = in.readByte(); | |||
if (is16BitUnicodeFlag.isSet(is16BitUnicode)) { | |||
field_4_name = StringUtil.readUnicodeLE(in, field_2_name_length); | |||
} else { | |||
field_4_name = StringUtil.readCompressedUnicode(in, field_2_name_length); | |||
} | |||
} | |||
} | |||
} | |||
/** | |||
* set the actual index of the style extended format record | |||
* @param xfIndex of the xf record | |||
*/ | |||
public void setXFIndex(int xfIndex) { | |||
field_1_xf_index = styleIndexMask.setValue(field_1_xf_index, xfIndex); | |||
} | |||
/** | |||
* get the actual index of the style extended format record | |||
* @see #getIndex() | |||
* @return index of the xf record | |||
*/ | |||
public int getXFIndex() { | |||
return styleIndexMask.getValue(field_1_xf_index); | |||
} | |||
/** | |||
* set the style's name | |||
* @param name of the style | |||
*/ | |||
public void setName(String name) { | |||
field_4_name = name; | |||
field_3_string_options = StringUtil.hasMultibyte(name) ? 0x01 : 0x00; | |||
field_1_xf_index = isBuiltinFlag.clear(field_1_xf_index); | |||
} | |||
/** | |||
* if this is a builtin style set the number of the built in style | |||
* @param builtinStyleId style number (0-7) | |||
* | |||
*/ | |||
public void setBuiltinStyle(int builtinStyleId) { | |||
field_1_xf_index = isBuiltinFlag.set(field_1_xf_index); | |||
field_2_builtin_style = builtinStyleId; | |||
} | |||
/** | |||
* set the row or column level of the style (if builtin 1||2) | |||
*/ | |||
public void setOutlineStyleLevel(int level) { | |||
field_3_outline_style_level = level & 0x00FF; | |||
} | |||
public boolean isBuiltin(){ | |||
return isBuiltinFlag.isSet(field_1_xf_index); | |||
} | |||
/** | |||
* get the style's name | |||
* @return name of the style | |||
*/ | |||
public String getName() { | |||
return field_4_name; | |||
} | |||
public String toString() { | |||
StringBuffer sb = new StringBuffer(); | |||
sb.append("[STYLE]\n"); | |||
sb.append(" .xf_index_raw =").append(HexDump.shortToHex(field_1_xf_index)).append("\n"); | |||
sb.append(" .type =").append(isBuiltin() ? "built-in" : "user-defined").append("\n"); | |||
sb.append(" .xf_index =").append(HexDump.shortToHex(getXFIndex())).append("\n"); | |||
if (isBuiltin()){ | |||
sb.append(" .builtin_style=").append(HexDump.byteToHex(field_2_builtin_style)).append("\n"); | |||
sb.append(" .outline_level=").append(HexDump.byteToHex(field_3_outline_style_level)).append("\n"); | |||
} else { | |||
sb.append(" .name =").append(getName()).append("\n"); | |||
} | |||
sb.append("[/STYLE]\n"); | |||
return sb.toString(); | |||
} | |||
private int getDataSize() { | |||
if (isBuiltin()) { | |||
return 4; // short, byte, byte | |||
} | |||
int size = 2 + 3; // short | |||
if (is16BitUnicodeFlag.isSet(field_3_string_options)) { | |||
size += 2 * field_4_name.length(); | |||
} else { | |||
size += field_4_name.length(); | |||
} | |||
return size; | |||
} | |||
public int serialize(int offset, byte [] data) { | |||
int dataSize = getDataSize(); | |||
LittleEndian.putShort(data, 0 + offset, sid); | |||
LittleEndian.putUShort(data, 2 + offset, dataSize); | |||
LittleEndian.putUShort(data, 4 + offset, field_1_xf_index); | |||
if (isBuiltin()) { | |||
LittleEndian.putByte(data, 6 + offset, field_2_builtin_style); | |||
LittleEndian.putByte(data, 7 + offset, field_3_outline_style_level); | |||
} else { | |||
LittleEndian.putUShort(data, 6 + offset, field_4_name.length()); | |||
LittleEndian.putByte(data, 8 + offset, field_3_string_options); | |||
StringUtil.putCompressedUnicode(getName(), data, 9 + offset); | |||
} | |||
return 4+dataSize; | |||
} | |||
public int getRecordSize() { | |||
return 4 + getDataSize(); | |||
} | |||
public short getSid() | |||
{ | |||
return sid; | |||
} | |||
} |
@@ -27,6 +27,7 @@ import org.apache.poi.hssf.record.formula.Ref3DPtg; | |||
import org.apache.poi.hssf.record.formula.RefPtg; | |||
import org.apache.poi.util.HexDump; | |||
import org.apache.poi.util.LittleEndian; | |||
import org.apache.poi.util.LittleEndianByteArrayInputStream; | |||
import org.apache.poi.util.LittleEndianInput; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
import org.apache.poi.util.LittleEndianOutputStream; | |||
@@ -209,17 +210,12 @@ public abstract class SubRecord { | |||
out.writeShort(_unknownShort13); | |||
} | |||
private static Ptg readRefPtg(byte[] formulaRawBytes) { | |||
byte[] data = new byte[formulaRawBytes.length + 4]; | |||
LittleEndian.putUShort(data, 0, -5555); | |||
LittleEndian.putUShort(data, 2, formulaRawBytes.length); | |||
System.arraycopy(formulaRawBytes, 0, data, 4, formulaRawBytes.length); | |||
RecordInputStream in = new RecordInputStream(new ByteArrayInputStream(data)); | |||
in.nextRecord(); | |||
byte ptgSid = in.readByte(); | |||
LittleEndianInput in = new LittleEndianByteArrayInputStream(formulaRawBytes); | |||
byte ptgSid = in.readByte(); | |||
switch(ptgSid) { | |||
case AreaPtg.sid: return new AreaPtg(in); | |||
case Area3DPtg.sid: return new Area3DPtg(in); | |||
case RefPtg.sid: return new RefPtg(in); | |||
case RefPtg.sid: return new RefPtg(in); | |||
case Ref3DPtg.sid: return new Ref3DPtg(in); | |||
} | |||
return null; |
@@ -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; | |||
@@ -25,6 +25,8 @@ import org.apache.poi.util.BitField; | |||
import org.apache.poi.util.BitFieldFactory; | |||
import org.apache.poi.util.HexDump; | |||
import org.apache.poi.util.LittleEndian; | |||
import org.apache.poi.util.LittleEndianByteArrayOutputStream; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
/** | |||
* The TXO record (0x01B6) is used to define the properties of a text box. It is | |||
@@ -129,13 +131,7 @@ public final class TextObjectRecord extends Record { | |||
_text = new HSSFRichTextString(text); | |||
if (field_7_formattingDataLength > 0) { | |||
if (in.isContinueNext() && in.remaining() == 0) { | |||
in.nextRecord(); | |||
processFontRuns(in, _text, field_7_formattingDataLength); | |||
} else { | |||
throw new RecordFormatException( | |||
"Expected Continue Record to hold font runs for TextObjectRecord"); | |||
} | |||
processFontRuns(in, _text, field_7_formattingDataLength); | |||
} | |||
} | |||
@@ -154,10 +150,6 @@ public final class TextObjectRecord extends Record { | |||
throw new RecordFormatException("Bad format run data length " + formattingRunDataLength | |||
+ ")"); | |||
} | |||
if (in.remaining() != formattingRunDataLength) { | |||
throw new RecordFormatException("Expected " + formattingRunDataLength | |||
+ " bytes but got " + in.remaining()); | |||
} | |||
int nRuns = formattingRunDataLength / FORMAT_RUN_ENCODED_SIZE; | |||
for (int i = 0; i < nRuns; i++) { | |||
short index = in.readShort(); | |||
@@ -190,36 +182,31 @@ public final class TextObjectRecord extends Record { | |||
private int serializeTXORecord(int offset, byte[] data) { | |||
int dataSize = getDataSize(); | |||
int recSize = dataSize+4; | |||
LittleEndianOutput out = new LittleEndianByteArrayOutputStream(data, offset, recSize); | |||
LittleEndian.putUShort(data, 0 + offset, TextObjectRecord.sid); | |||
LittleEndian.putUShort(data, 2 + offset, dataSize); | |||
out.writeShort(TextObjectRecord.sid); | |||
out.writeShort(dataSize); | |||
LittleEndian.putUShort(data, 4 + offset, field_1_options); | |||
LittleEndian.putUShort(data, 6 + offset, field_2_textOrientation); | |||
LittleEndian.putUShort(data, 8 + offset, field_3_reserved4); | |||
LittleEndian.putUShort(data, 10 + offset, field_4_reserved5); | |||
LittleEndian.putUShort(data, 12 + offset, field_5_reserved6); | |||
LittleEndian.putUShort(data, 14 + offset, _text.length()); | |||
LittleEndian.putUShort(data, 16 + offset, getFormattingDataLength()); | |||
LittleEndian.putInt(data, 18 + offset, field_8_reserved7); | |||
out.writeShort(field_1_options); | |||
out.writeShort(field_2_textOrientation); | |||
out.writeShort(field_3_reserved4); | |||
out.writeShort(field_4_reserved5); | |||
out.writeShort(field_5_reserved6); | |||
out.writeShort(_text.length()); | |||
out.writeShort(getFormattingDataLength()); | |||
out.writeInt(field_8_reserved7); | |||
if (_linkRefPtg != null) { | |||
int pos = offset+22; | |||
int formulaSize = _linkRefPtg.getSize(); | |||
LittleEndian.putUShort(data, pos, formulaSize); | |||
pos += LittleEndian.SHORT_SIZE; | |||
LittleEndian.putInt(data, pos, _unknownPreFormulaInt); | |||
pos += LittleEndian.INT_SIZE; | |||
_linkRefPtg.writeBytes(data, pos); | |||
pos += formulaSize; | |||
out.writeShort(formulaSize); | |||
out.writeInt(_unknownPreFormulaInt); | |||
_linkRefPtg.write(out); | |||
if (_unknownPostFormulaByte != null) { | |||
LittleEndian.putByte(data, pos, _unknownPostFormulaByte.byteValue()); | |||
pos += LittleEndian.BYTE_SIZE; | |||
out.writeByte(_unknownPostFormulaByte.byteValue()); | |||
} | |||
} | |||
return 4 + dataSize; | |||
return recSize; | |||
} | |||
private int serializeTrailingRecords(int offset, byte[] data) { |
@@ -36,13 +36,8 @@ import java.util.Collections; | |||
* @author Andrew C. Oliver | |||
* @author Marc Johnson (mjohnson at apache dot org) | |||
* @author Glen Stampoultzis (glens at apache.org) | |||
* @version 2.0-pre | |||
*/ | |||
public class UnicodeString | |||
implements Comparable | |||
{ | |||
public final static short sid = 0xFFF; | |||
public final class UnicodeString implements Comparable { | |||
private short field_1_charCount; // = 0; | |||
private byte field_2_optionflags; // = 0; | |||
private String field_3_string; // = null; | |||
@@ -53,8 +48,8 @@ public class UnicodeString | |||
private static final BitField richText = BitFieldFactory.getInstance(0x8); | |||
public static class FormatRun implements Comparable { | |||
private short character; | |||
private short fontIndex; | |||
short character; | |||
short fontIndex; | |||
public FormatRun(short character, short fontIndex) { | |||
this.character = character; | |||
@@ -102,15 +97,6 @@ public class UnicodeString | |||
setString(str); | |||
} | |||
/** | |||
* construct a unicode string record and fill its fields, ID is ignored | |||
* @param in the RecordInputstream to read the record from | |||
*/ | |||
public UnicodeString(RecordInputStream in) | |||
{ | |||
fillFields(in); // TODO - inline | |||
} | |||
public int hashCode() | |||
@@ -142,9 +128,9 @@ public class UnicodeString | |||
&& field_3_string.equals(other.field_3_string)); | |||
if (!eq) return false; | |||
//Ok string appears to be equal but now lets compare formatting runs | |||
//OK string appears to be equal but now lets compare formatting runs | |||
if ((field_4_format_runs == null) && (other.field_4_format_runs == null)) | |||
//Strings are equal, and there are not formtting runs. | |||
//Strings are equal, and there are not formatting runs. | |||
return true; | |||
if (((field_4_format_runs == null) && (other.field_4_format_runs != null)) || | |||
(field_4_format_runs != null) && (other.field_4_format_runs == null)) | |||
@@ -186,10 +172,10 @@ public class UnicodeString | |||
} | |||
/** | |||
* construct a unicode string record and fill its fields, ID is ignored | |||
* @param in the RecordInputstream to read the record from | |||
*/ | |||
protected void fillFields(RecordInputStream in) | |||
{ | |||
public UnicodeString(RecordInputStream in) { | |||
field_1_charCount = in.readShort(); | |||
field_2_optionflags = in.readByte(); | |||
@@ -206,35 +192,13 @@ public class UnicodeString | |||
extensionLength = in.readInt(); | |||
} | |||
//Now need to get the string data. | |||
//Turn off autocontinuation so that we can catch the continue boundary | |||
in.setAutoContinue(false); | |||
StringBuffer tmpString = new StringBuffer(field_1_charCount); | |||
int stringCharCount = field_1_charCount; | |||
boolean isCompressed = ((field_2_optionflags & 1) == 0); | |||
while (stringCharCount != 0) { | |||
if (in.remaining() == 0) { | |||
if (in.isContinueNext()) { | |||
in.nextRecord(); | |||
//Check if we are now reading, compressed or uncompressed unicode. | |||
byte optionflags = in.readByte(); | |||
isCompressed = ((optionflags & 1) == 0); | |||
} else | |||
throw new RecordFormatException("Expected continue record."); | |||
} | |||
if (isCompressed) { | |||
char ch = (char)in.readUByte(); // avoid sex | |||
tmpString.append(ch); | |||
} else { | |||
char ch = (char) in.readShort(); | |||
tmpString.append(ch); | |||
} | |||
stringCharCount --; | |||
if (isCompressed) { | |||
field_3_string = in.readCompressedUnicode(field_1_charCount); | |||
} else { | |||
field_3_string = in.readUnicodeLEString(field_1_charCount); | |||
} | |||
field_3_string = tmpString.toString(); | |||
//Turn back on autocontinuation | |||
in.setAutoContinue(true); | |||
if (isRichText() && (runCount > 0)) { | |||
field_4_format_runs = new ArrayList(runCount); | |||
@@ -305,13 +269,8 @@ public class UnicodeString | |||
} | |||
/** | |||
* get the actual string this contains as a java String object | |||
* | |||
* | |||
* @return String | |||
* | |||
* @return the actual string this contains as a java String object | |||
*/ | |||
public String getString() | |||
{ | |||
return field_3_string; | |||
@@ -341,7 +300,7 @@ public class UnicodeString | |||
} | |||
} | |||
if (useUTF16) | |||
//Set the uncomressed bit | |||
//Set the uncompressed bit | |||
field_2_optionflags = highByte.setByte(field_2_optionflags); | |||
else field_2_optionflags = highByte.clearByte(field_2_optionflags); | |||
} | |||
@@ -392,7 +351,7 @@ public class UnicodeString | |||
//Make sure that we now say that we are a rich string | |||
field_2_optionflags = richText.setByte(field_2_optionflags); | |||
} | |||
} | |||
public Iterator formatIterator() { | |||
if (field_4_format_runs != null) | |||
@@ -497,8 +456,8 @@ public class UnicodeString | |||
LittleEndian.putShort(data, offset, ContinueRecord.sid); | |||
offset+=2; | |||
//Record the location of the last continue legnth position, but dont write | |||
//anything there yet (since we dont know what it will be!) | |||
//Record the location of the last continue length position, but don't write | |||
//anything there yet (since we don't know what it will be!) | |||
stats.lastLengthPos = offset; | |||
offset += 2; | |||
@@ -506,7 +465,7 @@ public class UnicodeString | |||
stats.remainingSize = SSTRecord.MAX_RECORD_SIZE-4; | |||
} | |||
return offset; | |||
} | |||
} | |||
public int serialize(UnicodeRecordStats stats, final int offset, byte [] data) | |||
{ | |||
@@ -514,7 +473,6 @@ public class UnicodeString | |||
//Basic string overhead | |||
pos = writeContinueIfRequired(stats, 3, pos, data); | |||
// byte[] retval = new byte[ 3 + (getString().length() * charsize)]; | |||
LittleEndian.putShort(data, pos, getCharCount()); | |||
pos += 2; | |||
data[ pos ] = getOptionFlags(); | |||
@@ -568,39 +526,39 @@ public class UnicodeString | |||
//Check to see if the offset occurs mid string, if so then we need to add | |||
//the byte to start with that represents the first byte of the continue record. | |||
if (strSize > stats.remainingSize) { | |||
//Ok the offset occurs half way through the string, that means that | |||
//OK the offset occurs half way through the string, that means that | |||
//we need an extra byte after the continue record ie we didnt finish | |||
//writing out the string the 1st time through | |||
//But hang on, how many continue records did we span? What if this is | |||
//a REALLY long string. We need to work this all out. | |||
int ammountThatCantFit = strSize; | |||
int amountThatCantFit = strSize; | |||
int strPos = 0; | |||
while (ammountThatCantFit > 0) { | |||
int ammountWritten = Math.min(stats.remainingSize, ammountThatCantFit); | |||
//Make sure that the ammount that cant fit takes into account | |||
while (amountThatCantFit > 0) { | |||
int amountWritten = Math.min(stats.remainingSize, amountThatCantFit); | |||
//Make sure that the amount that can't fit takes into account | |||
//whether we are writing double byte unicode | |||
if (isUncompressedUnicode()) { | |||
//We have the '-1' here because whether this is the first record or | |||
//subsequent continue records, there is always the case that the | |||
//number of bytes in a string on doube byte boundaries is actually odd. | |||
if ( ( (ammountWritten ) % 2) == 1) | |||
ammountWritten--; | |||
//number of bytes in a string on double byte boundaries is actually odd. | |||
if ( ( (amountWritten ) % 2) == 1) | |||
amountWritten--; | |||
} | |||
System.arraycopy(strBytes, strPos, data, pos, ammountWritten); | |||
pos += ammountWritten; | |||
strPos += ammountWritten; | |||
stats.recordSize += ammountWritten; | |||
stats.remainingSize -= ammountWritten; | |||
System.arraycopy(strBytes, strPos, data, pos, amountWritten); | |||
pos += amountWritten; | |||
strPos += amountWritten; | |||
stats.recordSize += amountWritten; | |||
stats.remainingSize -= amountWritten; | |||
//Ok lets subtract what we can write | |||
ammountThatCantFit -= ammountWritten; | |||
amountThatCantFit -= amountWritten; | |||
//Each iteration of this while loop is another continue record, unless | |||
//everything now fits. | |||
if (ammountThatCantFit > 0) { | |||
if (amountThatCantFit > 0) { | |||
//We know that a continue WILL be requied, but use this common method | |||
pos = writeContinueIfRequired(stats, ammountThatCantFit, pos, data); | |||
pos = writeContinueIfRequired(stats, amountThatCantFit, pos, data); | |||
//The first byte after a continue mid string is the extra byte to | |||
//indicate if this run is compressed or not. | |||
@@ -686,7 +644,7 @@ public class UnicodeString | |||
return highByte.isSet(getOptionFlags()); | |||
} | |||
/** Returns the size of this record, given the ammount of record space | |||
/** Returns the size of this record, given the amount of record space | |||
* remaining, it will also include the size of writing a continue record. | |||
*/ | |||
@@ -833,13 +791,6 @@ public class UnicodeString | |||
} | |||
} | |||
public short getSid() | |||
{ | |||
return sid; | |||
} | |||
public int compareTo(Object obj) | |||
{ | |||
UnicodeString str = ( UnicodeString ) obj; | |||
@@ -877,7 +828,7 @@ public class UnicodeString | |||
} | |||
//Well the format runs are equal as well!, better check the ExtRst data | |||
//Which by the way we dont know how to decode! | |||
//Which by the way we don't know how to decode! | |||
if ((field_5_ext_rst == null) && (str.field_5_ext_rst == null)) | |||
return 0; | |||
if ((field_5_ext_rst == null) && (str.field_5_ext_rst != null)) |
@@ -1,4 +1,3 @@ | |||
/* ==================================================================== | |||
Licensed to the Apache Software Foundation (ASF) under one or more | |||
contributor license agreements. See the NOTICE file distributed with | |||
@@ -15,111 +14,130 @@ | |||
See the License for the specific language governing permissions and | |||
limitations under the License. | |||
==================================================================== */ | |||
package org.apache.poi.hssf.record; | |||
import java.util.Arrays; | |||
import org.apache.poi.util.LittleEndian; | |||
import org.apache.poi.util.StringUtil; | |||
/** | |||
* Title: Write Access Record<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; | |||
private String field_1_username; | |||
public WriteAccessRecord() | |||
{ | |||
} | |||
public WriteAccessRecord(RecordInputStream in) | |||
{ | |||
byte[] data = in.readRemainder(); | |||
//The string is always 112 characters (padded with spaces), therefore | |||
//this record can not be continued. | |||
//What a wierd record, it is not really a unicode string because the | |||
//header doesnt provide a correct size indication.??? | |||
//But the header is present, so we need to skip over it. | |||
//Odd, Odd, Odd ;-) | |||
field_1_username = StringUtil.getFromCompressedUnicode(data, 3, data.length - 3); | |||
} | |||
/** | |||
* set the username for the user that created the report. HSSF uses the logged in user. | |||
* @param username of the user who is logged in (probably "tomcat" or "apache") | |||
*/ | |||
public void setUsername(String username) | |||
{ | |||
field_1_username = username; | |||
} | |||
/** | |||
* get the username for the user that created the report. HSSF uses the logged in user. On | |||
* natively created M$ Excel sheet this would be the name you typed in when you installed it | |||
* in most cases. | |||
* @return username of the user who is logged in (probably "tomcat" or "apache") | |||
*/ | |||
public String getUsername() | |||
{ | |||
return field_1_username; | |||
} | |||
public String toString() | |||
{ | |||
StringBuffer buffer = new StringBuffer(); | |||
buffer.append("[WRITEACCESS]\n"); | |||
buffer.append(" .name = ") | |||
.append(field_1_username.toString()).append("\n"); | |||
buffer.append("[/WRITEACCESS]\n"); | |||
return buffer.toString(); | |||
} | |||
public int serialize(int offset, byte [] data) | |||
{ | |||
String username = getUsername(); | |||
StringBuffer temp = new StringBuffer(0x70 - (0x3)); | |||
temp.append(username); | |||
while (temp.length() < 0x70 - 0x3) | |||
{ | |||
temp.append( | |||
" "); // (70 = fixed lenght -3 = the overhead bits of unicode string) | |||
} | |||
username = temp.toString(); | |||
UnicodeString str = new UnicodeString(username); | |||
str.setOptionFlags(( byte ) 0x0); | |||
LittleEndian.putShort(data, 0 + offset, sid); | |||
LittleEndian.putShort(data, 2 + offset, (short)112); // 112 bytes (115 total) | |||
UnicodeString.UnicodeRecordStats stats = new UnicodeString.UnicodeRecordStats(); | |||
stats.recordSize += 4; | |||
stats.remainingSize-= 4; | |||
str.serialize(stats, 4 + offset, data); | |||
return getRecordSize(); | |||
} | |||
public int getRecordSize() | |||
{ | |||
return 116; | |||
} | |||
public short getSid() | |||
{ | |||
return sid; | |||
} | |||
public final class WriteAccessRecord extends Record { | |||
private static final byte PAD_CHAR = (byte) ' '; | |||
public final static short sid = 0x005C; | |||
private static final int DATA_SIZE = 112; | |||
private String field_1_username; | |||
/** this record is always padded to a constant length */ | |||
private byte[] padding; | |||
public WriteAccessRecord() { | |||
setUsername(""); | |||
padding = new byte[DATA_SIZE - 3]; | |||
} | |||
public WriteAccessRecord(RecordInputStream in) { | |||
if (in.remaining() > DATA_SIZE) { | |||
throw new RecordFormatException("Expected data size (" + DATA_SIZE + ") but got (" | |||
+ in.remaining() + ")"); | |||
} | |||
// The string is always 112 characters (padded with spaces), therefore | |||
// this record can not be continued. | |||
int nChars = in.readUShort(); | |||
int is16BitFlag = in.readUByte(); | |||
int expectedPadSize = DATA_SIZE - 3; | |||
if ((is16BitFlag & 0x01) == 0x00) { | |||
field_1_username = StringUtil.readCompressedUnicode(in, nChars); | |||
expectedPadSize -= nChars; | |||
} else { | |||
field_1_username = StringUtil.readUnicodeLE(in, nChars); | |||
expectedPadSize -= nChars * 2; | |||
} | |||
padding = new byte[expectedPadSize]; | |||
int padSize = in.remaining(); | |||
in.readFully(padding, 0, padSize); | |||
if (padSize < expectedPadSize) { | |||
// this occurs in a couple of test examples: "42564.xls", | |||
// "bug_42794.xls" | |||
Arrays.fill(padding, padSize, expectedPadSize, PAD_CHAR); | |||
} | |||
} | |||
/** | |||
* set the username for the user that created the report. HSSF uses the | |||
* logged in user. | |||
* | |||
* @param username of the user who is logged in (probably "tomcat" or "apache") | |||
*/ | |||
public void setUsername(String username) { | |||
boolean is16bit = StringUtil.hasMultibyte(username); | |||
int encodedByteCount = 3 + username.length() * (is16bit ? 2 : 1); | |||
int paddingSize = DATA_SIZE - encodedByteCount; | |||
if (paddingSize < 0) { | |||
throw new IllegalArgumentException("Name is too long: " + username); | |||
} | |||
padding = new byte[paddingSize]; | |||
Arrays.fill(padding, PAD_CHAR); | |||
field_1_username = username; | |||
} | |||
/** | |||
* get the username for the user that created the report. HSSF uses the | |||
* logged in user. On natively created M$ Excel sheet this would be the name | |||
* you typed in when you installed it in most cases. | |||
* | |||
* @return username of the user who is logged in (probably "tomcat" or "apache") | |||
*/ | |||
public String getUsername() { | |||
return field_1_username; | |||
} | |||
public String toString() { | |||
StringBuffer buffer = new StringBuffer(); | |||
buffer.append("[WRITEACCESS]\n"); | |||
buffer.append(" .name = ").append(field_1_username.toString()).append("\n"); | |||
buffer.append("[/WRITEACCESS]\n"); | |||
return buffer.toString(); | |||
} | |||
public int serialize(int offset, byte[] data) { | |||
String username = getUsername(); | |||
boolean is16bit = StringUtil.hasMultibyte(username); | |||
LittleEndian.putUShort(data, 0 + offset, sid); | |||
LittleEndian.putUShort(data, 2 + offset, DATA_SIZE); | |||
LittleEndian.putUShort(data, 4 + offset, username.length()); | |||
LittleEndian.putByte(data, 6 + offset, is16bit ? 0x01 : 0x00); | |||
int pos = offset + 7; | |||
if (is16bit) { | |||
StringUtil.putUnicodeLE(username, data, pos); | |||
pos += username.length() * 2; | |||
} else { | |||
StringUtil.putCompressedUnicode(username, data, pos); | |||
pos += username.length(); | |||
} | |||
System.arraycopy(padding, 0, data, pos, padding.length); | |||
return 4 + DATA_SIZE; | |||
} | |||
public int getRecordSize() { | |||
return 4 + DATA_SIZE; | |||
} | |||
public short getSid() { | |||
return sid; | |||
} | |||
} |
@@ -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() + "'"); |
@@ -98,7 +98,6 @@ public abstract class AbstractFunctionPtg extends OperationPtg { | |||
buf.append(")"); | |||
} | |||
public abstract void writeBytes(byte[] array, int offset); | |||
public abstract int getSize(); | |||
@@ -17,42 +17,48 @@ | |||
package org.apache.poi.hssf.record.formula; | |||
import org.apache.poi.hssf.record.RecordInputStream; | |||
import org.apache.poi.util.LittleEndian; | |||
import org.apache.poi.util.LittleEndianInput; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
/** | |||
* Common superclass of 2-D area refs | |||
* Common superclass of 2-D area refs | |||
*/ | |||
public abstract class Area2DPtgBase extends AreaPtgBase { | |||
private final static int SIZE = 9; | |||
private final static int SIZE = 9; | |||
protected Area2DPtgBase(int firstRow, int lastRow, int firstColumn, int lastColumn, boolean firstRowRelative, boolean lastRowRelative, boolean firstColRelative, boolean lastColRelative) { | |||
super(firstRow, lastRow, firstColumn, lastColumn, firstRowRelative, lastRowRelative, firstColRelative, lastColRelative); | |||
} | |||
protected Area2DPtgBase(RecordInputStream in) { | |||
protected Area2DPtgBase(LittleEndianInput in) { | |||
readCoordinates(in); | |||
} | |||
protected abstract byte getSid(); | |||
public final void writeBytes(byte [] array, int offset) { | |||
LittleEndian.putByte(array, offset+0, getSid() + getPtgClass()); | |||
writeCoordinates(array, offset+1); | |||
public final void write(LittleEndianOutput out) { | |||
out.writeByte(getSid() + getPtgClass()); | |||
writeCoordinates(out); | |||
} | |||
public Area2DPtgBase(String arearef) { | |||
super(arearef); | |||
super(arearef); | |||
} | |||
public final int getSize() { | |||
return SIZE; | |||
} | |||
public final String toFormulaString() { | |||
return formatReferenceAsString(); | |||
return formatReferenceAsString(); | |||
} | |||
public final String toString() { | |||
StringBuffer sb = new StringBuffer(); | |||
sb.append(getClass().getName()); | |||
sb.append(" ["); | |||
sb.append(formatReferenceAsString()); | |||
sb.append("]"); | |||
return sb.toString(); | |||
} | |||
public final String toString() { | |||
StringBuffer sb = new StringBuffer(); | |||
sb.append(getClass().getName()); | |||
sb.append(" ["); | |||
sb.append(formatReferenceAsString()); | |||
sb.append("]"); | |||
return sb.toString(); | |||
} | |||
} |
@@ -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() { |
@@ -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() { |
@@ -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); | |||
} | |||
@@ -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) { |
@@ -17,12 +17,12 @@ | |||
package org.apache.poi.hssf.record.formula; | |||
import org.apache.poi.hssf.record.RecordInputStream; | |||
import org.apache.poi.ss.util.AreaReference; | |||
import org.apache.poi.ss.util.CellReference; | |||
import org.apache.poi.util.BitField; | |||
import org.apache.poi.util.BitFieldFactory; | |||
import org.apache.poi.util.LittleEndian; | |||
import org.apache.poi.util.LittleEndianInput; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
/** | |||
* Specifies a rectangular area of cells A1:A4 for instance. | |||
@@ -46,7 +46,7 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { | |||
private int field_3_first_column; | |||
/** zero based, unsigned 8 bit */ | |||
private int field_4_last_column; | |||
private final static BitField rowRelative = BitFieldFactory.getInstance(0x8000); | |||
private final static BitField colRelative = BitFieldFactory.getInstance(0x4000); | |||
private final static BitField columnMask = BitFieldFactory.getInstance(0x3FFF); | |||
@@ -54,7 +54,7 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { | |||
protected AreaPtgBase() { | |||
// do nothing | |||
} | |||
protected AreaPtgBase(String arearef) { | |||
AreaReference ar = new AreaReference(arearef); | |||
CellReference firstCell = ar.getFirstCell(); | |||
@@ -66,17 +66,17 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { | |||
setFirstColRelative(!firstCell.isColAbsolute()); | |||
setLastColRelative(!lastCell.isColAbsolute()); | |||
setFirstRowRelative(!firstCell.isRowAbsolute()); | |||
setLastRowRelative(!lastCell.isRowAbsolute()); | |||
setLastRowRelative(!lastCell.isRowAbsolute()); | |||
} | |||
protected AreaPtgBase(int firstRow, int lastRow, int firstColumn, int lastColumn, | |||
boolean firstRowRelative, boolean lastRowRelative, boolean firstColRelative, boolean lastColRelative) { | |||
checkColumnBounds(firstColumn); | |||
checkColumnBounds(lastColumn); | |||
checkRowBounds(firstRow); | |||
checkRowBounds(lastRow); | |||
if (lastRow > firstRow) { | |||
setFirstRow(firstRow); | |||
setLastRow(lastRow); | |||
@@ -88,7 +88,7 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { | |||
setFirstRowRelative(lastRowRelative); | |||
setLastRowRelative(firstRowRelative); | |||
} | |||
if (lastColumn > firstColumn) { | |||
setFirstColumn(firstColumn); | |||
setLastColumn(lastColumn); | |||
@@ -100,7 +100,7 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { | |||
setFirstColRelative(lastColRelative); | |||
setLastColRelative(firstColRelative); | |||
} | |||
} | |||
} | |||
private static void checkColumnBounds(int colIx) { | |||
if((colIx & 0x0FF) != colIx) { | |||
@@ -113,17 +113,17 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { | |||
} | |||
} | |||
protected final void readCoordinates(RecordInputStream in) { | |||
protected final void readCoordinates(LittleEndianInput in) { | |||
field_1_first_row = in.readUShort(); | |||
field_2_last_row = in.readUShort(); | |||
field_3_first_column = in.readUShort(); | |||
field_4_last_column = in.readUShort(); | |||
} | |||
protected final void writeCoordinates(byte[] array, int offset) { | |||
LittleEndian.putUShort(array, offset + 0, field_1_first_row); | |||
LittleEndian.putUShort(array, offset + 2, field_2_last_row); | |||
LittleEndian.putUShort(array, offset + 4, field_3_first_column); | |||
LittleEndian.putUShort(array, offset + 6, field_4_last_column); | |||
protected final void writeCoordinates(LittleEndianOutput out) { | |||
out.writeShort(field_1_first_row); | |||
out.writeShort(field_2_last_row); | |||
out.writeShort(field_3_first_column); | |||
out.writeShort(field_4_last_column); | |||
} | |||
/** | |||
@@ -150,7 +150,7 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { | |||
} | |||
/** | |||
* @param rowIx last row number in the area | |||
* @param rowIx last row number in the area | |||
*/ | |||
public final void setLastRow(int rowIx) { | |||
checkRowBounds(rowIx); | |||
@@ -177,7 +177,7 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { | |||
public final boolean isFirstRowRelative() { | |||
return rowRelative.isSet(field_3_first_column); | |||
} | |||
/** | |||
* sets the first row to relative or not | |||
* @param rel is relative or not. | |||
@@ -192,9 +192,9 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { | |||
public final boolean isFirstColRelative() { | |||
return colRelative.isSet(field_3_first_column); | |||
} | |||
/** | |||
* set whether the first column is relative | |||
* set whether the first column is relative | |||
*/ | |||
public final void setFirstColRelative(boolean rel) { | |||
field_3_first_column=colRelative.setBoolean(field_3_first_column,rel); | |||
@@ -235,7 +235,7 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { | |||
public final boolean isLastRowRelative() { | |||
return rowRelative.isSet(field_4_last_column); | |||
} | |||
/** | |||
* set whether the last row is relative or not | |||
* @param rel <code>true</code> if the last row relative, else | |||
@@ -251,14 +251,14 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { | |||
public final boolean isLastColRelative() { | |||
return colRelative.isSet(field_4_last_column); | |||
} | |||
/** | |||
* set whether the last column should be relative or not | |||
*/ | |||
public final void setLastColRelative(boolean rel) { | |||
field_4_last_column=colRelative.setBoolean(field_4_last_column,rel); | |||
} | |||
/** | |||
* set the last column in the area | |||
*/ | |||
@@ -276,13 +276,13 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI { | |||
protected final String formatReferenceAsString() { | |||
CellReference topLeft = new CellReference(getFirstRow(),getFirstColumn(),!isFirstRowRelative(),!isFirstColRelative()); | |||
CellReference botRight = new CellReference(getLastRow(),getLastColumn(),!isLastRowRelative(),!isLastColRelative()); | |||
if(AreaReference.isWholeColumnReference(topLeft, botRight)) { | |||
return (new AreaReference(topLeft, botRight)).formatAsString(); | |||
} | |||
return topLeft.formatAsString() + ":" + botRight.formatAsString(); | |||
return topLeft.formatAsString() + ":" + botRight.formatAsString(); | |||
} | |||
public String toFormulaString() { | |||
return formatReferenceAsString(); | |||
} |
@@ -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) { | |||
@@ -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); | |||
} | |||
} | |||
@@ -17,42 +17,43 @@ | |||
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; | |||
private final boolean _value; | |||
public BoolPtg(RecordInputStream in) { | |||
_value = (in.readByte() == 1); | |||
} | |||
public BoolPtg(String formulaToken) { | |||
_value = (formulaToken.equalsIgnoreCase("TRUE")); | |||
} | |||
public boolean getValue() { | |||
return _value; | |||
} | |||
public void writeBytes(byte [] array, int offset) { | |||
array[ offset + 0 ] = sid; | |||
array[ offset + 1 ] = (byte) (_value ? 1 : 0); | |||
} | |||
public int getSize() { | |||
return SIZE; | |||
} | |||
public String toFormulaString() { | |||
return _value ? "TRUE" : "FALSE"; | |||
} | |||
public final static int SIZE = 2; | |||
public final static byte sid = 0x1D; | |||
private final boolean _value; | |||
public BoolPtg(LittleEndianInput in) { | |||
_value = (in.readByte() == 1); | |||
} | |||
public BoolPtg(String formulaToken) { | |||
_value = (formulaToken.equalsIgnoreCase("TRUE")); | |||
} | |||
public boolean getValue() { | |||
return _value; | |||
} | |||
public void write(LittleEndianOutput out) { | |||
out.writeByte(sid + getPtgClass()); | |||
out.writeByte(_value ? 1 : 0); | |||
} | |||
public int getSize() { | |||
return SIZE; | |||
} | |||
public String toFormulaString() { | |||
return _value ? "TRUE" : "FALSE"; | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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() { |
@@ -18,8 +18,8 @@ | |||
package org.apache.poi.hssf.record.formula; | |||
import org.apache.poi.hssf.record.RecordFormatException; | |||
import org.apache.poi.hssf.record.RecordInputStream; | |||
import org.apache.poi.util.LittleEndian; | |||
import org.apache.poi.util.LittleEndianInput; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
/** | |||
* | |||
@@ -33,42 +33,41 @@ public final class ExpPtg extends ControlPtg { | |||
private final short field_1_first_row; | |||
private final short field_2_first_col; | |||
public ExpPtg(RecordInputStream in) | |||
public ExpPtg(LittleEndianInput in) | |||
{ | |||
field_1_first_row = in.readShort(); | |||
field_2_first_col = in.readShort(); | |||
} | |||
public void writeBytes(byte [] array, int offset) | |||
{ | |||
array[offset+0]= (byte) (sid); | |||
LittleEndian.putShort(array,offset+1,field_1_first_row); | |||
LittleEndian.putShort(array,offset+3,field_2_first_col); | |||
public void write(LittleEndianOutput out) { | |||
out.writeByte(sid + getPtgClass()); | |||
out.writeShort(field_1_first_row); | |||
out.writeShort(field_2_first_col); | |||
} | |||
public int getSize() | |||
{ | |||
return SIZE; | |||
} | |||
public short getRow() { | |||
return field_1_first_row; | |||
} | |||
public short getColumn() { | |||
return field_2_first_col; | |||
} | |||
} | |||
public String toFormulaString() | |||
{ | |||
throw new RecordFormatException("Coding Error: Expected ExpPtg to be converted from Shared to Non-Shared Formula by ValueRecordsAggregate, but it wasn't"); | |||
} | |||
public String toString() | |||
{ | |||
StringBuffer buffer = new StringBuffer("[Array Formula or Shared Formula]\n"); | |||
buffer.append("row = ").append(getRow()).append("\n"); | |||
buffer.append("col = ").append(getColumn()).append("\n"); | |||
return buffer.toString(); | |||
} | |||
} | |||
} |
@@ -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() { |
@@ -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() { |
@@ -17,58 +17,60 @@ | |||
package org.apache.poi.hssf.record.formula; | |||
import org.apache.poi.hssf.record.RecordInputStream; | |||
import org.apache.poi.util.LittleEndian; | |||
import org.apache.poi.util.LittleEndianInput; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
/** | |||
* Integer (unsigned short integer) | |||
* Stores an unsigned short value (java int) in a formula | |||
* @author Andrew C. Oliver (acoliver at apache dot org) | |||
* Integer (unsigned short integer) Stores an unsigned short value (java int) in | |||
* a formula | |||
* | |||
* @author Andrew C. Oliver (acoliver at apache dot org) | |||
* @author Jason Height (jheight at chariot dot net dot au) | |||
*/ | |||
public final class IntPtg extends ScalarConstantPtg { | |||
// 16 bit unsigned integer | |||
private static final int MIN_VALUE = 0x0000; | |||
private static final int MAX_VALUE = 0xFFFF; | |||
/** | |||
* Excel represents integers 0..65535 with the tInt token. | |||
* @return <code>true</code> if the specified value is within the range of values | |||
* <tt>IntPtg</tt> can represent. | |||
*/ | |||
public static boolean isInRange(int i) { | |||
return i>=MIN_VALUE && i <=MAX_VALUE; | |||
} | |||
// 16 bit unsigned integer | |||
private static final int MIN_VALUE = 0x0000; | |||
private static final int MAX_VALUE = 0xFFFF; | |||
public final static int SIZE = 3; | |||
public final static byte sid = 0x1e; | |||
private final int field_1_value; | |||
public IntPtg(RecordInputStream in) { | |||
this(in.readUShort()); | |||
} | |||
/** | |||
* Excel represents integers 0..65535 with the tInt token. | |||
* | |||
* @return <code>true</code> if the specified value is within the range of values | |||
* <tt>IntPtg</tt> can represent. | |||
*/ | |||
public static boolean isInRange(int i) { | |||
return i >= MIN_VALUE && i <= MAX_VALUE; | |||
} | |||
public IntPtg(int value) { | |||
if(!isInRange(value)) { | |||
throw new IllegalArgumentException("value is out of range: " + value); | |||
} | |||
field_1_value = value; | |||
} | |||
public final static int SIZE = 3; | |||
public final static byte sid = 0x1e; | |||
private final int field_1_value; | |||
public int getValue() { | |||
return field_1_value; | |||
} | |||
public IntPtg(LittleEndianInput in) { | |||
this(in.readUShort()); | |||
} | |||
public void writeBytes(byte [] array, int offset) { | |||
array[ offset + 0 ] = sid; | |||
LittleEndian.putUShort(array, offset + 1, getValue()); | |||
} | |||
public IntPtg(int value) { | |||
if (!isInRange(value)) { | |||
throw new IllegalArgumentException("value is out of range: " + value); | |||
} | |||
field_1_value = value; | |||
} | |||
public int getSize() { | |||
return SIZE; | |||
} | |||
public int getValue() { | |||
return field_1_value; | |||
} | |||
public String toFormulaString() { | |||
return String.valueOf(getValue()); | |||
} | |||
public void write(LittleEndianOutput out) { | |||
out.writeByte(sid + getPtgClass()); | |||
out.writeShort(getValue()); | |||
} | |||
public int getSize() { | |||
return SIZE; | |||
} | |||
public String toFormulaString() { | |||
return String.valueOf(getValue()); | |||
} | |||
} |
@@ -17,54 +17,46 @@ | |||
package org.apache.poi.hssf.record.formula; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
/** | |||
* @author Daniel Noll (daniel at nuix dot com dot au) | |||
*/ | |||
public final class IntersectionPtg extends OperationPtg { | |||
public final static byte sid = 0x0f; | |||
public final static byte sid = 0x0f; | |||
public static final OperationPtg instance = new IntersectionPtg(); | |||
public static final OperationPtg instance = new IntersectionPtg(); | |||
private IntersectionPtg() { | |||
// enforce singleton | |||
} | |||
private IntersectionPtg() { | |||
// enforce singleton | |||
} | |||
public final boolean isBaseToken() { | |||
return true; | |||
} | |||
public final boolean isBaseToken() { | |||
return true; | |||
} | |||
public int getSize() | |||
{ | |||
return 1; | |||
} | |||
public int getSize() { | |||
return 1; | |||
} | |||
public void writeBytes( byte[] array, int offset ) | |||
{ | |||
array[ offset + 0 ] = sid; | |||
} | |||
public void write(LittleEndianOutput out) { | |||
out.writeByte(sid + getPtgClass()); | |||
} | |||
/** Implementation of method from Ptg */ | |||
public String toFormulaString() | |||
{ | |||
return " "; | |||
} | |||
public String toFormulaString() { | |||
return " "; | |||
} | |||
public String toFormulaString(String[] operands) { | |||
StringBuffer buffer = new StringBuffer(); | |||
/** implementation of method from OperationsPtg*/ | |||
public String toFormulaString(String[] operands) | |||
{ | |||
StringBuffer buffer = new StringBuffer(); | |||
buffer.append(operands[ 0 ]); | |||
buffer.append(" "); | |||
buffer.append(operands[ 1 ]); | |||
return buffer.toString(); | |||
} | |||
public int getNumberOfOperands() | |||
{ | |||
return 2; | |||
} | |||
buffer.append(operands[0]); | |||
buffer.append(" "); | |||
buffer.append(operands[1]); | |||
return buffer.toString(); | |||
} | |||
public int getNumberOfOperands() { | |||
return 2; | |||
} | |||
} |
@@ -17,68 +17,45 @@ | |||
package org.apache.poi.hssf.record.formula; | |||
import org.apache.poi.hssf.record.RecordInputStream; | |||
import org.apache.poi.util.LittleEndian; | |||
import org.apache.poi.util.LittleEndianInput; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
/** | |||
* @author Daniel Noll (daniel at nuix dot com dot au) | |||
*/ | |||
public class MemAreaPtg extends OperandPtg { | |||
public final static short sid = 0x26; | |||
private final static int SIZE = 7; | |||
private int field_1_reserved; | |||
private short field_2_subex_len; | |||
/** Creates new MemAreaPtg */ | |||
public MemAreaPtg() | |||
{ | |||
} | |||
public MemAreaPtg(RecordInputStream in) | |||
{ | |||
field_1_reserved = in.readInt(); | |||
field_2_subex_len = in.readShort(); | |||
} | |||
public void setReserved(int res) | |||
{ | |||
field_1_reserved = res; | |||
} | |||
public int getReserved() | |||
{ | |||
return field_1_reserved; | |||
} | |||
public void setSubexpressionLength(short subexlen) | |||
{ | |||
field_2_subex_len = subexlen; | |||
} | |||
public short getSubexpressionLength() | |||
{ | |||
return field_2_subex_len; | |||
} | |||
public void writeBytes(byte [] array, int offset) | |||
{ | |||
array[offset] = (byte) (sid + getPtgClass()); | |||
LittleEndian.putInt(array, offset + 1, field_1_reserved); | |||
LittleEndian.putShort(array, offset + 5, field_2_subex_len); | |||
} | |||
public int getSize() | |||
{ | |||
return SIZE; | |||
} | |||
public String toFormulaString() | |||
{ | |||
return ""; // TODO: Not sure how to format this. -- DN | |||
} | |||
public byte getDefaultOperandClass() { | |||
return Ptg.CLASS_VALUE; | |||
} | |||
public final static short sid = 0x26; | |||
private final static int SIZE = 7; | |||
private final int field_1_reserved; | |||
private final int field_2_subex_len; | |||
/** Creates new MemAreaPtg */ | |||
public MemAreaPtg(int subexLen) { | |||
field_1_reserved = 0; | |||
field_2_subex_len = subexLen; | |||
} | |||
public MemAreaPtg(LittleEndianInput in) { | |||
field_1_reserved = in.readInt(); | |||
field_2_subex_len = in.readShort(); | |||
} | |||
public void write(LittleEndianOutput out) { | |||
out.writeByte(sid + getPtgClass()); | |||
out.writeInt(field_1_reserved); | |||
out.writeShort(field_2_subex_len); | |||
} | |||
public int getSize() { | |||
return SIZE; | |||
} | |||
public String toFormulaString() { | |||
return ""; // TODO: Not sure how to format this. -- DN | |||
} | |||
public byte getDefaultOperandClass() { | |||
return Ptg.CLASS_VALUE; | |||
} | |||
} |
@@ -17,36 +17,41 @@ | |||
package org.apache.poi.hssf.record.formula; | |||
import org.apache.poi.hssf.record.RecordInputStream; | |||
import org.apache.poi.hssf.usermodel.HSSFWorkbook; | |||
import org.apache.poi.util.LittleEndianInput; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
/** | |||
* | |||
* @author andy | |||
* | |||
* @author andy | |||
* @author Jason Height (jheight at chariot dot net dot au) | |||
* @author Daniel Noll (daniel at nuix dot com dot au) | |||
*/ | |||
public final class MemErrPtg extends MemAreaPtg { | |||
public final static short sid = 0x27; | |||
/** Creates new MemErrPtg */ | |||
public MemErrPtg() | |||
{ | |||
} | |||
public MemErrPtg(RecordInputStream in) { | |||
super(in); | |||
} | |||
public void writeBytes(byte [] array, int offset) { | |||
super.writeBytes(array, offset); | |||
array[offset] = (byte) (sid + getPtgClass()); | |||
} | |||
public String toFormulaString() | |||
{ | |||
return "ERR#"; | |||
} | |||
public final class MemErrPtg extends OperandPtg { | |||
public final static short sid = 0x27; | |||
private final static int SIZE = 7; | |||
private int field_1_reserved; | |||
private short field_2_subex_len; | |||
public MemErrPtg(LittleEndianInput in) { | |||
field_1_reserved = in.readInt(); | |||
field_2_subex_len = in.readShort(); | |||
} | |||
public void write(LittleEndianOutput out) { | |||
out.writeByte(sid + getPtgClass()); | |||
out.writeInt(field_1_reserved); | |||
out.writeShort(field_2_subex_len); | |||
} | |||
public int getSize() { | |||
return SIZE; | |||
} | |||
public String toFormulaString() { | |||
return "ERR#"; | |||
} | |||
public byte getDefaultOperandClass() { | |||
return Ptg.CLASS_VALUE; | |||
} | |||
} |
@@ -17,56 +17,51 @@ | |||
package org.apache.poi.hssf.record.formula; | |||
import org.apache.poi.hssf.record.RecordInputStream; | |||
import org.apache.poi.util.LittleEndian; | |||
import org.apache.poi.util.LittleEndianInput; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
/** | |||
* @author Glen Stampoultzis (glens at apache.org) | |||
*/ | |||
public final class MemFuncPtg extends OperandPtg { | |||
public final static byte sid = 0x29; | |||
private final int field_1_len_ref_subexpression; | |||
public final static byte sid = 0x29; | |||
private final int field_1_len_ref_subexpression; | |||
/**Creates new function pointer from a byte array | |||
* usually called while reading an excel file. | |||
*/ | |||
public MemFuncPtg(RecordInputStream in) { | |||
this(in.readUShort()); | |||
} | |||
/** | |||
* Creates new function pointer from a byte array usually called while | |||
* reading an excel file. | |||
*/ | |||
public MemFuncPtg(LittleEndianInput in) { | |||
this(in.readUShort()); | |||
} | |||
public MemFuncPtg(int subExprLen) { | |||
field_1_len_ref_subexpression = subExprLen; | |||
public MemFuncPtg(int subExprLen) { | |||
field_1_len_ref_subexpression = subExprLen; | |||
} | |||
public int getSize() | |||
{ | |||
return 3; | |||
} | |||
public int getSize() { | |||
return 3; | |||
} | |||
public void writeBytes( byte[] array, int offset ) | |||
{ | |||
array[offset + 0] = sid ; | |||
LittleEndian.putUShort( array, offset + 1, field_1_len_ref_subexpression ); | |||
} | |||
public void write(LittleEndianOutput out) { | |||
out.writeByte(sid + getPtgClass()); | |||
out.writeShort(field_1_len_ref_subexpression); | |||
} | |||
public String toFormulaString() | |||
{ | |||
return ""; | |||
} | |||
public String toFormulaString() { | |||
return ""; | |||
} | |||
public byte getDefaultOperandClass() | |||
{ | |||
return Ptg.CLASS_REF; | |||
} | |||
public byte getDefaultOperandClass() { | |||
return Ptg.CLASS_REF; | |||
} | |||
public int getNumberOfOperands() | |||
{ | |||
return field_1_len_ref_subexpression; | |||
} | |||
public int getNumberOfOperands() { | |||
return field_1_len_ref_subexpression; | |||
} | |||
public int getLenRefSubexpression() | |||
{ | |||
return field_1_len_ref_subexpression; | |||
} | |||
public int getLenRefSubexpression() { | |||
return field_1_len_ref_subexpression; | |||
} | |||
} |
@@ -17,32 +17,35 @@ | |||
package org.apache.poi.hssf.record.formula; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
/** | |||
* Missing Function Arguments | |||
* | |||
* | |||
* Avik Sengupta <avik at apache.org> | |||
* | |||
* @author Jason Height (jheight at chariot dot net dot au) | |||
*/ | |||
public final class MissingArgPtg extends ScalarConstantPtg { | |||
private final static int SIZE = 1; | |||
public final static byte sid = 0x16; | |||
public static final Ptg instance = new MissingArgPtg(); | |||
private MissingArgPtg() { | |||
// enforce singleton | |||
} | |||
public void writeBytes(byte [] array, int offset) { | |||
array[ offset + 0 ] = sid; | |||
} | |||
public int getSize() { | |||
return SIZE; | |||
} | |||
public String toFormulaString() { | |||
return " "; | |||
} | |||
private final static int SIZE = 1; | |||
public final static byte sid = 0x16; | |||
public static final Ptg instance = new MissingArgPtg(); | |||
private MissingArgPtg() { | |||
// enforce singleton | |||
} | |||
public void write(LittleEndianOutput out) { | |||
out.writeByte(sid + getPtgClass()); | |||
} | |||
public int getSize() { | |||
return SIZE; | |||
} | |||
public String toFormulaString() { | |||
return " "; | |||
} | |||
} |
@@ -17,63 +17,63 @@ | |||
package org.apache.poi.hssf.record.formula; | |||
import org.apache.poi.hssf.record.RecordInputStream; | |||
import org.apache.poi.ss.formula.WorkbookDependentFormula; | |||
import org.apache.poi.ss.formula.FormulaRenderingWorkbook; | |||
import org.apache.poi.util.LittleEndian; | |||
import org.apache.poi.ss.formula.WorkbookDependentFormula; | |||
import org.apache.poi.util.LittleEndianInput; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
/** | |||
* | |||
* @author andy | |||
* | |||
* @author andy | |||
* @author Jason Height (jheight at chariot dot net dot au) | |||
*/ | |||
public final class NamePtg extends OperandPtg implements WorkbookDependentFormula { | |||
public final static short sid = 0x23; | |||
private final static int SIZE = 5; | |||
/** one-based index to defined name record */ | |||
private int field_1_label_index; | |||
private short field_2_zero; // reserved must be 0 | |||
public final static short sid = 0x23; | |||
private final static int SIZE = 5; | |||
/** one-based index to defined name record */ | |||
private int field_1_label_index; | |||
private short field_2_zero; // reserved must be 0 | |||
/** | |||
* @param nameIndex zero-based index to name within workbook | |||
*/ | |||
public NamePtg(int nameIndex) { | |||
field_1_label_index = 1+nameIndex; // convert to 1-based | |||
} | |||
/** | |||
* @param nameIndex zero-based index to name within workbook | |||
*/ | |||
public NamePtg(int nameIndex) { | |||
field_1_label_index = 1 + nameIndex; // convert to 1-based | |||
} | |||
/** Creates new NamePtg */ | |||
/** Creates new NamePtg */ | |||
public NamePtg(RecordInputStream in) { | |||
field_1_label_index = in.readShort(); | |||
field_2_zero = in.readShort(); | |||
} | |||
/** | |||
* @return zero based index to a defined name record in the LinkTable | |||
*/ | |||
public int getIndex() { | |||
return field_1_label_index-1; // convert to zero based | |||
} | |||
public NamePtg(LittleEndianInput in) { | |||
field_1_label_index = in.readShort(); | |||
field_2_zero = in.readShort(); | |||
} | |||
public void writeBytes(byte [] array, int offset) { | |||
LittleEndian.putByte(array, offset + 0, sid + getPtgClass()); | |||
LittleEndian.putUShort(array, offset + 1, field_1_label_index); | |||
LittleEndian.putUShort(array, offset + 3, field_2_zero); | |||
} | |||
/** | |||
* @return zero based index to a defined name record in the LinkTable | |||
*/ | |||
public int getIndex() { | |||
return field_1_label_index - 1; // convert to zero based | |||
} | |||
public int getSize() { | |||
return SIZE; | |||
} | |||
public void write(LittleEndianOutput out) { | |||
out.writeByte(sid + getPtgClass()); | |||
out.writeShort(field_1_label_index); | |||
out.writeShort(field_2_zero); | |||
} | |||
public int getSize() { | |||
return SIZE; | |||
} | |||
public String toFormulaString(FormulaRenderingWorkbook book) { | |||
return book.getNameText(this); | |||
} | |||
public String toFormulaString(FormulaRenderingWorkbook book) | |||
{ | |||
return book.getNameText(this); | |||
} | |||
public String toFormulaString() { | |||
throw new RuntimeException("3D references need a workbook to determine formula text"); | |||
} | |||
public byte getDefaultOperandClass() { | |||
public byte getDefaultOperandClass() { | |||
return Ptg.CLASS_REF; | |||
} | |||
} |
@@ -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() { |
@@ -17,55 +17,55 @@ | |||
package org.apache.poi.hssf.record.formula; | |||
import org.apache.poi.hssf.record.RecordInputStream; | |||
import org.apache.poi.util.LittleEndian; | |||
import org.apache.poi.util.LittleEndianInput; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
/** | |||
* Number | |||
* Stores a floating point value in a formula | |||
* value stored in a 8 byte field using IEEE notation | |||
* @author Avik Sengupta | |||
* Number Stores a floating point value in a formula value stored in a 8 byte | |||
* field using IEEE notation | |||
* | |||
* @author Avik Sengupta | |||
* @author Jason Height (jheight at chariot dot net dot au) | |||
*/ | |||
public final class NumberPtg extends ScalarConstantPtg { | |||
public final static int SIZE = 9; | |||
public final static byte sid = 0x1f; | |||
private final double field_1_value; | |||
/** Create a NumberPtg from a byte array read from disk */ | |||
public NumberPtg(RecordInputStream in) { | |||
this(in.readDouble()); | |||
} | |||
/** Create a NumberPtg from a string representation of the number | |||
* Number format is not checked, it is expected to be validated in the parser | |||
* that calls this method. | |||
* @param value : String representation of a floating point number | |||
*/ | |||
public NumberPtg(String value) { | |||
this(Double.parseDouble(value)); | |||
} | |||
public NumberPtg(double value) { | |||
field_1_value = value; | |||
} | |||
public double getValue() { | |||
return field_1_value; | |||
} | |||
public final static int SIZE = 9; | |||
public final static byte sid = 0x1f; | |||
private final double field_1_value; | |||
public void writeBytes(byte [] array, int offset) { | |||
array[ offset + 0 ] = sid; | |||
LittleEndian.putDouble(array, offset + 1, getValue()); | |||
} | |||
public NumberPtg(LittleEndianInput in) { | |||
this(in.readDouble()); | |||
} | |||
public int getSize() { | |||
return SIZE; | |||
} | |||
/** | |||
* Create a NumberPtg from a string representation of the number Number | |||
* format is not checked, it is expected to be validated in the parser that | |||
* calls this method. | |||
* | |||
* @param value String representation of a floating point number | |||
*/ | |||
public NumberPtg(String value) { | |||
this(Double.parseDouble(value)); | |||
} | |||
public String toFormulaString() { | |||
// TODO - java's rendering of double values is not quite same as excel's | |||
// Maybe use HSSFDataFormatter? | |||
return String.valueOf(field_1_value); | |||
} | |||
public NumberPtg(double value) { | |||
field_1_value = value; | |||
} | |||
public double getValue() { | |||
return field_1_value; | |||
} | |||
public void write(LittleEndianOutput out) { | |||
out.writeByte(sid + getPtgClass()); | |||
out.writeDouble(getValue()); | |||
} | |||
public int getSize() { | |||
return SIZE; | |||
} | |||
public String toFormulaString() { | |||
// TODO - java's rendering of double values is not quite same as excel's | |||
return String.valueOf(field_1_value); | |||
} | |||
} |
@@ -15,46 +15,44 @@ | |||
limitations under the License. | |||
==================================================================== */ | |||
package org.apache.poi.hssf.record.formula; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
/** | |||
* While formula tokens are stored in RPN order and thus do not need parenthesis for | |||
* precedence reasons, Parenthesis tokens ARE written to ensure that user entered | |||
* parenthesis are displayed as-is on reading back | |||
* | |||
* Avik Sengupta <lists@aviksengupta.com> | |||
* Andrew C. Oliver (acoliver at apache dot org) | |||
* While formula tokens are stored in RPN order and thus do not need parenthesis | |||
* for precedence reasons, Parenthesis tokens ARE written to ensure that user | |||
* entered parenthesis are displayed as-is on reading back | |||
* | |||
* Avik Sengupta <lists@aviksengupta.com> Andrew C. Oliver (acoliver at | |||
* apache dot org) | |||
* | |||
* @author Jason Height (jheight at chariot dot net dot au) | |||
*/ | |||
public final class ParenthesisPtg extends ControlPtg { | |||
private final static int SIZE = 1; | |||
public final static byte sid = 0x15; | |||
public static final ControlPtg instance = new ParenthesisPtg(); | |||
private ParenthesisPtg() { | |||
// enforce singleton | |||
} | |||
public void writeBytes(byte [] array, int offset) | |||
{ | |||
array[ offset + 0 ] = sid; | |||
} | |||
public int getSize() | |||
{ | |||
return SIZE; | |||
} | |||
public String toFormulaString() | |||
{ | |||
return "()"; | |||
} | |||
public String toFormulaString(String[] operands) { | |||
return "("+operands[0]+")"; | |||
} | |||
private final static int SIZE = 1; | |||
public final static byte sid = 0x15; | |||
public static final ControlPtg instance = new ParenthesisPtg(); | |||
private ParenthesisPtg() { | |||
// enforce singleton | |||
} | |||
public void write(LittleEndianOutput out) { | |||
out.writeByte(sid + getPtgClass()); | |||
} | |||
public int getSize() { | |||
return SIZE; | |||
} | |||
public String toFormulaString() { | |||
return "()"; | |||
} | |||
public String toFormulaString(String[] operands) { | |||
return "(" + operands[0] + ")"; | |||
} | |||
} |
@@ -20,8 +20,8 @@ package org.apache.poi.hssf.record.formula; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.apache.poi.hssf.record.RecordInputStream; | |||
import org.apache.poi.util.HexDump; | |||
import org.apache.poi.util.LittleEndianByteArrayOutputStream; | |||
import org.apache.poi.util.LittleEndianInput; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
/** | |||
@@ -48,12 +48,12 @@ public abstract class Ptg implements Cloneable { | |||
* Reads <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; | |||
while (pos < size) { | |||
Ptg ptg = Ptg.createPtg( in ); | |||
Ptg ptg = Ptg.createPtg(in); | |||
if (ptg instanceof ArrayPtg) { | |||
if (arrayPtgs == null) { | |||
arrayPtgs = new ArrayList(5); | |||
@@ -77,7 +77,7 @@ public abstract class Ptg implements Cloneable { | |||
return toPtgArray(temp); | |||
} | |||
public static Ptg createPtg(RecordInputStream in) { | |||
public static Ptg createPtg(LittleEndianInput in) { | |||
byte id = in.readByte(); | |||
if (id < 0x20) { | |||
@@ -97,7 +97,7 @@ public abstract class Ptg implements Cloneable { | |||
return retval; | |||
} | |||
private static Ptg createClassifiedPtg(byte id, RecordInputStream in) { | |||
private static Ptg createClassifiedPtg(byte id, LittleEndianInput in) { | |||
int baseId = id & 0x1F | 0x20; | |||
@@ -126,9 +126,9 @@ public abstract class Ptg implements Cloneable { | |||
Integer.toHexString(id) + " (" + ( int ) id + ")"); | |||
} | |||
private static Ptg createBasePtg(byte id, RecordInputStream in) { | |||
private static Ptg createBasePtg(byte id, LittleEndianInput in) { | |||
switch(id) { | |||
case 0x00: return new UnknownPtg(); // TODO - not a real Ptg | |||
case 0x00: return new UnknownPtg(id); // TODO - not a real Ptg | |||
case ExpPtg.sid: return new ExpPtg(in); // 0x01 | |||
case TblPtg.sid: return new TblPtg(in); // 0x02 | |||
case AddPtg.sid: return AddPtg.instance; // 0x03 | |||
@@ -228,32 +228,30 @@ public abstract class Ptg implements Cloneable { | |||
* @return number of bytes written | |||
*/ | |||
public static int serializePtgs(Ptg[] ptgs, byte[] array, int offset) { | |||
int pos = 0; | |||
int size = ptgs.length; | |||
int nTokens = ptgs.length; | |||
LittleEndianByteArrayOutputStream out = new LittleEndianByteArrayOutputStream(array, offset); | |||
List arrayPtgs = null; | |||
for (int k = 0; k < size; k++) { | |||
for (int k = 0; k < nTokens; k++) { | |||
Ptg ptg = ptgs[k]; | |||
ptg.writeBytes(array, pos + offset); | |||
ptg.write(out); | |||
if (ptg instanceof ArrayPtg) { | |||
if (arrayPtgs == null) { | |||
arrayPtgs = new ArrayList(5); | |||
} | |||
arrayPtgs.add(ptg); | |||
pos += ArrayPtg.PLAIN_TOKEN_SIZE; | |||
} else { | |||
pos += ptg.getSize(); | |||
} | |||
} | |||
if (arrayPtgs != null) { | |||
for (int i=0;i<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 |
@@ -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() |
@@ -17,51 +17,55 @@ | |||
package org.apache.poi.hssf.record.formula; | |||
import org.apache.poi.hssf.record.RecordInputStream; | |||
import org.apache.poi.util.LittleEndian; | |||
import org.apache.poi.util.LittleEndianInput; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
/** | |||
* @author Josh Micich | |||
*/ | |||
abstract class Ref2DPtgBase extends RefPtgBase { | |||
private final static int SIZE = 5; | |||
private final static int SIZE = 5; | |||
/** | |||
* Takes in a String representation of a cell reference and fills out the | |||
* numeric fields. | |||
*/ | |||
protected Ref2DPtgBase(String cellref) { | |||
super(cellref); | |||
} | |||
/** | |||
* Takes in a String representation of a cell reference and fills out the | |||
* numeric fields. | |||
*/ | |||
protected Ref2DPtgBase(String cellref) { | |||
super(cellref); | |||
} | |||
protected Ref2DPtgBase(int row, int column, boolean isRowRelative, boolean isColumnRelative) { | |||
setRow(row); | |||
setColumn(column); | |||
setRowRelative(isRowRelative); | |||
setColRelative(isColumnRelative); | |||
} | |||
protected Ref2DPtgBase(int row, int column, boolean isRowRelative, boolean isColumnRelative) { | |||
setRow(row); | |||
setColumn(column); | |||
setRowRelative(isRowRelative); | |||
setColRelative(isColumnRelative); | |||
} | |||
protected Ref2DPtgBase(RecordInputStream in) { | |||
readCoordinates(in); | |||
} | |||
public final void writeBytes(byte [] array, int offset) { | |||
LittleEndian.putByte(array, offset+0, getSid() + getPtgClass()); | |||
writeCoordinates(array, offset+1); | |||
} | |||
public final String toFormulaString() { | |||
return formatReferenceAsString(); | |||
} | |||
protected Ref2DPtgBase(LittleEndianInput in) { | |||
readCoordinates(in); | |||
} | |||
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()); | |||
sb.append(" ["); | |||
sb.append(formatReferenceAsString()); | |||
sb.append("]"); | |||
return sb.toString(); | |||
} | |||
public final int getSize() { | |||
return SIZE; | |||
} | |||
public final String toString() { | |||
StringBuffer sb = new StringBuffer(); | |||
sb.append(getClass().getName()); | |||
sb.append(" ["); | |||
sb.append(formatReferenceAsString()); | |||
sb.append("]"); | |||
return sb.toString(); | |||
} | |||
} |
@@ -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() { |
@@ -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() |
@@ -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); | |||
} | |||
@@ -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); | |||
} | |||
@@ -17,112 +17,114 @@ | |||
package org.apache.poi.hssf.record.formula; | |||
import org.apache.poi.hssf.record.RecordInputStream; | |||
import org.apache.poi.hssf.util.CellReference; | |||
import org.apache.poi.util.BitField; | |||
import org.apache.poi.util.BitFieldFactory; | |||
import org.apache.poi.util.LittleEndian; | |||
import org.apache.poi.util.LittleEndianInput; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
/** | |||
* ReferencePtgBase - handles references (such as A1, A2, IA4) | |||
* @author Andrew C. Oliver (acoliver@apache.org) | |||
* | |||
* @author Andrew C. Oliver (acoliver@apache.org) | |||
* @author Jason Height (jheight at chariot dot net dot au) | |||
*/ | |||
public abstract class RefPtgBase extends OperandPtg { | |||
private final static int MAX_ROW_NUMBER = 65536; | |||
/** The row index - zero based unsigned 16 bit value */ | |||
private int field_1_row; | |||
/** Field 2 | |||
* - lower 8 bits is the zero based unsigned byte column index | |||
* - bit 16 - isRowRelative | |||
* - bit 15 - isColumnRelative | |||
*/ | |||
private int field_2_col; | |||
private static final BitField rowRelative = BitFieldFactory.getInstance(0x8000); | |||
private static final BitField colRelative = BitFieldFactory.getInstance(0x4000); | |||
private static final BitField column = BitFieldFactory.getInstance(0x00FF); | |||
protected RefPtgBase() { | |||
//Required for clone methods | |||
} | |||
/** | |||
* Takes in a String representation of a cell reference and fills out the | |||
* numeric fields. | |||
*/ | |||
protected RefPtgBase(String cellref) { | |||
CellReference c= new CellReference(cellref); | |||
setRow(c.getRow()); | |||
setColumn(c.getCol()); | |||
setColRelative(!c.isColAbsolute()); | |||
setRowRelative(!c.isRowAbsolute()); | |||
} | |||
protected RefPtgBase(int row, int column, boolean isRowRelative, boolean isColumnRelative) { | |||
setRow(row); | |||
setColumn(column); | |||
setRowRelative(isRowRelative); | |||
setColRelative(isColumnRelative); | |||
} | |||
protected final void readCoordinates(RecordInputStream in) { | |||
field_1_row = in.readUShort(); | |||
field_2_col = in.readUShort(); | |||
} | |||
protected final void writeCoordinates(byte[] array, int offset) { | |||
LittleEndian.putUShort(array, offset + 0, field_1_row); | |||
LittleEndian.putUShort(array, offset + 2, field_2_col); | |||
} | |||
public final void setRow(int row) { | |||
if(row < 0 || row >= MAX_ROW_NUMBER) { | |||
throw new IllegalArgumentException("The row number, when specified as an integer, must be between 0 and " + MAX_ROW_NUMBER); | |||
} | |||
field_1_row = row; | |||
} | |||
/** | |||
* @return the row number as an int, between 0 and 65535 | |||
*/ | |||
public final int getRow(){ | |||
return field_1_row; | |||
} | |||
public final boolean isRowRelative() { | |||
return rowRelative.isSet(field_2_col); | |||
} | |||
public final void setRowRelative(boolean rel) { | |||
field_2_col=rowRelative.setBoolean(field_2_col,rel); | |||
} | |||
public final boolean isColRelative() { | |||
return colRelative.isSet(field_2_col); | |||
} | |||
public final void setColRelative(boolean rel) { | |||
field_2_col=colRelative.setBoolean(field_2_col,rel); | |||
} | |||
public final void setColumn(int col) { | |||
if(col < 0 || col >= 0x100) { | |||
throw new IllegalArgumentException("Specified colIx (" + col + ") is out of range"); | |||
} | |||
field_2_col = column.setValue(field_2_col, col); | |||
} | |||
public final int getColumn() { | |||
return column.getValue(field_2_col); | |||
} | |||
protected final String formatReferenceAsString() { | |||
// Only make cell references as needed. Memory is an issue | |||
CellReference cr = new CellReference(getRow(), getColumn(), !isRowRelative(), !isColRelative()); | |||
return cr.formatAsString(); | |||
} | |||
public final byte getDefaultOperandClass() { | |||
return Ptg.CLASS_REF; | |||
} | |||
private final static int MAX_ROW_NUMBER = 65536; | |||
/** The row index - zero based unsigned 16 bit value */ | |||
private int field_1_row; | |||
/** | |||
* Field 2 - lower 8 bits is the zero based unsigned byte column index - bit | |||
* 16 - isRowRelative - bit 15 - isColumnRelative | |||
*/ | |||
private int field_2_col; | |||
private static final BitField rowRelative = BitFieldFactory.getInstance(0x8000); | |||
private static final BitField colRelative = BitFieldFactory.getInstance(0x4000); | |||
private static final BitField column = BitFieldFactory.getInstance(0x00FF); | |||
protected RefPtgBase() { | |||
// Required for clone methods | |||
} | |||
/** | |||
* Takes in a String representation of a cell reference and fills out the | |||
* numeric fields. | |||
*/ | |||
protected RefPtgBase(String cellref) { | |||
CellReference c = new CellReference(cellref); | |||
setRow(c.getRow()); | |||
setColumn(c.getCol()); | |||
setColRelative(!c.isColAbsolute()); | |||
setRowRelative(!c.isRowAbsolute()); | |||
} | |||
protected RefPtgBase(int row, int column, boolean isRowRelative, boolean isColumnRelative) { | |||
setRow(row); | |||
setColumn(column); | |||
setRowRelative(isRowRelative); | |||
setColRelative(isColumnRelative); | |||
} | |||
protected final void readCoordinates(LittleEndianInput in) { | |||
field_1_row = in.readUShort(); | |||
field_2_col = in.readUShort(); | |||
} | |||
protected final void writeCoordinates(LittleEndianOutput out) { | |||
out.writeShort(field_1_row); | |||
out.writeShort(field_2_col); | |||
} | |||
public final void setRow(int rowIndex) { | |||
if (rowIndex < 0 || rowIndex >= MAX_ROW_NUMBER) { | |||
throw new IllegalArgumentException("rowIndex must be between 0 and " + MAX_ROW_NUMBER); | |||
} | |||
field_1_row = rowIndex; | |||
} | |||
/** | |||
* @return the row number as an int, between 0 and 65535 | |||
*/ | |||
public final int getRow() { | |||
return field_1_row; | |||
} | |||
public final boolean isRowRelative() { | |||
return rowRelative.isSet(field_2_col); | |||
} | |||
public final void setRowRelative(boolean rel) { | |||
field_2_col = rowRelative.setBoolean(field_2_col, rel); | |||
} | |||
public final boolean isColRelative() { | |||
return colRelative.isSet(field_2_col); | |||
} | |||
public final void setColRelative(boolean rel) { | |||
field_2_col = colRelative.setBoolean(field_2_col, rel); | |||
} | |||
public final void setColumn(int col) { | |||
if (col < 0 || col >= 0x100) { | |||
throw new IllegalArgumentException("Specified colIx (" + col + ") is out of range"); | |||
} | |||
field_2_col = column.setValue(field_2_col, col); | |||
} | |||
public final int getColumn() { | |||
return column.getValue(field_2_col); | |||
} | |||
protected final String formatReferenceAsString() { | |||
// Only make cell references as needed. Memory is an issue | |||
CellReference cr = new CellReference(getRow(), getColumn(), !isRowRelative(), !isColRelative()); | |||
return cr.formatAsString(); | |||
} | |||
public final byte getDefaultOperandClass() { | |||
return Ptg.CLASS_REF; | |||
} | |||
} |
@@ -17,9 +17,8 @@ | |||
package org.apache.poi.hssf.record.formula; | |||
import org.apache.poi.hssf.record.RecordInputStream; | |||
import org.apache.poi.util.BitField; | |||
import org.apache.poi.util.BitFieldFactory; | |||
import org.apache.poi.util.LittleEndianInput; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
import org.apache.poi.util.StringUtil; | |||
/** | |||
@@ -31,29 +30,26 @@ import org.apache.poi.util.StringUtil; | |||
* @author Bernard Chesnoy | |||
*/ | |||
public final class StringPtg extends ScalarConstantPtg { | |||
public final static int SIZE = 9; | |||
public final static byte sid = 0x17; | |||
private static final BitField fHighByte = BitFieldFactory.getInstance(0x01); | |||
/** the character (")used in formulas to delimit string literals */ | |||
public final static byte sid = 0x17; | |||
/** the character (") used in formulas to delimit string literals */ | |||
private static final char FORMULA_DELIMITER = '"'; | |||
private final boolean _is16bitUnicode; | |||
/** | |||
* NOTE: OO doc says 16bit length, but BiffViewer says 8 Book says something | |||
* totally different, so don't look there! | |||
*/ | |||
private final int field_1_length; | |||
private final byte field_2_options; | |||
private final String field_3_string; | |||
/** Create a StringPtg from a stream */ | |||
public StringPtg(RecordInputStream in) { | |||
field_1_length = in.readUByte(); | |||
field_2_options = in.readByte(); | |||
if (fHighByte.isSet(field_2_options)) { | |||
field_3_string = in.readUnicodeLEString(field_1_length); | |||
} else { | |||
field_3_string = in.readCompressedUnicode(field_1_length); | |||
} | |||
public StringPtg(LittleEndianInput in) { | |||
int nChars = in.readUByte(); // Note - nChars is 8-bit | |||
_is16bitUnicode = (in.readByte() & 0x01) != 0; | |||
if (_is16bitUnicode) { | |||
field_3_string = StringUtil.readUnicodeLE(in, nChars); | |||
} else { | |||
field_3_string = StringUtil.readCompressedUnicode(in, nChars); | |||
} | |||
} | |||
/** | |||
@@ -69,32 +65,27 @@ public final class StringPtg extends ScalarConstantPtg { | |||
throw new IllegalArgumentException( | |||
"String literals in formulas can't be bigger than 255 characters ASCII"); | |||
} | |||
field_2_options = (byte) fHighByte.setBoolean(0, StringUtil.hasMultibyte(value)); | |||
_is16bitUnicode = StringUtil.hasMultibyte(value); | |||
field_3_string = value; | |||
field_1_length = value.length(); // for the moment, we support only ASCII strings in formulas we create | |||
} | |||
public String getValue() { | |||
return field_3_string; | |||
} | |||
public void writeBytes(byte[] array, int offset) { | |||
array[offset + 0] = sid; | |||
array[offset + 1] = (byte) field_1_length; | |||
array[offset + 2] = field_2_options; | |||
if (fHighByte.isSet(field_2_options)) { | |||
StringUtil.putUnicodeLE(getValue(), array, offset + 3); | |||
public void write(LittleEndianOutput out) { | |||
out.writeByte(sid + getPtgClass()); | |||
out.writeByte(field_3_string.length()); // Note - nChars is 8-bit | |||
out.writeByte(_is16bitUnicode ? 0x01 : 0x00); | |||
if (_is16bitUnicode) { | |||
StringUtil.putUnicodeLE(field_3_string, out); | |||
} else { | |||
StringUtil.putCompressedUnicode(getValue(), array, offset + 3); | |||
StringUtil.putCompressedUnicode(field_3_string, out); | |||
} | |||
} | |||
public int getSize() { | |||
if (fHighByte.isSet(field_2_options)) { | |||
return 2 * field_1_length + 3; | |||
} else { | |||
return field_1_length + 3; | |||
} | |||
return 3 + field_3_string.length() * (_is16bitUnicode ? 2 : 1); | |||
} | |||
public String toFormulaString() { |
@@ -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() { |
@@ -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() |
@@ -16,7 +16,7 @@ | |||
==================================================================== */ | |||
package org.apache.poi.hssf.record.formula; | |||
import org.apache.poi.hssf.record.RecordInputStream; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
/** | |||
* | |||
@@ -25,22 +25,17 @@ import org.apache.poi.hssf.record.RecordInputStream; | |||
*/ | |||
public class UnknownPtg extends Ptg { | |||
private short size = 1; | |||
private final int _sid; | |||
/** Creates new UnknownPtg */ | |||
public UnknownPtg() | |||
{ | |||
} | |||
public UnknownPtg(RecordInputStream in) { | |||
// doesn't need anything | |||
public UnknownPtg(int sid) { | |||
_sid = sid; | |||
} | |||
public boolean isBaseToken() { | |||
return true; | |||
return true; | |||
} | |||
public void writeBytes(byte [] array, int offset) | |||
{ | |||
public void write(LittleEndianOutput out) { | |||
out.writeByte(_sid); | |||
} | |||
public int getSize() | |||
@@ -55,8 +50,6 @@ public class UnknownPtg extends Ptg { | |||
public byte getDefaultOperandClass() {return Ptg.CLASS_VALUE;} | |||
public Object clone() { | |||
return new UnknownPtg(); | |||
return this; | |||
} | |||
} |
@@ -17,18 +17,19 @@ | |||
package org.apache.poi.hssf.record.formula; | |||
import org.apache.poi.hssf.usermodel.HSSFWorkbook; | |||
import org.apache.poi.util.LittleEndianOutput; | |||
/** | |||
* Common superclass of all value operators. | |||
* Subclasses include all unary and binary operators except for the reference operators (IntersectionPtg, RangePtg, UnionPtg) | |||
* Common superclass of all value operators. Subclasses include all unary and | |||
* binary operators except for the reference operators (IntersectionPtg, | |||
* RangePtg, UnionPtg) | |||
* | |||
* @author Josh Micich | |||
*/ | |||
public abstract class ValueOperatorPtg extends OperationPtg { | |||
/** | |||
* All Operator <tt>Ptg</tt>s are base tokens (i.e. are not RVA classified) | |||
* All Operator <tt>Ptg</tt>s are base tokens (i.e. are not RVA classified) | |||
*/ | |||
public final boolean isBaseToken() { | |||
return true; | |||
@@ -38,8 +39,8 @@ public abstract class ValueOperatorPtg extends OperationPtg { | |||
return Ptg.CLASS_VALUE; | |||
} | |||
public final void writeBytes(byte[] array, int offset) { | |||
array[offset + 0] = getSid(); | |||
public void write(LittleEndianOutput out) { | |||
out.writeByte(getSid()); | |||
} | |||
protected abstract byte getSid(); | |||
@@ -47,8 +48,9 @@ public abstract class ValueOperatorPtg extends OperationPtg { | |||
public final int getSize() { | |||
return 1; | |||
} | |||
public final String toFormulaString() { | |||
// TODO - prune this method out of the hierarchy | |||
throw new RuntimeException("toFormulaString(String[] operands) should be used for subclasses of OperationPtgs"); | |||
public final String toFormulaString() { | |||
// TODO - prune this method out of the hierarchy | |||
throw new RuntimeException("toFormulaString(String[] operands) should be used for subclasses of OperationPtgs"); | |||
} | |||
} |
@@ -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); |
@@ -1,4 +1,3 @@ | |||
/* ==================================================================== | |||
Licensed to the Apache Software Foundation (ASF) under one or more | |||
contributor license agreements. See the NOTICE file distributed with | |||
@@ -15,437 +14,314 @@ | |||
See the License for the specific language governing permissions and | |||
limitations under the License. | |||
==================================================================== */ | |||
package org.apache.poi.poifs.filesystem; | |||
import java.io.*; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import org.apache.poi.poifs.storage.DataInputBlock; | |||
import org.apache.poi.util.LittleEndianInput; | |||
/** | |||
* This class provides methods to read a DocumentEntry managed by a | |||
* Filesystem instance. | |||
* {@link POIFSFileSystem} instance. | |||
* | |||
* @author Marc Johnson (mjohnson at apache dot org) | |||
*/ | |||
public class DocumentInputStream | |||
extends InputStream | |||
{ | |||
// current offset into the Document | |||
private int _current_offset; | |||
// current marked offset into the Document (used by mark and | |||
// reset) | |||
private int _marked_offset; | |||
// the Document's size | |||
private int _document_size; | |||
// have we been closed? | |||
private boolean _closed; | |||
// the actual Document | |||
private POIFSDocument _document; | |||
// buffer used to read one byte at a time | |||
private byte[] _tiny_buffer; | |||
// returned by read operations if we're at end of document | |||
static private final int EOD = -1; | |||
/** | |||
* Create an InputStream from the specified DocumentEntry | |||
* | |||
* @param document the DocumentEntry to be read | |||
* | |||
* @exception IOException if the DocumentEntry cannot be opened | |||
* (like, maybe it has been deleted?) | |||
*/ | |||
public DocumentInputStream(final DocumentEntry document) | |||
throws IOException | |||
{ | |||
_current_offset = 0; | |||
_marked_offset = 0; | |||
_document_size = document.getSize(); | |||
_closed = false; | |||
_tiny_buffer = null; | |||
if (document instanceof DocumentNode) | |||
{ | |||
_document = (( DocumentNode ) document).getDocument(); | |||
} | |||
else | |||
{ | |||
throw new IOException("Cannot open internal document storage"); | |||
} | |||
} | |||
/** | |||
* Create an InputStream from the specified Document | |||
* | |||
* @param document the Document to be read | |||
* | |||
* @exception IOException if the DocumentEntry cannot be opened | |||
* (like, maybe it has been deleted?) | |||
*/ | |||
public DocumentInputStream(final POIFSDocument document) | |||
throws IOException | |||
{ | |||
_current_offset = 0; | |||
_marked_offset = 0; | |||
_document_size = document.getSize(); | |||
_closed = false; | |||
_tiny_buffer = null; | |||
_document = document; | |||
} | |||
/** | |||
* Returns the number of bytes that can be read (or skipped over) | |||
* from this input stream without blocking by the next caller of a | |||
* method for this input stream. The next caller might be the same | |||
* thread or or another thread. | |||
* | |||
* @return the number of bytes that can be read from this input | |||
* stream without blocking. | |||
* | |||
* @exception IOException on error (such as the stream has been | |||
* closed) | |||
*/ | |||
public int available() | |||
throws IOException | |||
{ | |||
dieIfClosed(); | |||
return _document_size - _current_offset; | |||
} | |||
/** | |||
* Closes this input stream and releases any system resources | |||
* associated with the stream. | |||
* | |||
* @exception IOException | |||
*/ | |||
public void close() | |||
throws IOException | |||
{ | |||
_closed = true; | |||
} | |||
/** | |||
* Marks the current position in this input stream. A subsequent | |||
* call to the reset method repositions this stream at the last | |||
* marked position so that subsequent reads re-read the same | |||
* bytes. | |||
* <p> | |||
* The readlimit arguments tells this input stream to allow that | |||
* many bytes to be read before the mark position gets | |||
* invalidated. This implementation, however, does not care. | |||
* <p> | |||
* The general contract of mark is that, if the method | |||
* markSupported returns true, the stream somehow remembers all | |||
* the bytes read after the call to mark and stands ready to | |||
* supply those same bytes again if and whenever the method reset | |||
* is called. However, the stream is not required to remember any | |||
* data at all if more than readlimit bytes are read from the | |||
* stream before reset is called. But this stream will. | |||
* | |||
* @param ignoredReadlimit the maximum limit of bytes that can be | |||
* read before the mark position becomes | |||
* invalid. Ignored by this | |||
* implementation. | |||
*/ | |||
public void mark(int ignoredReadlimit) | |||
{ | |||
_marked_offset = _current_offset; | |||
} | |||
/** | |||
* Tests if this input stream supports the mark and reset methods. | |||
* | |||
* @return true | |||
*/ | |||
public boolean markSupported() | |||
{ | |||
return true; | |||
} | |||
/** | |||
* Reads the next byte of data from the input stream. The value | |||
* byte is returned as an int in the range 0 to 255. If no byte is | |||
* available because the end of the stream has been reached, the | |||
* value -1 is returned. The definition of this method in | |||
* java.io.InputStream allows this method to block, but it won't. | |||
* | |||
* @return the next byte of data, or -1 if the end of the stream | |||
* is reached. | |||
* | |||
* @exception IOException | |||
*/ | |||
public int read() | |||
throws IOException | |||
{ | |||
dieIfClosed(); | |||
if (atEOD()) | |||
{ | |||
return EOD; | |||
} | |||
if (_tiny_buffer == null) | |||
{ | |||
_tiny_buffer = new byte[ 1 ]; | |||
} | |||
_document.read(_tiny_buffer, _current_offset++); | |||
return ((int)_tiny_buffer[ 0 ]) & 0x000000FF; | |||
} | |||
/** | |||
* Reads some number of bytes from the input stream and stores | |||
* them into the buffer array b. The number of bytes actually read | |||
* is returned as an integer. The definition of this method in | |||
* java.io.InputStream allows this method to block, but it won't. | |||
* <p> | |||
* If b is null, a NullPointerException is thrown. If the length | |||
* of b is zero, then no bytes are read and 0 is returned; | |||
* otherwise, there is an attempt to read at least one byte. If no | |||
* byte is available because the stream is at end of file, the | |||
* value -1 is returned; otherwise, at least one byte is read and | |||
* stored into b. | |||
* <p> | |||
* The first byte read is stored into element b[0], the next one | |||
* into b[1], and so on. The number of bytes read is, at most, | |||
* equal to the length of b. Let k be the number of bytes actually | |||
* read; these bytes will be stored in elements b[0] through | |||
* b[k-1], leaving elements b[k] through b[b.length-1] unaffected. | |||
* <p> | |||
* If the first byte cannot be read for any reason other than end | |||
* of file, then an IOException is thrown. In particular, an | |||
* IOException is thrown if the input stream has been closed. | |||
* <p> | |||
* The read(b) method for class InputStream has the same effect as: | |||
* <p> | |||
* <code>read(b, 0, b.length)</code> | |||
* | |||
* @param b the buffer into which the data is read. | |||
* | |||
* @return the total number of bytes read into the buffer, or -1 | |||
* if there is no more data because the end of the stream | |||
* has been reached. | |||
* | |||
* @exception IOException | |||
* @exception NullPointerException | |||
*/ | |||
public int read(final byte [] b) | |||
throws IOException, NullPointerException | |||
{ | |||
return read(b, 0, b.length); | |||
} | |||
/** | |||
* Reads up to len bytes of data from the input stream into an | |||
* array of bytes. An attempt is made to read as many as len | |||
* bytes, but a smaller number may be read, possibly zero. The | |||
* number of bytes actually read is returned as an integer. | |||
* <p> | |||
* The definition of this method in java.io.InputStream allows it | |||
* to block, but it won't. | |||
* <p> | |||
* If b is null, a NullPointerException is thrown. | |||
* <p> | |||
* If off is negative, or len is negative, or off+len is greater | |||
* than the length of the array b, then an | |||
* IndexOutOfBoundsException is thrown. | |||
* <p> | |||
* If len is zero, then no bytes are read and 0 is returned; | |||
* otherwise, there is an attempt to read at least one byte. If no | |||
* byte is available because the stream is at end of file, the | |||
* value -1 is returned; otherwise, at least one byte is read and | |||
* stored into b. | |||
* <p> | |||
* The first byte read is stored into element b[off], the next one | |||
* into b[off+1], and so on. The number of bytes read is, at most, | |||
* equal to len. Let k be the number of bytes actually read; these | |||
* bytes will be stored in elements b[off] through b[off+k-1], | |||
* leaving elements b[off+k] through b[off+len-1] unaffected. | |||
* <p> | |||
* In every case, elements b[0] through b[off] and elements | |||
* b[off+len] through b[b.length-1] are unaffected. | |||
* <p> | |||
* If the first byte cannot be read for any reason other than end | |||
* of file, then an IOException is thrown. In particular, an | |||
* IOException is thrown if the input stream has been closed. | |||
* | |||
* @param b the buffer into which the data is read. | |||
* @param off the start offset in array b at which the data is | |||
* written. | |||
* @param len the maximum number of bytes to read. | |||
* | |||
* @return the total number of bytes read into the buffer, or -1 | |||
* if there is no more data because the end of the stream | |||
* has been reached. | |||
* | |||
* @exception IOException | |||
* @exception NullPointerException | |||
* @exception IndexOutOfBoundsException | |||
*/ | |||
public int read(final byte [] b, final int off, final int len) | |||
throws IOException, NullPointerException, IndexOutOfBoundsException | |||
{ | |||
dieIfClosed(); | |||
if (b == null) | |||
{ | |||
throw new NullPointerException("buffer is null"); | |||
} | |||
if ((off < 0) || (len < 0) || (b.length < (off + len))) | |||
{ | |||
throw new IndexOutOfBoundsException( | |||
"can't read past buffer boundaries"); | |||
} | |||
if (len == 0) | |||
{ | |||
return 0; | |||
} | |||
if (atEOD()) | |||
{ | |||
return EOD; | |||
} | |||
int limit = Math.min(available(), len); | |||
if ((off == 0) && (limit == b.length)) | |||
{ | |||
_document.read(b, _current_offset); | |||
} | |||
else | |||
{ | |||
byte[] buffer = new byte[ limit ]; | |||
_document.read(buffer, _current_offset); | |||
System.arraycopy(buffer, 0, b, off, limit); | |||
} | |||
_current_offset += limit; | |||
return limit; | |||
} | |||
/** | |||
* Repositions this stream to the position at the time the mark | |||
* method was last called on this input stream. | |||
* <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. | |||
*/ | |||
public void reset() | |||
{ | |||
_current_offset = _marked_offset; | |||
} | |||
/** | |||
* Skips over and discards n bytes of data from this input | |||
* stream. The skip method may, for a variety of reasons, end up | |||
* skipping over some smaller number of bytes, possibly 0. This | |||
* may result from any of a number of conditions; reaching end of | |||
* file before n bytes have been skipped is only one | |||
* possibility. The actual number of bytes skipped is returned. If | |||
* n is negative, no bytes are skipped. | |||
* | |||
* @param n the number of bytes to be skipped. | |||
* | |||
* @return the actual number of bytes skipped. | |||
* | |||
* @exception IOException | |||
*/ | |||
public long skip(final long n) | |||
throws IOException | |||
{ | |||
dieIfClosed(); | |||
if (n < 0) | |||
{ | |||
return 0; | |||
} | |||
int new_offset = _current_offset + ( int ) n; | |||
if (new_offset < _current_offset) | |||
{ | |||
// wrap around in converting a VERY large long to an int | |||
new_offset = _document_size; | |||
} | |||
else if (new_offset > _document_size) | |||
{ | |||
new_offset = _document_size; | |||
} | |||
long rval = new_offset - _current_offset; | |||
_current_offset = new_offset; | |||
return rval; | |||
} | |||
private void dieIfClosed() | |||
throws IOException | |||
{ | |||
if (_closed) | |||
{ | |||
throw new IOException( | |||
"cannot perform requested operation on a closed stream"); | |||
} | |||
} | |||
private boolean atEOD() | |||
{ | |||
return _current_offset == _document_size; | |||
} | |||
} // end public class DocumentInputStream | |||
public final class DocumentInputStream extends InputStream implements LittleEndianInput { | |||
/** returned by read operations if we're at end of document */ | |||
private static final int EOF = -1; | |||
private static final int SIZE_SHORT = 2; | |||
private static final int SIZE_INT = 4; | |||
private static final int SIZE_LONG = 8; | |||
/** current offset into the Document */ | |||
private int _current_offset; | |||
/** current marked offset into the Document (used by mark and reset) */ | |||
private int _marked_offset; | |||
/** the Document's size */ | |||
private int _document_size; | |||
/** have we been closed? */ | |||
private boolean _closed; | |||
/** the actual Document */ | |||
private POIFSDocument _document; | |||
/** the data block containing the current stream pointer */ | |||
private DataInputBlock _currentBlock; | |||
/** | |||
* Create an InputStream from the specified DocumentEntry | |||
* | |||
* @param document the DocumentEntry to be read | |||
* | |||
* @exception IOException if the DocumentEntry cannot be opened (like, maybe it has | |||
* been deleted?) | |||
*/ | |||
public DocumentInputStream(DocumentEntry document) throws IOException { | |||
if (!(document instanceof DocumentNode)) { | |||
throw new IOException("Cannot open internal document storage"); | |||
} | |||
_current_offset = 0; | |||
_marked_offset = 0; | |||
_document_size = document.getSize(); | |||
_closed = false; | |||
_document = ((DocumentNode) document).getDocument(); | |||
_currentBlock = getDataInputBlock(0); | |||
} | |||
/** | |||
* Create an InputStream from the specified Document | |||
* | |||
* @param document the Document to be read | |||
*/ | |||
public DocumentInputStream(POIFSDocument document) { | |||
_current_offset = 0; | |||
_marked_offset = 0; | |||
_document_size = document.getSize(); | |||
_closed = false; | |||
_document = document; | |||
_currentBlock = getDataInputBlock(0); | |||
} | |||
public int available() { | |||
if (_closed) { | |||
throw new IllegalStateException("cannot perform requested operation on a closed stream"); | |||
} | |||
return _document_size - _current_offset; | |||
} | |||
public void close() { | |||
_closed = true; | |||
} | |||
public void mark(int ignoredReadlimit) { | |||
_marked_offset = _current_offset; | |||
} | |||
/** | |||
* Tests if this input stream supports the mark and reset methods. | |||
* | |||
* @return <code>true</code> always | |||
*/ | |||
public boolean markSupported() { | |||
return true; | |||
} | |||
private DataInputBlock getDataInputBlock(int offset) { | |||
return _document.getDataInputBlock(offset); | |||
} | |||
public int read() throws IOException { | |||
dieIfClosed(); | |||
if (atEOD()) { | |||
return EOF; | |||
} | |||
int result = _currentBlock.readUByte(); | |||
_current_offset++; | |||
if (_currentBlock.available() < 1) { | |||
_currentBlock = getDataInputBlock(_current_offset); | |||
} | |||
return result; | |||
} | |||
public int read(byte[] b) throws IOException { | |||
return read(b, 0, b.length); | |||
} | |||
public int read(byte[] b, int off, int len) throws IOException { | |||
dieIfClosed(); | |||
if (b == null) { | |||
throw new IllegalArgumentException("buffer must not be null"); | |||
} | |||
if (off < 0 || len < 0 || b.length < off + len) { | |||
throw new IndexOutOfBoundsException("can't read past buffer boundaries"); | |||
} | |||
if (len == 0) { | |||
return 0; | |||
} | |||
if (atEOD()) { | |||
return EOF; | |||
} | |||
int limit = Math.min(available(), len); | |||
readFully(b, off, limit); | |||
return limit; | |||
} | |||
/** | |||
* Repositions this stream to the position at the time the mark() method was | |||
* last called on this input stream. If mark() has not been called this | |||
* method repositions the stream to its beginning. | |||
*/ | |||
public void reset() { | |||
_current_offset = _marked_offset; | |||
_currentBlock = getDataInputBlock(_current_offset); | |||
} | |||
public long skip(long n) throws IOException { | |||
dieIfClosed(); | |||
if (n < 0) { | |||
return 0; | |||
} | |||
int new_offset = _current_offset + (int) n; | |||
if (new_offset < _current_offset) { | |||
// wrap around in converting a VERY large long to an int | |||
new_offset = _document_size; | |||
} else if (new_offset > _document_size) { | |||
new_offset = _document_size; | |||
} | |||
long rval = new_offset - _current_offset; | |||
_current_offset = new_offset; | |||
_currentBlock = getDataInputBlock(_current_offset); | |||
return rval; | |||
} | |||
private void dieIfClosed() throws IOException { | |||
if (_closed) { | |||
throw new IOException("cannot perform requested operation on a closed stream"); | |||
} | |||
} | |||
private boolean atEOD() { | |||
return _current_offset == _document_size; | |||
} | |||
private void checkAvaliable(int requestedSize) { | |||
if (_closed) { | |||
throw new IllegalStateException("cannot perform requested operation on a closed stream"); | |||
} | |||
if (requestedSize > _document_size - _current_offset) { | |||
throw new RuntimeException("Buffer underrun - requested " + requestedSize | |||
+ " bytes but " + (_document_size - _current_offset) + " was available"); | |||
} | |||
} | |||
public byte readByte() { | |||
return (byte) readUByte(); | |||
} | |||
public double readDouble() { | |||
return Double.longBitsToDouble(readLong()); | |||
} | |||
public void readFully(byte[] buf) { | |||
readFully(buf, 0, buf.length); | |||
} | |||
public short readShort() { | |||
return (short) readUShort(); | |||
} | |||
public void readFully(byte[] buf, int off, int len) { | |||
checkAvaliable(len); | |||
int blockAvailable = _currentBlock.available(); | |||
if (blockAvailable > len) { | |||
_currentBlock.readFully(buf, off, len); | |||
_current_offset += len; | |||
return; | |||
} | |||
// else read big amount in chunks | |||
int remaining = len; | |||
int writePos = off; | |||
while (remaining > 0) { | |||
boolean blockIsExpiring = remaining >= blockAvailable; | |||
int reqSize; | |||
if (blockIsExpiring) { | |||
reqSize = blockAvailable; | |||
} else { | |||
reqSize = remaining; | |||
} | |||
_currentBlock.readFully(buf, writePos, reqSize); | |||
remaining -= reqSize; | |||
writePos += reqSize; | |||
_current_offset += reqSize; | |||
if (blockIsExpiring) { | |||
if (_current_offset == _document_size) { | |||
if (remaining > 0) { | |||
throw new IllegalStateException( | |||
"reached end of document stream unexpectedly"); | |||
} | |||
_currentBlock = null; | |||
break; | |||
} | |||
_currentBlock = getDataInputBlock(_current_offset); | |||
blockAvailable = _currentBlock.available(); | |||
} | |||
} | |||
} | |||
public long readLong() { | |||
checkAvaliable(SIZE_LONG); | |||
int blockAvailable = _currentBlock.available(); | |||
long result; | |||
if (blockAvailable > SIZE_LONG) { | |||
result = _currentBlock.readLongLE(); | |||
} else { | |||
DataInputBlock nextBlock = getDataInputBlock(_current_offset + blockAvailable); | |||
if (blockAvailable == SIZE_LONG) { | |||
result = _currentBlock.readLongLE(); | |||
} else { | |||
result = nextBlock.readLongLE(_currentBlock, blockAvailable); | |||
} | |||
_currentBlock = nextBlock; | |||
} | |||
_current_offset += SIZE_LONG; | |||
return result; | |||
} | |||
public int readInt() { | |||
checkAvaliable(SIZE_INT); | |||
int blockAvailable = _currentBlock.available(); | |||
int result; | |||
if (blockAvailable > SIZE_INT) { | |||
result = _currentBlock.readIntLE(); | |||
} else { | |||
DataInputBlock nextBlock = getDataInputBlock(_current_offset + blockAvailable); | |||
if (blockAvailable == SIZE_INT) { | |||
result = _currentBlock.readIntLE(); | |||
} else { | |||
result = nextBlock.readIntLE(_currentBlock, blockAvailable); | |||
} | |||
_currentBlock = nextBlock; | |||
} | |||
_current_offset += SIZE_INT; | |||
return result; | |||
} | |||
public int readUShort() { | |||
checkAvaliable(SIZE_SHORT); | |||
int blockAvailable = _currentBlock.available(); | |||
int result; | |||
if (blockAvailable > SIZE_SHORT) { | |||
result = _currentBlock.readUShortLE(); | |||
} else { | |||
DataInputBlock nextBlock = getDataInputBlock(_current_offset + blockAvailable); | |||
if (blockAvailable == SIZE_SHORT) { | |||
result = _currentBlock.readUShortLE(); | |||
} else { | |||
result = nextBlock.readUShortLE(_currentBlock); | |||
} | |||
_currentBlock = nextBlock; | |||
} | |||
_current_offset += SIZE_SHORT; | |||
return result; | |||
} | |||
public int readUByte() { | |||
checkAvaliable(1); | |||
int result = _currentBlock.readUByte(); | |||
_current_offset++; | |||
if (_currentBlock.available() < 1) { | |||
_currentBlock = getDataInputBlock(_current_offset); | |||
} | |||
return result; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -1,4 +1,3 @@ | |||
/* ==================================================================== | |||
Licensed to the Apache Software Foundation (ASF) under one or more | |||
contributor license agreements. See the NOTICE file distributed with | |||
@@ -15,31 +14,27 @@ | |||
See the License for the specific language governing permissions and | |||
limitations under the License. | |||
==================================================================== */ | |||
package org.apache.poi.poifs.storage; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.OutputStream; | |||
import java.util.Arrays; | |||
import org.apache.poi.poifs.common.POIFSConstants; | |||
import org.apache.poi.util.IOUtils; | |||
import org.apache.poi.util.IntegerField; | |||
import org.apache.poi.util.LittleEndian; | |||
import org.apache.poi.util.LittleEndianConsts; | |||
/** | |||
* A block of document data. | |||
* | |||
* @author Marc Johnson (mjohnson at apache dot org) | |||
*/ | |||
public final class DocumentBlock extends BigBlock { | |||
private static final int BLOCK_SHIFT = 9; | |||
private static final int BLOCK_SIZE = 1 << BLOCK_SHIFT; | |||
private static final int BLOCK_MASK = BLOCK_SIZE-1; | |||
public class DocumentBlock | |||
extends BigBlock | |||
{ | |||
private static final byte _default_value = ( byte ) 0xFF; | |||
private byte[] _data; | |||
private int _bytes_read; | |||
@@ -161,45 +156,10 @@ public class DocumentBlock | |||
return rval; | |||
} | |||
/** | |||
* read data from an array of DocumentBlocks | |||
* | |||
* @param blocks the blocks to read from | |||
* @param buffer the buffer to write the data into | |||
* @param offset the offset into the array of blocks to read from | |||
*/ | |||
public static void read(final DocumentBlock [] blocks, | |||
final byte [] buffer, final int offset) | |||
{ | |||
int firstBlockIndex = offset / POIFSConstants.BIG_BLOCK_SIZE; | |||
int firstBlockOffset = offset % POIFSConstants.BIG_BLOCK_SIZE; | |||
int lastBlockIndex = (offset + buffer.length - 1) | |||
/ POIFSConstants.BIG_BLOCK_SIZE; | |||
if (firstBlockIndex == lastBlockIndex) | |||
{ | |||
System.arraycopy(blocks[ firstBlockIndex ]._data, | |||
firstBlockOffset, buffer, 0, buffer.length); | |||
} | |||
else | |||
{ | |||
int buffer_offset = 0; | |||
System.arraycopy(blocks[ firstBlockIndex ]._data, | |||
firstBlockOffset, buffer, buffer_offset, | |||
POIFSConstants.BIG_BLOCK_SIZE | |||
- firstBlockOffset); | |||
buffer_offset += POIFSConstants.BIG_BLOCK_SIZE - firstBlockOffset; | |||
for (int j = firstBlockIndex + 1; j < lastBlockIndex; j++) | |||
{ | |||
System.arraycopy(blocks[ j ]._data, 0, buffer, buffer_offset, | |||
POIFSConstants.BIG_BLOCK_SIZE); | |||
buffer_offset += POIFSConstants.BIG_BLOCK_SIZE; | |||
} | |||
System.arraycopy(blocks[ lastBlockIndex ]._data, 0, buffer, | |||
buffer_offset, buffer.length - buffer_offset); | |||
} | |||
public static DataInputBlock getDataInputBlock(DocumentBlock[] blocks, int offset) { | |||
int firstBlockIndex = offset >> BLOCK_SHIFT; | |||
int firstBlockOffset= offset & BLOCK_MASK; | |||
return new DataInputBlock(blocks[firstBlockIndex]._data, firstBlockOffset); | |||
} | |||
/* ********** START extension of BigBlock ********** */ |
@@ -1,4 +1,3 @@ | |||
/* ==================================================================== | |||
Licensed to the Apache Software Foundation (ASF) under one or more | |||
contributor license agreements. See the NOTICE file distributed with | |||
@@ -15,13 +14,15 @@ | |||
See the License for the specific language governing permissions and | |||
limitations under the License. | |||
==================================================================== */ | |||
package org.apache.poi.poifs.storage; | |||
import java.io.*; | |||
import java.util.*; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.IOException; | |||
import java.io.OutputStream; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.List; | |||
import org.apache.poi.poifs.common.POIFSConstants; | |||
@@ -31,13 +32,14 @@ import org.apache.poi.poifs.common.POIFSConstants; | |||
* | |||
* @author Marc Johnson (mjohnson at apache dot org) | |||
*/ | |||
public final class SmallDocumentBlock implements BlockWritable, ListManagedBlock { | |||
private static final int BLOCK_SHIFT = 6; | |||
public class SmallDocumentBlock | |||
implements BlockWritable, ListManagedBlock | |||
{ | |||
private byte[] _data; | |||
private static final byte _default_fill = ( byte ) 0xff; | |||
private static final int _block_size = 64; | |||
private static final int _block_size = 1 << BLOCK_SHIFT; | |||
private static final int BLOCK_MASK = _block_size-1; | |||
private static final int _blocks_per_big_block = | |||
POIFSConstants.BIG_BLOCK_SIZE / _block_size; | |||
@@ -178,46 +180,10 @@ public class SmallDocumentBlock | |||
return sdbs; | |||
} | |||
/** | |||
* read data from an array of SmallDocumentBlocks | |||
* | |||
* @param blocks the blocks to read from | |||
* @param buffer the buffer to write the data into | |||
* @param offset the offset into the array of blocks to read from | |||
*/ | |||
public static void read(final BlockWritable [] blocks, | |||
final byte [] buffer, final int offset) | |||
{ | |||
int firstBlockIndex = offset / _block_size; | |||
int firstBlockOffset = offset % _block_size; | |||
int lastBlockIndex = (offset + buffer.length - 1) / _block_size; | |||
if (firstBlockIndex == lastBlockIndex) | |||
{ | |||
System.arraycopy( | |||
(( SmallDocumentBlock ) blocks[ firstBlockIndex ])._data, | |||
firstBlockOffset, buffer, 0, buffer.length); | |||
} | |||
else | |||
{ | |||
int buffer_offset = 0; | |||
System.arraycopy( | |||
(( SmallDocumentBlock ) blocks[ firstBlockIndex ])._data, | |||
firstBlockOffset, buffer, buffer_offset, | |||
_block_size - firstBlockOffset); | |||
buffer_offset += _block_size - firstBlockOffset; | |||
for (int j = firstBlockIndex + 1; j < lastBlockIndex; j++) | |||
{ | |||
System.arraycopy((( SmallDocumentBlock ) blocks[ j ])._data, | |||
0, buffer, buffer_offset, _block_size); | |||
buffer_offset += _block_size; | |||
} | |||
System.arraycopy( | |||
(( SmallDocumentBlock ) blocks[ lastBlockIndex ])._data, 0, | |||
buffer, buffer_offset, buffer.length - buffer_offset); | |||
} | |||
public static DataInputBlock getDataInputBlock(SmallDocumentBlock[] blocks, int offset) { | |||
int firstBlockIndex = offset >> BLOCK_SHIFT; | |||
int firstBlockOffset= offset & BLOCK_MASK; | |||
return new DataInputBlock(blocks[firstBlockIndex]._data, firstBlockOffset); | |||
} | |||
/** |
@@ -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 |
@@ -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); | |||
} | |||
} |
@@ -28,13 +28,12 @@ import java.io.InputStream; | |||
*@author Marc Johnson (mjohnson at apache dot org) | |||
*@author Andrew Oliver (acoliver at apache dot org) | |||
*/ | |||
public final class LittleEndian implements LittleEndianConsts { | |||
public class LittleEndian implements LittleEndianConsts { | |||
private LittleEndian() { | |||
// no instances of this class | |||
// no instances of this class | |||
} | |||
/** | |||
* get a short value from a byte array | |||
* | |||
@@ -42,9 +41,10 @@ public final class LittleEndian implements LittleEndianConsts { | |||
*@param offset a starting offset into the byte array | |||
*@return the short (16-bit) value | |||
*/ | |||
public static short getShort(final byte[] data, final int offset) { | |||
return (short) getNumber(data, offset, SHORT_SIZE); | |||
public static short getShort(byte[] data, int offset) { | |||
int b0 = data[offset] & 0xFF; | |||
int b1 = data[offset+1] & 0xFF; | |||
return (short) ((b1 << 8) + (b0 << 0)); | |||
} | |||
@@ -55,73 +55,32 @@ public final class LittleEndian implements LittleEndianConsts { | |||
*@param offset a starting offset into the byte array | |||
*@return the unsigned short (16-bit) value in an integer | |||
*/ | |||
public static int getUShort(final byte[] data, final int offset) { | |||
short num = (short) getNumber(data, offset, SHORT_SIZE); | |||
int retNum; | |||
if (num < 0) { | |||
retNum = (Short.MAX_VALUE + 1) * 2 + num; | |||
} else { | |||
retNum = num; | |||
} | |||
return retNum; | |||
} | |||
/** | |||
* get a short array from a byte array. | |||
* | |||
*@param data Description of the Parameter | |||
*@param offset Description of the Parameter | |||
*@param size Description of the Parameter | |||
*@return The simpleShortArray value | |||
*/ | |||
public static short[] getSimpleShortArray(final byte[] data, final int offset, final int size) { | |||
short[] results = new short[size]; | |||
for (int i = 0; i < size; i++) { | |||
results[i] = getShort(data, offset + 2 + (i * 2)); | |||
} | |||
return results; | |||
} | |||
/** | |||
* get a short array from a byte array. The short array is assumed to start | |||
* with a word describing the length of the array. | |||
* | |||
*@param data Description of the Parameter | |||
*@param offset Description of the Parameter | |||
*@return The shortArray value | |||
*/ | |||
public static short[] getShortArray(final byte[] data, final int offset) { | |||
int size = (int) getNumber(data, offset, SHORT_SIZE); | |||
short[] results = getSimpleShortArray(data, offset, size); | |||
return results; | |||
public static int getUShort(byte[] data, int offset) { | |||
int b0 = data[offset] & 0xFF; | |||
int b1 = data[offset+1] & 0xFF; | |||
return (b1 << 8) + (b0 << 0); | |||
} | |||
/** | |||
* get a short value from the beginning of a byte array | |||
* | |||
*@param data the byte array | |||
*@return the short (16-bit) value | |||
*/ | |||
public static short getShort(final byte[] data) { | |||
public static short getShort(byte[] data) { | |||
return getShort(data, 0); | |||
} | |||
/** | |||
* get an unsigned short value from the beginning of a byte array | |||
* | |||
*@param data the byte array | |||
*@return the unsigned short (16-bit) value in an int | |||
*/ | |||
public static int getUShort(final byte[] data) { | |||
public static int getUShort(byte[] data) { | |||
return getUShort(data, 0); | |||
} | |||
/** | |||
* get an int value from a byte array | |||
* | |||
@@ -129,9 +88,13 @@ public final class LittleEndian implements LittleEndianConsts { | |||
*@param offset a starting offset into the byte array | |||
*@return the int (32-bit) value | |||
*/ | |||
public static int getInt(final byte[] data, final int offset) { | |||
return (int) getNumber(data, offset, INT_SIZE); | |||
public static int getInt(byte[] data, int offset) { | |||
int i=offset; | |||
int b0 = data[i++] & 0xFF; | |||
int b1 = data[i++] & 0xFF; | |||
int b2 = data[i++] & 0xFF; | |||
int b3 = data[i++] & 0xFF; | |||
return (b3 << 24) + (b2 << 16) + (b1 << 8) + (b0 << 0); | |||
} | |||
@@ -139,10 +102,9 @@ public final class LittleEndian implements LittleEndianConsts { | |||
* get an int value from the beginning of a byte array | |||
* | |||
*@param data the byte array | |||
*@return the int (32-bit) value | |||
*@return the int (32-bit) value | |||
*/ | |||
public static int getInt(final byte[] data) { | |||
public static int getInt(byte[] data) { | |||
return getInt(data, 0); | |||
} | |||
@@ -154,15 +116,9 @@ public final class LittleEndian implements LittleEndianConsts { | |||
*@param offset a starting offset into the byte array | |||
*@return the unsigned int (32-bit) value in a long | |||
*/ | |||
public static long getUInt(final byte[] data, final int offset) { | |||
int num = (int) getNumber(data, offset, INT_SIZE); | |||
long retNum; | |||
if (num < 0) { | |||
retNum = ((long) Integer.MAX_VALUE + 1) * 2 + num; | |||
} else { | |||
retNum = num; | |||
} | |||
return retNum; | |||
public static long getUInt(byte[] data, int offset) { | |||
long retNum = getInt(data, offset); | |||
return retNum & 0x00FFFFFFFF; | |||
} | |||
/** | |||
@@ -171,8 +127,8 @@ public final class LittleEndian implements LittleEndianConsts { | |||
*@param data the byte array | |||
*@return the unsigned int (32-bit) value in a long | |||
*/ | |||
public static long getUInt(final byte[] data) { | |||
return getUInt(data,0); | |||
public static long getUInt(byte[] data) { | |||
return getUInt(data,0); | |||
} | |||
/** | |||
@@ -182,24 +138,16 @@ public final class LittleEndian implements LittleEndianConsts { | |||
*@param offset a starting offset into the byte array | |||
*@return the long (64-bit) value | |||
*/ | |||
public static long getLong(final byte[] data, final int offset) { | |||
return getNumber(data, offset, LONG_SIZE); | |||
} | |||
/** | |||
* get a long value from the beginning of a byte array | |||
* | |||
*@param data the byte array | |||
*@return the long (64-bit) value | |||
*/ | |||
public static long getLong(final byte[] data) { | |||
return getLong(data, 0); | |||
public static long getLong(byte[] data, int offset) { | |||
long result = 0; | |||
for (int j = offset + LONG_SIZE - 1; j >= offset; j--) { | |||
result <<= 8; | |||
result |= 0xff & data[j]; | |||
} | |||
return result; | |||
} | |||
/** | |||
* get a double value from a byte array, reads it in little endian format | |||
* then converts the resulting revolting IEEE 754 (curse them) floating | |||
@@ -209,24 +157,10 @@ public final class LittleEndian implements LittleEndianConsts { | |||
*@param offset a starting offset into the byte array | |||
*@return the double (64-bit) value | |||
*/ | |||
public static double getDouble(final byte[] data, final int offset) { | |||
return Double.longBitsToDouble(getNumber(data, offset, DOUBLE_SIZE)); | |||
} | |||
/** | |||
* get a double value from the beginning of a byte array | |||
* | |||
*@param data the byte array | |||
*@return the double (64-bit) value | |||
*/ | |||
public static double getDouble(final byte[] data) { | |||
return getDouble(data, 0); | |||
public static double getDouble(byte[] data, int offset) { | |||
return Double.longBitsToDouble(getLong(data, offset)); | |||
} | |||
/** | |||
* put a short value into a byte array | |||
* | |||
@@ -234,9 +168,10 @@ public final class LittleEndian implements LittleEndianConsts { | |||
*@param offset a starting offset into the byte array | |||
*@param value the short (16-bit) value | |||
*/ | |||
public static void putShort(final byte[] data, final int offset, | |||
final short value) { | |||
putNumber(data, offset, value, SHORT_SIZE); | |||
public static void putShort(byte[] data, int offset, short value) { | |||
int i = offset; | |||
data[i++] = (byte)((value >>> 0) & 0xFF); | |||
data[i++] = (byte)((value >>> 8) & 0xFF); | |||
} | |||
/** | |||
@@ -247,7 +182,7 @@ public final class LittleEndian implements LittleEndianConsts { | |||
* Added for consistency with other put~() methods | |||
*/ | |||
public static void putByte(byte[] data, int offset, int value) { | |||
putNumber(data, offset, value, LittleEndianConsts.BYTE_SIZE); | |||
data[offset] = (byte) value; | |||
} | |||
/** | |||
@@ -259,10 +194,10 @@ public final class LittleEndian implements LittleEndianConsts { | |||
* | |||
* @exception ArrayIndexOutOfBoundsException may be thrown | |||
*/ | |||
public static void putUShort(final byte[] data, final int offset, | |||
final int value) | |||
{ | |||
putNumber(data, offset, value, SHORT_SIZE); | |||
public static void putUShort(byte[] data, int offset, int value) { | |||
int i = offset; | |||
data[i++] = (byte)((value >>> 0) & 0xFF); | |||
data[i++] = (byte)((value >>> 8) & 0xFF); | |||
} | |||
/** | |||
@@ -271,8 +206,7 @@ public final class LittleEndian implements LittleEndianConsts { | |||
*@param data the byte array | |||
*@param value the short (16-bit) value | |||
*/ | |||
public static void putShort(final byte[] data, final short value) { | |||
public static void putShort(byte[] data, short value) { | |||
putShort(data, 0, value); | |||
} | |||
@@ -284,10 +218,12 @@ public final class LittleEndian implements LittleEndianConsts { | |||
*@param offset a starting offset into the byte array | |||
*@param value the int (32-bit) value | |||
*/ | |||
public static void putInt(final byte[] data, final int offset, | |||
final int value) { | |||
putNumber(data, offset, value, INT_SIZE); | |||
public static void putInt(byte[] data, int offset, int value) { | |||
int i = offset; | |||
data[i++] = (byte)((value >>> 0) & 0xFF); | |||
data[i++] = (byte)((value >>> 8) & 0xFF); | |||
data[i++] = (byte)((value >>> 16) & 0xFF); | |||
data[i++] = (byte)((value >>> 24) & 0xFF); | |||
} | |||
@@ -297,8 +233,7 @@ public final class LittleEndian implements LittleEndianConsts { | |||
*@param data the byte array | |||
*@param value the int (32-bit) value | |||
*/ | |||
public static void putInt(final byte[] data, final int value) { | |||
public static void putInt(byte[] data, int value) { | |||
putInt(data, 0, value); | |||
} | |||
@@ -310,22 +245,14 @@ public final class LittleEndian implements LittleEndianConsts { | |||
*@param offset a starting offset into the byte array | |||
*@param value the long (64-bit) value | |||
*/ | |||
public static void putLong(final byte[] data, final int offset, | |||
final long value) { | |||
putNumber(data, offset, value, LONG_SIZE); | |||
} | |||
/** | |||
* put a long value into beginning of a byte array | |||
* | |||
*@param data the byte array | |||
*@param value the long (64-bit) value | |||
*/ | |||
public static void putLong(final byte[] data, final long value) { | |||
putLong(data, 0, value); | |||
public static void putLong(byte[] data, int offset, long value) { | |||
int limit = LONG_SIZE + offset; | |||
long v = value; | |||
for (int j = offset; j < limit; j++) { | |||
data[j] = (byte) (v & 0xFF); | |||
v >>= 8; | |||
} | |||
} | |||
@@ -336,26 +263,13 @@ public final class LittleEndian implements LittleEndianConsts { | |||
*@param offset a starting offset into the byte array | |||
*@param value the double (64-bit) value | |||
*/ | |||
public static void putDouble(final byte[] data, final int offset, | |||
final double value) { | |||
public static void putDouble(byte[] data, int offset, double value) { | |||
// Excel likes NaN to be a specific value. | |||
if (Double.isNaN(value)) | |||
putNumber(data, offset, -276939487313920L, DOUBLE_SIZE); | |||
else | |||
putNumber(data, offset, Double.doubleToLongBits(value), DOUBLE_SIZE); | |||
} | |||
/** | |||
* put a double value into beginning of a byte array | |||
* | |||
*@param data the byte array | |||
*@param value the double (64-bit) value | |||
*/ | |||
public static void putDouble(final byte[] data, final double value) { | |||
putDouble(data, 0, value); | |||
if (Double.isNaN(value)) { | |||
putLong(data, offset, -276939487313920L); | |||
} else { | |||
putLong(data, offset, Double.doubleToLongBits(value)); | |||
} | |||
} | |||
@@ -364,7 +278,6 @@ public final class LittleEndian implements LittleEndianConsts { | |||
* | |||
*@author Marc Johnson (mjohnson at apache dot org) | |||
*/ | |||
public static final class BufferUnderrunException extends IOException { | |||
BufferUnderrunException() { | |||
@@ -376,79 +289,72 @@ public final class LittleEndian implements LittleEndianConsts { | |||
/** | |||
* get a short value from an InputStream | |||
* | |||
*@param stream the InputStream from which the short | |||
* is to be read | |||
*@param stream the InputStream from which the short is to be read | |||
*@return the short (16-bit) value | |||
*@exception IOException will be propagated back to the caller | |||
*@exception BufferUnderrunException if the stream cannot provide enough | |||
* bytes | |||
*@exception BufferUnderrunException if the stream cannot provide enough bytes | |||
*/ | |||
public static short readShort(InputStream stream) throws IOException, BufferUnderrunException { | |||
return (short) readUShort(stream); | |||
} | |||
return (short) readUShort(stream); | |||
} | |||
public static int readUShort(InputStream stream) throws IOException, BufferUnderrunException { | |||
public static int readUShort(InputStream stream) throws IOException, BufferUnderrunException { | |||
int ch1 = stream.read(); | |||
int ch2 = stream.read(); | |||
if ((ch1 | ch2) < 0) { | |||
throw new BufferUnderrunException(); | |||
} | |||
return ((ch2 << 8) + (ch1 << 0)); | |||
} | |||
int ch1 = stream.read(); | |||
int ch2 = stream.read(); | |||
if ((ch1 | ch2) < 0) { | |||
throw new BufferUnderrunException(); | |||
} | |||
return (ch2 << 8) + (ch1 << 0); | |||
} | |||
/** | |||
* get an int value from an InputStream | |||
* | |||
*@param stream the InputStream from which the int is | |||
* to be read | |||
*@return the int (32-bit) value | |||
*@exception IOException will be propagated back to the caller | |||
*@exception BufferUnderrunException if the stream cannot provide enough | |||
* bytes | |||
*@param stream the InputStream from which the int is to be read | |||
* @return the int (32-bit) value | |||
* @exception IOException will be propagated back to the caller | |||
* @exception BufferUnderrunException if the stream cannot provide enough bytes | |||
*/ | |||
public static int readInt(final InputStream stream) | |||
public static int readInt(InputStream stream) | |||
throws IOException, BufferUnderrunException { | |||
int ch1 = stream.read(); | |||
int ch2 = stream.read(); | |||
int ch3 = stream.read(); | |||
int ch4 = stream.read(); | |||
if ((ch1 | ch2 | ch3 | ch4) < 0) { | |||
throw new BufferUnderrunException(); | |||
} | |||
return ((ch4 << 24) + (ch3<<16) + (ch2 << 8) + (ch1 << 0)); | |||
int ch1 = stream.read(); | |||
int ch2 = stream.read(); | |||
int ch3 = stream.read(); | |||
int ch4 = stream.read(); | |||
if ((ch1 | ch2 | ch3 | ch4) < 0) { | |||
throw new BufferUnderrunException(); | |||
} | |||
return (ch4 << 24) + (ch3<<16) + (ch2 << 8) + (ch1 << 0); | |||
} | |||
/** | |||
* get a long value from an InputStream | |||
* | |||
*@param stream the InputStream from which the long | |||
* is to be read | |||
*@return the long (64-bit) value | |||
*@exception IOException will be propagated back to the caller | |||
*@exception BufferUnderrunException if the stream cannot provide enough | |||
* bytes | |||
* @param stream the InputStream from which the long is to be read | |||
* @return the long (64-bit) value | |||
* @exception IOException will be propagated back to the caller | |||
* @exception BufferUnderrunException if the stream cannot provide enough bytes | |||
*/ | |||
public static long readLong(final InputStream stream) | |||
public static long readLong(InputStream stream) | |||
throws IOException, BufferUnderrunException { | |||
int ch1 = stream.read(); | |||
int ch2 = stream.read(); | |||
int ch3 = stream.read(); | |||
int ch4 = stream.read(); | |||
int ch5 = stream.read(); | |||
int ch6 = stream.read(); | |||
int ch7 = stream.read(); | |||
int ch8 = stream.read(); | |||
if ((ch1 | ch2 | ch3 | ch4 | ch5 | ch6 | ch7 | ch8) < 0) { | |||
throw new BufferUnderrunException(); | |||
} | |||
return | |||
((long)ch8 << 56) + | |||
int ch1 = stream.read(); | |||
int ch2 = stream.read(); | |||
int ch3 = stream.read(); | |||
int ch4 = stream.read(); | |||
int ch5 = stream.read(); | |||
int ch6 = stream.read(); | |||
int ch7 = stream.read(); | |||
int ch8 = stream.read(); | |||
if ((ch1 | ch2 | ch3 | ch4 | ch5 | ch6 | ch7 | ch8) < 0) { | |||
throw new BufferUnderrunException(); | |||
} | |||
return | |||
((long)ch8 << 56) + | |||
((long)ch7 << 48) + | |||
((long)ch6 << 40) + | |||
((long)ch5 << 32) + | |||
@@ -458,114 +364,44 @@ public final class LittleEndian implements LittleEndianConsts { | |||
(ch1 << 0); | |||
} | |||
/** | |||
* Gets the number attribute of the LittleEndian class | |||
* | |||
*@param data Description of the Parameter | |||
*@param offset Description of the Parameter | |||
*@param size Description of the Parameter | |||
*@return The number value | |||
*/ | |||
private static long getNumber(final byte[] data, final int offset, | |||
final int size) { | |||
long result = 0; | |||
for (int j = offset + size - 1; j >= offset; j--) { | |||
result <<= 8; | |||
result |= 0xff & data[j]; | |||
} | |||
return result; | |||
} | |||
/** | |||
* Description of the Method | |||
* | |||
*@param data Description of the Parameter | |||
*@param offset Description of the Parameter | |||
*@param value Description of the Parameter | |||
*@param size Description of the Parameter | |||
*/ | |||
private static void putNumber(final byte[] data, final int offset, | |||
final long value, final int size) { | |||
int limit = size + offset; | |||
long v = value; | |||
for (int j = offset; j < limit; j++) { | |||
data[j] = (byte) (v & 0xFF); | |||
v >>= 8; | |||
} | |||
} | |||
/** | |||
* Convert an 'unsigned' byte to an integer. ie, don't carry across the | |||
* sign. | |||
* | |||
*@param b Description of the Parameter | |||
*@return Description of the Return Value | |||
* @param b Description of the Parameter | |||
* @return Description of the Return Value | |||
*/ | |||
public static int ubyteToInt(byte b) { | |||
return ((b & 0x80) == 0 ? (int) b : (b & (byte) 0x7f) + 0x80); | |||
return b & 0xFF; | |||
} | |||
/** | |||
* get the unsigned value of a byte. | |||
* | |||
*@param data the byte array. | |||
*@param offset a starting offset into the byte array. | |||
*@return the unsigned value of the byte as a 32 bit integer | |||
* @param data the byte array. | |||
* @param offset a starting offset into the byte array. | |||
* @return the unsigned value of the byte as a 32 bit integer | |||
*/ | |||
public static int getUnsignedByte(final byte[] data, final int offset) { | |||
return (int) getNumber(data, offset, BYTE_SIZE); | |||
} | |||
/** | |||
* get the unsigned value of a byte. | |||
* | |||
*@param data the byte array | |||
*@return the unsigned value of the byte as a 32 bit integer | |||
*/ | |||
public static int getUnsignedByte(final byte[] data) { | |||
return getUnsignedByte(data, 0); | |||
public static int getUnsignedByte(byte[] data, int offset) { | |||
return data[offset] & 0xFF; | |||
} | |||
/** | |||
* Copy a portion of a byte array | |||
* | |||
*@param data the original byte array | |||
*@param offset Where to start copying from. | |||
*@param size Number of bytes to copy. | |||
*@return The byteArray value | |||
*@throws IndexOutOfBoundsException - if copying would cause access of | |||
* @param data the original byte array | |||
* @param offset Where to start copying from. | |||
* @param size Number of bytes to copy. | |||
* @return The byteArray value | |||
* @throws IndexOutOfBoundsException - if copying would cause access of | |||
* data outside array bounds. | |||
*/ | |||
public static byte[] getByteArray(final byte[] data, int offset, int size) { | |||
public static byte[] getByteArray(byte[] data, int offset, int size) { | |||
byte[] copy = new byte[size]; | |||
System.arraycopy(data, offset, copy, 0, size); | |||
return copy; | |||
} | |||
/** | |||
* <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; | |||
} | |||
} |
@@ -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()); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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 | |||
@@ -220,6 +218,14 @@ public class StringUtil { | |||
} | |||
out.write(bytes); | |||
} | |||
public static String readUnicodeLE(LittleEndianInput in, int nChars) { | |||
char[] buf = new char[nChars]; | |||
for (int i = 0; i < buf.length; i++) { | |||
buf[i] = (char) in.readUShort(); | |||
} | |||
return new String(buf); | |||
} | |||
/** | |||
* Apply printf() like formatting to a string. |
@@ -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); |
@@ -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()); |
@@ -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) { |
@@ -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); | |||
@@ -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); |
@@ -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); | |||
@@ -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" | |||
} | |||
} |
@@ -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()); |
@@ -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()); | |||
} |
@@ -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) { |
@@ -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++) { |
@@ -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); |
@@ -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 { |
@@ -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() ); |
@@ -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); |
@@ -1,4 +1,3 @@ | |||
/* ==================================================================== | |||
Licensed to the Apache Software Foundation (ASF) under one or more | |||
contributor license agreements. See the NOTICE file distributed with | |||
@@ -15,18 +14,16 @@ | |||
See the License for the specific language governing permissions and | |||
limitations under the License. | |||
==================================================================== */ | |||
package org.apache.poi.poifs.filesystem; | |||
import java.io.*; | |||
import java.util.*; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.IOException; | |||
import java.util.Arrays; | |||
import junit.framework.*; | |||
import junit.framework.TestCase; | |||
import org.apache.poi.poifs.property.DirectoryProperty; | |||
import org.apache.poi.poifs.property.DocumentProperty; | |||
import org.apache.poi.poifs.storage.RawDataBlock; | |||
/** | |||
@@ -35,22 +32,9 @@ import org.apache.poi.poifs.storage.RawDataBlock; | |||
* @author Marc Johnson | |||
*/ | |||
public class TestDocumentInputStream | |||
extends TestCase | |||
{ | |||
public final class TestDocumentInputStream extends TestCase { | |||
/** | |||
* Constructor TestDocumentInputStream | |||
* | |||
* @param name | |||
* | |||
* @exception IOException | |||
*/ | |||
public TestDocumentInputStream(String name) | |||
throws IOException | |||
{ | |||
super(name); | |||
protected void setUp() throws Exception { | |||
int blocks = (_workbook_size + 511) / 512; | |||
_workbook_data = new byte[ 512 * blocks ]; | |||
@@ -86,13 +70,8 @@ public class TestDocumentInputStream | |||
/** | |||
* test constructor | |||
* | |||
* @exception IOException | |||
*/ | |||
public void testConstructor() | |||
throws IOException | |||
{ | |||
public void testConstructor() throws IOException { | |||
DocumentInputStream stream = new DocumentInputStream(_workbook); | |||
assertEquals(_workbook_size, stream.available()); | |||
@@ -100,13 +79,8 @@ public class TestDocumentInputStream | |||
/** | |||
* test available() behavior | |||
* | |||
* @exception IOException | |||
*/ | |||
public void testAvailable() | |||
throws IOException | |||
{ | |||
public void testAvailable() throws IOException { | |||
DocumentInputStream stream = new DocumentInputStream(_workbook); | |||
assertEquals(_workbook_size, stream.available()); | |||
@@ -115,9 +89,7 @@ public class TestDocumentInputStream | |||
{ | |||
stream.available(); | |||
fail("Should have caught IOException"); | |||
} | |||
catch (IOException ignored) | |||
{ | |||
} catch (IllegalStateException ignored) { | |||
// as expected | |||
} | |||
@@ -125,13 +97,8 @@ public class TestDocumentInputStream | |||
/** | |||
* test mark/reset/markSupported. | |||
* | |||
* @exception IOException | |||
*/ | |||
public void testMarkFunctions() | |||
throws IOException | |||
{ | |||
public void testMarkFunctions() throws IOException { | |||
DocumentInputStream stream = new DocumentInputStream(_workbook); | |||
byte[] buffer = new byte[ _workbook_size / 5 ]; | |||
@@ -169,13 +136,8 @@ public class TestDocumentInputStream | |||
/** | |||
* test simple read method | |||
* | |||
* @exception IOException | |||
*/ | |||
public void testReadSingleByte() | |||
throws IOException | |||
{ | |||
public void testReadSingleByte() throws IOException { | |||
DocumentInputStream stream = new DocumentInputStream(_workbook); | |||
int remaining = _workbook_size; | |||
@@ -205,13 +167,8 @@ public class TestDocumentInputStream | |||
/** | |||
* Test buffered read | |||
* | |||
* @exception IOException | |||
*/ | |||
public void testBufferRead() | |||
throws IOException | |||
{ | |||
public void testBufferRead() throws IOException { | |||
DocumentInputStream stream = new DocumentInputStream(_workbook); | |||
try | |||
@@ -275,23 +232,14 @@ public class TestDocumentInputStream | |||
/** | |||
* Test complex buffered read | |||
* | |||
* @exception IOException | |||
*/ | |||
public void testComplexBufferRead() | |||
throws IOException | |||
{ | |||
public void testComplexBufferRead() throws IOException { | |||
DocumentInputStream stream = new DocumentInputStream(_workbook); | |||
try | |||
{ | |||
try { | |||
stream.read(null, 0, 1); | |||
fail("Should have caught NullPointerException"); | |||
} | |||
catch (NullPointerException ignored) | |||
{ | |||
} catch (IllegalArgumentException ignored) { | |||
// as expected | |||
} | |||
@@ -391,13 +339,8 @@ public class TestDocumentInputStream | |||
/** | |||
* test skip | |||
* | |||
* @exception IOException | |||
*/ | |||
public void testSkip() | |||
throws IOException | |||
{ | |||
public void testSkip() throws IOException { | |||
DocumentInputStream stream = new DocumentInputStream(_workbook); | |||
assertEquals(_workbook_size, stream.available()); | |||
@@ -422,17 +365,4 @@ public class TestDocumentInputStream | |||
stream.skip(2 + ( long ) Integer.MAX_VALUE)); | |||
assertEquals(0, stream.available()); | |||
} | |||
/** | |||
* main method to run the unit tests | |||
* | |||
* @param ignored_args | |||
*/ | |||
public static void main(String [] ignored_args) | |||
{ | |||
System.out.println( | |||
"Testing org.apache.poi.poifs.filesystem.DocumentInputStream"); | |||
junit.textui.TestRunner.run(TestDocumentInputStream.class); | |||
} | |||
} |
@@ -26,22 +26,22 @@ import junit.framework.TestSuite; | |||
*/ | |||
public final class AllPOIFSStorageTests { | |||
public static Test suite() { | |||
TestSuite result = new TestSuite("Tests for org.apache.poi.poifs.storage"); | |||
result.addTestSuite(TestBATBlock.class); | |||
result.addTestSuite(TestBlockAllocationTableReader.class); | |||
result.addTestSuite(TestBlockAllocationTableWriter.class); | |||
result.addTestSuite(TestBlockListImpl.class); | |||
result.addTestSuite(TestDocumentBlock.class); | |||
result.addTestSuite(TestHeaderBlockReader.class); | |||
result.addTestSuite(TestHeaderBlockWriter.class); | |||
result.addTestSuite(TestPropertyBlock.class); | |||
result.addTestSuite(TestRawDataBlock.class); | |||
result.addTestSuite(TestRawDataBlockList.class); | |||
result.addTestSuite(TestSmallBlockTableReader.class); | |||
result.addTestSuite(TestSmallBlockTableWriter.class); | |||
result.addTestSuite(TestSmallDocumentBlock.class); | |||
result.addTestSuite(TestSmallDocumentBlockList.class); | |||
return result; | |||
} | |||
public static Test suite() { | |||
TestSuite result = new TestSuite(AllPOIFSStorageTests.class.getName()); | |||
result.addTestSuite(TestBATBlock.class); | |||
result.addTestSuite(TestBlockAllocationTableReader.class); | |||
result.addTestSuite(TestBlockAllocationTableWriter.class); | |||
result.addTestSuite(TestBlockListImpl.class); | |||
result.addTestSuite(TestDocumentBlock.class); | |||
result.addTestSuite(TestHeaderBlockReader.class); | |||
result.addTestSuite(TestHeaderBlockWriter.class); | |||
result.addTestSuite(TestPropertyBlock.class); | |||
result.addTestSuite(TestRawDataBlock.class); | |||
result.addTestSuite(TestRawDataBlockList.class); | |||
result.addTestSuite(TestSmallBlockTableReader.class); | |||
result.addTestSuite(TestSmallBlockTableWriter.class); | |||
result.addTestSuite(TestSmallDocumentBlock.class); | |||
result.addTestSuite(TestSmallDocumentBlockList.class); | |||
return result; | |||
} | |||
} |
@@ -1,4 +1,3 @@ | |||
/* ==================================================================== | |||
Licensed to the Apache Software Foundation (ASF) under one or more | |||
contributor license agreements. See the NOTICE file distributed with | |||
@@ -15,25 +14,21 @@ | |||
See the License for the specific language governing permissions and | |||
limitations under the License. | |||
==================================================================== */ | |||
package org.apache.poi.poifs.storage; | |||
import java.io.*; | |||
import java.util.*; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.IOException; | |||
import junit.framework.*; | |||
import junit.framework.TestCase; | |||
/** | |||
* Class to test DocumentBlock functionality | |||
* | |||
* @author Marc Johnson | |||
*/ | |||
public class TestDocumentBlock | |||
extends TestCase | |||
{ | |||
public final class TestDocumentBlock extends TestCase { | |||
static final private byte[] _testdata; | |||
static | |||
@@ -44,25 +39,10 @@ public class TestDocumentBlock | |||
_testdata[ j ] = ( byte ) j; | |||
} | |||
} | |||
; | |||
/** | |||
* Constructor TestDocumentBlock | |||
* | |||
* @param name | |||
*/ | |||
public TestDocumentBlock(String name) | |||
{ | |||
super(name); | |||
} | |||
/** | |||
* Test the writing DocumentBlock constructor. | |||
* | |||
* @exception IOException | |||
*/ | |||
public void testConstructor() | |||
throws IOException | |||
{ | |||
@@ -88,46 +68,10 @@ public class TestDocumentBlock | |||
assertEquals(_testdata.length, size); | |||
} | |||
/** | |||
* test static read method | |||
* | |||
* @exception IOException | |||
*/ | |||
public void testRead() | |||
throws IOException | |||
{ | |||
DocumentBlock[] blocks = new DocumentBlock[ 4 ]; | |||
ByteArrayInputStream input = new ByteArrayInputStream(_testdata); | |||
for (int j = 0; j < 4; j++) | |||
{ | |||
blocks[ j ] = new DocumentBlock(input); | |||
} | |||
for (int j = 1; j <= 2000; j += 17) | |||
{ | |||
byte[] buffer = new byte[ j ]; | |||
int offset = 0; | |||
for (int k = 0; k < (2000 / j); k++) | |||
{ | |||
DocumentBlock.read(blocks, buffer, offset); | |||
for (int n = 0; n < buffer.length; n++) | |||
{ | |||
assertEquals("checking byte " + (k * j) + n, | |||
_testdata[ (k * j) + n ], buffer[ n ]); | |||
} | |||
offset += j; | |||
} | |||
} | |||
} | |||
/** | |||
* Test 'reading' constructor | |||
* | |||
* @exception IOException | |||
*/ | |||
public void testReadingConstructor() | |||
throws IOException | |||
{ | |||
@@ -164,17 +108,4 @@ public class TestDocumentBlock | |||
assertEquals(( byte ) 0xFF, copy[ j ]); | |||
} | |||
} | |||
/** | |||
* main method to run the unit tests | |||
* | |||
* @param ignored_args | |||
*/ | |||
public static void main(String [] ignored_args) | |||
{ | |||
System.out | |||
.println("Testing org.apache.poi.poifs.storage.DocumentBlock"); | |||
junit.textui.TestRunner.run(TestDocumentBlock.class); | |||
} | |||
} |
@@ -1,4 +1,3 @@ | |||
/* ==================================================================== | |||
Licensed to the Apache Software Foundation (ASF) under one or more | |||
contributor license agreements. See the NOTICE file distributed with | |||
@@ -15,25 +14,24 @@ | |||
See the License for the specific language governing permissions and | |||
limitations under the License. | |||
==================================================================== */ | |||
package org.apache.poi.poifs.storage; | |||
import java.io.*; | |||
import java.util.*; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.IOException; | |||
import java.util.ArrayList; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import junit.framework.*; | |||
import junit.framework.TestCase; | |||
/** | |||
* Class to test SmallDocumentBlock functionality | |||
* | |||
* @author Marc Johnson | |||
*/ | |||
public class TestSmallDocumentBlock | |||
extends TestCase | |||
{ | |||
public final class TestSmallDocumentBlock extends TestCase { | |||
static final private byte[] _testdata; | |||
static final private int _testdata_size = 2999; | |||
@@ -45,25 +43,10 @@ public class TestSmallDocumentBlock | |||
_testdata[ j ] = ( byte ) j; | |||
} | |||
} | |||
; | |||
/** | |||
* constructor | |||
* | |||
* @param name | |||
*/ | |||
public TestSmallDocumentBlock(String name) | |||
{ | |||
super(name); | |||
} | |||
/** | |||
* Test conversion from DocumentBlocks | |||
* | |||
* @exception IOException | |||
*/ | |||
public void testConvert1() | |||
throws IOException | |||
{ | |||
@@ -113,12 +96,7 @@ public class TestSmallDocumentBlock | |||
/** | |||
* Test conversion from byte array | |||
* | |||
* @exception IOException; | |||
* | |||
* @exception IOException | |||
*/ | |||
public void testConvert2() | |||
throws IOException | |||
{ | |||
@@ -154,57 +132,9 @@ public class TestSmallDocumentBlock | |||
} | |||
} | |||
/** | |||
* Test read method | |||
* | |||
* @exception IOException | |||
*/ | |||
public void testRead() | |||
throws IOException | |||
{ | |||
ByteArrayInputStream stream = new ByteArrayInputStream(_testdata); | |||
List documents = new ArrayList(); | |||
while (true) | |||
{ | |||
DocumentBlock block = new DocumentBlock(stream); | |||
documents.add(block); | |||
if (block.partiallyRead()) | |||
{ | |||
break; | |||
} | |||
} | |||
SmallDocumentBlock[] blocks = | |||
SmallDocumentBlock | |||
.convert(( BlockWritable [] ) documents | |||
.toArray(new DocumentBlock[ 0 ]), _testdata_size); | |||
for (int j = 1; j <= _testdata_size; j += 38) | |||
{ | |||
byte[] buffer = new byte[ j ]; | |||
int offset = 0; | |||
for (int k = 0; k < (_testdata_size / j); k++) | |||
{ | |||
SmallDocumentBlock.read(blocks, buffer, offset); | |||
for (int n = 0; n < buffer.length; n++) | |||
{ | |||
assertEquals("checking byte " + (k * j) + n, | |||
_testdata[ (k * j) + n ], buffer[ n ]); | |||
} | |||
offset += j; | |||
} | |||
} | |||
} | |||
/** | |||
* test fill | |||
* | |||
* @exception IOException | |||
*/ | |||
public void testFill() | |||
throws IOException | |||
{ | |||
@@ -294,17 +224,4 @@ public class TestSmallDocumentBlock | |||
} | |||
} | |||
} | |||
/** | |||
* main method to run the unit tests | |||
* | |||
* @param ignored_args | |||
*/ | |||
public static void main(String [] ignored_args) | |||
{ | |||
System.out.println( | |||
"Testing org.apache.poi.poifs.storage.SmallDocumentBlock"); | |||
junit.textui.TestRunner.run(TestSmallDocumentBlock.class); | |||
} | |||
} |
@@ -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); |
@@ -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 | |||
} | |||
} | |||
} |
@@ -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)); |
@@ -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 |