From 5fe8f2bdd99590ded791e08b43f15cad51aacd7e Mon Sep 17 00:00:00 2001 From: Josh Micich Date: Sun, 15 Nov 2009 06:04:03 +0000 Subject: [PATCH] made ArrayPtg immutable git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@836332 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/hssf/record/formula/ArrayPtg.java | 163 +++++++++++------- .../apache/poi/hssf/record/formula/Ptg.java | 26 ++- .../poi/hssf/record/formula/TestArrayPtg.java | 32 ++-- 3 files changed, 132 insertions(+), 89 deletions(-) diff --git a/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java b/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java index fd34081e6f..0058985ae3 100644 --- a/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java @@ -43,22 +43,23 @@ public final class ArrayPtg extends Ptg { */ public static final int PLAIN_TOKEN_SIZE = 1+RESERVED_FIELD_LEN; - private static final byte[] DEFAULT_RESERVED_DATA = new byte[RESERVED_FIELD_LEN]; - - // TODO - fix up field visibility and subclasses - private final byte[] field_1_reserved; + // 7 bytes of data (stored as an int, short and byte here) + private final int _reserved0Int; + private final int _reserved1Short; + private final int _reserved2Byte; // data from these fields comes after the Ptg data of all tokens in current formula - private int token_1_columns; - private short token_2_rows; - private Object[] token_3_arrayValues; + private final int _nColumns; + private final int _nRows; + private final Object[] _arrayValues; - 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++) { - field_1_reserved[i] = in.readByte(); - } + ArrayPtg(int reserved0, int reserved1, int reserved2, int nColumns, int nRows, Object[] arrayValues) { + _reserved0Int = reserved0; + _reserved1Short = reserved1; + _reserved2Byte = reserved2; + _nColumns = nColumns; + _nRows = nRows; + _arrayValues = arrayValues; } /** * @param values2d array values arranged in rows @@ -67,10 +68,10 @@ public final class ArrayPtg extends Ptg { int nColumns = values2d[0].length; int nRows = values2d.length; // convert 2-d to 1-d array (row by row according to getValueIndex()) - token_1_columns = (short) nColumns; - token_2_rows = (short) nRows; + _nColumns = (short) nColumns; + _nRows = (short) nRows; - Object[] vv = new Object[token_1_columns * token_2_rows]; + Object[] vv = new Object[_nColumns * _nRows]; for (int r=0; r= token_1_columns) { + if(colIx < 0 || colIx >= _nColumns) { throw new IllegalArgumentException("Specified colIx (" + colIx - + ") is outside the allowed range (0.." + (token_1_columns-1) + ")"); + + ") is outside the allowed range (0.." + (_nColumns-1) + ")"); } - if(rowIx < 0 || rowIx >= token_2_rows) { + if(rowIx < 0 || rowIx >= _nRows) { throw new IllegalArgumentException("Specified rowIx (" + rowIx - + ") is outside the allowed range (0.." + (token_2_rows-1) + ")"); + + ") is outside the allowed range (0.." + (_nRows-1) + ")"); } - return rowIx * token_1_columns + colIx; + return rowIx * _nColumns + colIx; } public void write(LittleEndianOutput out) { out.writeByte(sid + getPtgClass()); - out.write(field_1_reserved); + out.writeInt(_reserved0Int); + out.writeShort(_reserved1Short); + out.writeByte(_reserved2Byte); } public int writeTokenValueBytes(LittleEndianOutput out) { - 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); + out.writeByte(_nColumns-1); + out.writeShort(_nRows-1); + ConstantValueParser.encode(out, _arrayValues); + return 3 + ConstantValueParser.getEncodedSize(_arrayValues); } - public short getRowCount() { - return token_2_rows; + public int getRowCount() { + return _nRows; } - public short getColumnCount() { - return (short)token_1_columns; + public int getColumnCount() { + return _nColumns; } /** This size includes the size of the array Ptg plus the Array Ptg Token value size*/ @@ -178,7 +162,7 @@ public final class ArrayPtg extends Ptg { return PLAIN_TOKEN_SIZE // data written after the all tokens: + 1 + 2 // column, row - + ConstantValueParser.getEncodedSize(token_3_arrayValues); + + ConstantValueParser.getEncodedSize(_arrayValues); } public String toFormulaString() { @@ -192,7 +176,7 @@ public final class ArrayPtg extends Ptg { if (x > 0) { b.append(","); } - Object o = token_3_arrayValues[getValueIndex(x, y)]; + Object o = _arrayValues[getValueIndex(x, y)]; b.append(getConstantText(o)); } } @@ -223,4 +207,61 @@ public final class ArrayPtg extends Ptg { public byte getDefaultOperandClass() { return Ptg.CLASS_ARRAY; } + + /** + * Represents the initial plain tArray token (without the constant data that trails the whole + * formula). Objects of this class are only temporary and cannot be used as {@link Ptg}s. + * These temporary objects get converted to {@link ArrayPtg} by the + * {@link #finishReading(LittleEndianInput)} method. + */ + static final class Initial extends Ptg { + private final int _reserved0; + private final int _reserved1; + private final int _reserved2; + + public Initial(LittleEndianInput in) { + _reserved0 = in.readInt(); + _reserved1 = in.readUShort(); + _reserved2 = in.readUByte(); + } + private static RuntimeException invalid() { + throw new IllegalStateException("This object is a partially initialised tArray, and cannot be used as a Ptg"); + } + public byte getDefaultOperandClass() { + throw invalid(); + } + public int getSize() { + return PLAIN_TOKEN_SIZE; + } + public boolean isBaseToken() { + return false; + } + public String toFormulaString() { + throw invalid(); + } + public void write(LittleEndianOutput out) { + throw invalid(); + } + /** + * Read in the actual token (array) values. This occurs + * AFTER the last Ptg in the expression. + * See page 304-305 of Excel97-2007BinaryFileFormat(xls)Specification.pdf + */ + public ArrayPtg finishReading(LittleEndianInput in) { + int nColumns = in.readUByte(); + short nRows = in.readShort(); + //The token_1_columns and token_2_rows do not follow the documentation. + //The number of physical rows and columns is actually +1 of these values. + //Which is not explicitly documented. + nColumns++; + nRows++; + + int totalCount = nRows * nColumns; + Object[] arrayValues = ConstantValueParser.parse(in, totalCount); + + ArrayPtg result = new ArrayPtg(_reserved0, _reserved1, _reserved2, nColumns, nRows, arrayValues); + result.setClass(getPtgClass()); + return result; + } + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/Ptg.java b/src/java/org/apache/poi/hssf/record/formula/Ptg.java index 7e2ad66a46..f2857a7dfa 100644 --- a/src/java/org/apache/poi/hssf/record/formula/Ptg.java +++ b/src/java/org/apache/poi/hssf/record/formula/Ptg.java @@ -51,28 +51,26 @@ public abstract class Ptg implements Cloneable { public static Ptg[] readTokens(int size, LittleEndianInput in) { List temp = new ArrayList(4 + size / 2); int pos = 0; - List arrayPtgs = null; + boolean hasArrayPtgs = false; while (pos < size) { Ptg ptg = Ptg.createPtg(in); - if (ptg instanceof ArrayPtg) { - if (arrayPtgs == null) { - arrayPtgs = new ArrayList(5); - } - arrayPtgs.add(ptg); - pos += ArrayPtg.PLAIN_TOKEN_SIZE; - } else { - pos += ptg.getSize(); + if (ptg instanceof ArrayPtg.Initial) { + hasArrayPtgs = true; } + pos += ptg.getSize(); temp.add(ptg); } if(pos != size) { throw new RuntimeException("Ptg array size mismatch"); } - if (arrayPtgs != null) { - for (int i=0;i