Better bounds checking in RecordInputStream. Removed rarely used methods readShortArray and putShortArray
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@701747 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
4bf266b1c7
commit
9897317e8e
@ -33,7 +33,7 @@ public class RecordInputStream extends InputStream {
|
||||
/** 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;
|
||||
|
||||
|
||||
private InputStream in;
|
||||
protected short currentSid;
|
||||
protected short currentLength = -1;
|
||||
@ -42,34 +42,34 @@ public class RecordInputStream extends InputStream {
|
||||
protected byte[] data = new byte[MAX_RECORD_DATA_SIZE];
|
||||
protected short recordOffset;
|
||||
protected long pos;
|
||||
|
||||
|
||||
private boolean autoContinue = true;
|
||||
|
||||
public RecordInputStream(InputStream in) throws RecordFormatException {
|
||||
public RecordInputStream(InputStream in) throws RecordFormatException {
|
||||
this.in = in;
|
||||
try {
|
||||
nextSid = LittleEndian.readShort(in);
|
||||
//Dont increment the pos just yet (technically we are at the start of
|
||||
//the record stream until nextRecord is called).
|
||||
//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();
|
||||
|
||||
byte result = data[recordOffset];
|
||||
recordOffset += 1;
|
||||
pos += 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
/** 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;
|
||||
}
|
||||
|
||||
public short getSid() {
|
||||
return currentSid;
|
||||
}
|
||||
|
||||
|
||||
public short getLength() {
|
||||
return currentLength;
|
||||
}
|
||||
@ -85,12 +85,11 @@ public class RecordInputStream extends InputStream {
|
||||
public boolean hasNextRecord() {
|
||||
return nextSid != INVALID_SID_VALUE;
|
||||
}
|
||||
|
||||
|
||||
/** 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));
|
||||
@ -100,7 +99,7 @@ public class RecordInputStream extends InputStream {
|
||||
autoContinue = true;
|
||||
try {
|
||||
recordOffset = 0;
|
||||
currentLength = LittleEndian.readShort(in);
|
||||
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;
|
||||
@ -113,138 +112,124 @@ public class RecordInputStream extends InputStream {
|
||||
// ex45582-22397.xls has one extra byte after the last record
|
||||
// Excel reads that file OK
|
||||
}
|
||||
nextSid = INVALID_SID_VALUE;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setAutoContinue(boolean enable) {
|
||||
this.autoContinue = enable;
|
||||
this.autoContinue = enable;
|
||||
}
|
||||
|
||||
|
||||
public boolean getAutoContinue() {
|
||||
return autoContinue;
|
||||
}
|
||||
|
||||
protected void checkRecordPosition() {
|
||||
if (remaining() <= 0) {
|
||||
if (isContinueNext() && autoContinue) {
|
||||
nextRecord();
|
||||
}
|
||||
else throw new ArrayIndexOutOfBoundsException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an 8 bit, signed value
|
||||
*/
|
||||
public byte readByte() {
|
||||
checkRecordPosition();
|
||||
|
||||
byte result = data[recordOffset];
|
||||
recordOffset += 1;
|
||||
pos += 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a 16 bit, signed value
|
||||
*/
|
||||
public short readShort() {
|
||||
checkRecordPosition();
|
||||
|
||||
short result = LittleEndian.getShort(data, recordOffset);
|
||||
recordOffset += LittleEndian.SHORT_SIZE;
|
||||
pos += LittleEndian.SHORT_SIZE;
|
||||
return result;
|
||||
}
|
||||
|
||||
public int readInt() {
|
||||
checkRecordPosition();
|
||||
|
||||
int result = LittleEndian.getInt(data, recordOffset);
|
||||
recordOffset += LittleEndian.INT_SIZE;
|
||||
pos += LittleEndian.INT_SIZE;
|
||||
return result;
|
||||
}
|
||||
private void checkRecordPosition(int requiredByteCount) {
|
||||
|
||||
public long readLong() {
|
||||
checkRecordPosition();
|
||||
|
||||
long result = LittleEndian.getLong(data, recordOffset);
|
||||
recordOffset += LittleEndian.LONG_SIZE;
|
||||
pos += LittleEndian.LONG_SIZE;
|
||||
return result;
|
||||
}
|
||||
if (remaining() < requiredByteCount) {
|
||||
if (isContinueNext() && autoContinue) {
|
||||
nextRecord();
|
||||
} else {
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an 8 bit, signed value
|
||||
*/
|
||||
public byte readByte() {
|
||||
checkRecordPosition(LittleEndian.BYTE_SIZE);
|
||||
|
||||
byte result = data[recordOffset];
|
||||
recordOffset += LittleEndian.BYTE_SIZE;
|
||||
pos += LittleEndian.BYTE_SIZE;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a 16 bit, signed value
|
||||
*/
|
||||
public short readShort() {
|
||||
checkRecordPosition(LittleEndian.SHORT_SIZE);
|
||||
|
||||
short result = LittleEndian.getShort(data, recordOffset);
|
||||
recordOffset += LittleEndian.SHORT_SIZE;
|
||||
pos += LittleEndian.SHORT_SIZE;
|
||||
return result;
|
||||
}
|
||||
|
||||
public int readInt() {
|
||||
checkRecordPosition(LittleEndian.INT_SIZE);
|
||||
|
||||
int result = LittleEndian.getInt(data, recordOffset);
|
||||
recordOffset += LittleEndian.INT_SIZE;
|
||||
pos += LittleEndian.INT_SIZE;
|
||||
return result;
|
||||
}
|
||||
|
||||
public long readLong() {
|
||||
checkRecordPosition(LittleEndian.LONG_SIZE);
|
||||
|
||||
long result = LittleEndian.getLong(data, recordOffset);
|
||||
recordOffset += LittleEndian.LONG_SIZE;
|
||||
pos += LittleEndian.LONG_SIZE;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an 8 bit, unsigned value
|
||||
*/
|
||||
public short readUByte() {
|
||||
return (short) (readByte() & 0x00FF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a 16 bit, unsigned value.
|
||||
* @return
|
||||
*/
|
||||
public int readUShort() {
|
||||
checkRecordPosition(LittleEndian.SHORT_SIZE);
|
||||
|
||||
int result = LittleEndian.getUShort(data, recordOffset);
|
||||
recordOffset += LittleEndian.SHORT_SIZE;
|
||||
pos += LittleEndian.SHORT_SIZE;
|
||||
return result;
|
||||
}
|
||||
|
||||
public double readDouble() {
|
||||
checkRecordPosition(LittleEndian.DOUBLE_SIZE);
|
||||
long valueLongBits = LittleEndian.getLong(data, recordOffset);
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an 8 bit, unsigned value
|
||||
*/
|
||||
public short readUByte() {
|
||||
short s = readByte();
|
||||
if(s < 0) {
|
||||
s += 256;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a 16 bit,un- signed value.
|
||||
* @return
|
||||
*/
|
||||
public int readUShort() {
|
||||
checkRecordPosition();
|
||||
|
||||
int result = LittleEndian.getUShort(data, recordOffset);
|
||||
recordOffset += LittleEndian.SHORT_SIZE;
|
||||
pos += LittleEndian.SHORT_SIZE;
|
||||
return result;
|
||||
}
|
||||
|
||||
public double readDouble() {
|
||||
checkRecordPosition();
|
||||
long valueLongBits = LittleEndian.getLong(data, recordOffset);
|
||||
double result = Double.longBitsToDouble(valueLongBits);
|
||||
if (Double.isNaN(result)) {
|
||||
throw new RuntimeException("Did not expect to read NaN");
|
||||
}
|
||||
recordOffset += LittleEndian.DOUBLE_SIZE;
|
||||
pos += LittleEndian.DOUBLE_SIZE;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public short[] readShortArray() {
|
||||
checkRecordPosition();
|
||||
|
||||
short[] arr = LittleEndian.getShortArray(data, recordOffset);
|
||||
final int size = (2 * (arr.length +1));
|
||||
recordOffset += size;
|
||||
pos += size;
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* given a byte array of 16-bit unicode characters, compress to 8-bit and
|
||||
* return a string
|
||||
*
|
||||
* { 0x16, 0x00 } -0x16
|
||||
*
|
||||
* given a byte array of 16-bit unicode characters, compress to 8-bit and
|
||||
* return a string
|
||||
*
|
||||
* { 0x16, 0x00 } -0x16
|
||||
*
|
||||
* @param length the length of the final string
|
||||
* @return the converted string
|
||||
* @exception IllegalArgumentException if len is too large (i.e.,
|
||||
* there is not enough data in string to create a String of that
|
||||
* length)
|
||||
*/
|
||||
* there is not enough data in string to create a String of that
|
||||
* length)
|
||||
*/
|
||||
public String readUnicodeLEString(int length) {
|
||||
if ((length < 0) || (((remaining() / 2) < length) && !isContinueNext())) {
|
||||
throw new IllegalArgumentException("Illegal length - asked for " + length + " but only " + (remaining()/2) + " left!");
|
||||
@ -258,11 +243,11 @@ public class RecordInputStream extends InputStream {
|
||||
if(compressByte != 1) throw new IllegalArgumentException("compressByte in continue records must be 1 while reading unicode LE string");
|
||||
}
|
||||
char ch = (char)readShort();
|
||||
buf.append(ch);
|
||||
buf.append(ch);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
|
||||
public String readCompressedUnicode(int length) {
|
||||
if ((length < 0) || ((remaining() < length) && !isContinueNext())) {
|
||||
throw new IllegalArgumentException("Illegal length " + length);
|
||||
@ -277,23 +262,23 @@ public class RecordInputStream extends InputStream {
|
||||
}
|
||||
byte b = readByte();
|
||||
char ch = (char)(0x00FF & b); // avoid sex
|
||||
buf.append(ch);
|
||||
buf.append(ch);
|
||||
}
|
||||
return buf.toString();
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
|
||||
/** Returns an excel style unicode string from the bytes reminaing in the record.
|
||||
* <i>Note:</i> Unicode strings differ from <b>normal</b> strings due to the addition of
|
||||
* formatting information.
|
||||
*
|
||||
*
|
||||
* @return The unicode string representation of the remaining bytes.
|
||||
*/
|
||||
public UnicodeString readUnicodeString() {
|
||||
return new UnicodeString(this);
|
||||
}
|
||||
|
||||
|
||||
/** Returns the remaining bytes for the current record.
|
||||
*
|
||||
*
|
||||
* @return The remaining bytes of the current record.
|
||||
*/
|
||||
public byte[] readRemainder() {
|
||||
@ -304,39 +289,39 @@ public class RecordInputStream extends InputStream {
|
||||
pos += size;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/** Reads all byte data for the current record, including any
|
||||
* that overlaps into any following continue records.
|
||||
*
|
||||
*
|
||||
* @deprecated Best to write a input stream that wraps this one where there is
|
||||
* special sub record that may overlap continue records.
|
||||
*/
|
||||
*/
|
||||
public byte[] readAllContinuedRemainder() {
|
||||
//Using a ByteArrayOutputStream is just an easy way to get a
|
||||
//growable array of the data.
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(2*MAX_RECORD_DATA_SIZE);
|
||||
|
||||
while (isContinueNext()) {
|
||||
byte[] b = readRemainder();
|
||||
byte[] b = readRemainder();
|
||||
out.write(b, 0, b.length);
|
||||
nextRecord();
|
||||
}
|
||||
byte[] b = readRemainder();
|
||||
out.write(b, 0, b.length);
|
||||
|
||||
byte[] b = readRemainder();
|
||||
out.write(b, 0, b.length);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/** Returns true iif a Continue record is next in the excel stream
|
||||
*
|
||||
/** Returns true iif a Continue record is next in the excel stream
|
||||
*
|
||||
* @return True when a ContinueRecord is next.
|
||||
*/
|
||||
public boolean isContinueNext() {
|
||||
|
@ -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,66 +14,66 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
|
||||
package org.apache.poi.hssf.record;
|
||||
|
||||
|
||||
|
||||
import org.apache.poi.util.*;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
|
||||
/**
|
||||
* The series list record defines the series displayed as an overlay to the main chart record.
|
||||
* NOTE: This source is automatically generated please do not modify this file. Either subclass or
|
||||
* remove the record in src/records/definitions.
|
||||
|
||||
*
|
||||
* The series list record defines the series displayed as an overlay to the main chart record.<br/>
|
||||
* TODO - does this record (0x1016) really exist. It doesn't seem to be referenced in either the OOO or MS doc
|
||||
*
|
||||
* @author Glen Stampoultzis (glens at apache.org)
|
||||
*/
|
||||
public class SeriesListRecord
|
||||
extends Record
|
||||
{
|
||||
public final static short sid = 0x1016;
|
||||
public final class SeriesListRecord extends Record {
|
||||
public final static short sid = 0x1016;
|
||||
private short[] field_1_seriesNumbers;
|
||||
|
||||
|
||||
public SeriesListRecord()
|
||||
{
|
||||
|
||||
public SeriesListRecord(short[] seriesNumbers) {
|
||||
field_1_seriesNumbers = seriesNumbers;
|
||||
}
|
||||
|
||||
public SeriesListRecord(RecordInputStream in)
|
||||
{
|
||||
field_1_seriesNumbers = in.readShortArray();
|
||||
public SeriesListRecord(RecordInputStream in) {
|
||||
int nItems = in.readUShort();
|
||||
short[] ss = new short[nItems];
|
||||
for (int i = 0; i < nItems; i++) {
|
||||
ss[i] = in.readShort();
|
||||
|
||||
}
|
||||
field_1_seriesNumbers = ss;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
public String toString() {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
|
||||
buffer.append("[SERIESLIST]\n");
|
||||
buffer.append(" .seriesNumbers = ")
|
||||
.append(" (").append( getSeriesNumbers() ).append(" )");
|
||||
buffer.append(System.getProperty("line.separator"));
|
||||
buffer.append(" .seriesNumbers= ").append(" (").append( getSeriesNumbers() ).append(" )");
|
||||
buffer.append("\n");
|
||||
|
||||
buffer.append("[/SERIESLIST]\n");
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
public int serialize(int offset, byte[] data)
|
||||
{
|
||||
int pos = 0;
|
||||
public int serialize(int offset, byte[] data) {
|
||||
|
||||
LittleEndian.putShort(data, 0 + offset, sid);
|
||||
LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize() - 4));
|
||||
int nItems = field_1_seriesNumbers.length;
|
||||
int dataSize = 2 + 2 * nItems;
|
||||
|
||||
LittleEndian.putUShort(data, 0 + offset, sid);
|
||||
LittleEndian.putUShort(data, 2 + offset, dataSize);
|
||||
|
||||
LittleEndian.putShortArray(data, 4 + offset + pos, field_1_seriesNumbers);
|
||||
LittleEndian.putUShort(data, 4 + offset, nItems);
|
||||
|
||||
int pos = offset + 6;
|
||||
for (int i = 0; i < nItems; i++) {
|
||||
LittleEndian.putUShort(data, pos, field_1_seriesNumbers[i]);
|
||||
pos += 2;
|
||||
}
|
||||
|
||||
return getRecordSize();
|
||||
return 4 + dataSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Size of record (exluding 4 byte header)
|
||||
*/
|
||||
public int getRecordSize()
|
||||
{
|
||||
return 4 + field_1_seriesNumbers.length * 2 + 2;
|
||||
@ -86,34 +85,23 @@ public class SeriesListRecord
|
||||
}
|
||||
|
||||
public Object clone() {
|
||||
SeriesListRecord rec = new SeriesListRecord();
|
||||
|
||||
rec.field_1_seriesNumbers = field_1_seriesNumbers;
|
||||
return rec;
|
||||
return new SeriesListRecord((short[]) field_1_seriesNumbers.clone());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the series numbers field for the SeriesList record.
|
||||
*/
|
||||
public short[] getSeriesNumbers()
|
||||
{
|
||||
public short[] getSeriesNumbers() {
|
||||
return field_1_seriesNumbers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the series numbers field for the SeriesList record.
|
||||
*/
|
||||
public void setSeriesNumbers(short[] field_1_seriesNumbers)
|
||||
{
|
||||
public void setSeriesNumbers(short[] field_1_seriesNumbers) {
|
||||
this.field_1_seriesNumbers = field_1_seriesNumbers;
|
||||
}
|
||||
|
||||
|
||||
} // END OF CLASS
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -250,20 +250,6 @@ public final class LittleEndian implements LittleEndianConsts {
|
||||
putNumber(data, offset, value, LittleEndianConsts.BYTE_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* put a array of shorts into a byte array
|
||||
*
|
||||
*@param data the byte array
|
||||
*@param offset a starting offset into the byte array
|
||||
*@param value the short array
|
||||
*/
|
||||
public static void putShortArray(final byte[] data, final int offset, final short[] value) {
|
||||
putNumber(data, offset, value.length, SHORT_SIZE);
|
||||
for (int i = 0; i < value.length; i++) {
|
||||
putNumber(data, offset + 2 + (i * 2), value[i], SHORT_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* put an unsigned short value into a byte array
|
||||
*
|
||||
|
@ -29,7 +29,7 @@ import junit.framework.TestCase;
|
||||
* @author Glen Stampoultzis (glens at apache.org)
|
||||
*/
|
||||
public final class TestSeriesListRecord extends TestCase {
|
||||
byte[] data = new byte[] {
|
||||
private static final byte[] data = {
|
||||
(byte)0x02,(byte)0x00,(byte)0x01,(byte)0x20,(byte)0xff,(byte)0xf0
|
||||
};
|
||||
|
||||
@ -43,10 +43,8 @@ public final class TestSeriesListRecord extends TestCase {
|
||||
assertEquals( 4 + 6, record.getRecordSize() );
|
||||
}
|
||||
|
||||
public void testStore()
|
||||
{
|
||||
SeriesListRecord record = new SeriesListRecord();
|
||||
record.setSeriesNumbers( new short[] { (short)0x2001, (short)0xf0ff } );
|
||||
public void testStore() {
|
||||
SeriesListRecord record = new SeriesListRecord(new short[] { (short)0x2001, (short)0xf0ff } );
|
||||
|
||||
byte [] recordBytes = record.serialize();
|
||||
assertEquals(recordBytes.length - 4, data.length);
|
||||
|
Loading…
Reference in New Issue
Block a user