field_6_zero = in.readInt();
field_7_expression_len = in.readShort();
- field_8_parsed_expr = getParsedExpressionTokens(in, field_7_expression_len);
+ field_8_parsed_expr = Ptg.createParsedExpressionTokens(field_7_expression_len, in);
} catch (java.lang.UnsupportedOperationException uoe) {
throw new RecordFormatException(uoe.toString());
}
}
- private Stack getParsedExpressionTokens(RecordInputStream in, short size)
- {
- Stack stack = new Stack();
- int pos = 0;
-
- while (pos < size)
- {
- Ptg ptg = Ptg.createPtg(in);
- pos += ptg.getSize();
- stack.push(ptg);
- }
- return stack;
- }
-
//public void setRow(short row)
public void setRow(int row)
{
//Microsoft Excel Developer's Kit Page 318
LittleEndian.putInt(data, 20 + offset, 0);
LittleEndian.putShort(data, 24 + offset, getExpressionLength());
- serializePtgs(data, 26+offset);
+ Ptg.serializePtgStack(field_8_parsed_expr, data, 26+offset);
} else {
System.arraycopy(all_data,0,data,offset,all_data.length);
}
return retval;
}
- private void serializePtgs(byte [] data, int offset)
- {
- int pos = offset;
-
- for (int k = 0; k < field_8_parsed_expr.size(); k++)
- {
- Ptg ptg = ( Ptg ) field_8_parsed_expr.get(k);
-
- ptg.writeBytes(data, pos);
- pos += ptg.getSize();
- }
- }
-
public boolean isBefore(CellValueRecordInterface i)
{
if (this.getRow() > i.getRow())
public int fillField( RecordInputStream in )
{
short tokenSize = in.readShort();
- formulaTokens = getParsedExpressionTokens(tokenSize, in);
+ formulaTokens = Ptg.createParsedExpressionTokens(tokenSize, in);
return tokenSize + 2;
}
int size = getSize();
LittleEndian.putShort(data, offset, (short)(size - 2));
int pos = offset + 2;
- for ( Iterator iterator = formulaTokens.iterator(); iterator.hasNext(); )
- {
- Ptg ptg = (Ptg) iterator.next();
- ptg.writeBytes(data, pos);
- pos += ptg.getSize();
- }
+ pos += Ptg.serializePtgStack(formulaTokens, data, pos);
return size;
}
}
}
- private Stack getParsedExpressionTokens(short size, RecordInputStream in )
- {
- Stack stack = new Stack();
- int pos = 0;
- while ( pos < size )
- {
- Ptg ptg = Ptg.createPtg( in );
- pos += ptg.getSize();
- stack.push( ptg );
- }
- return stack;
- }
-
public void setFormulaTokens( Stack formulaTokens )
{
this.formulaTokens = (Stack) formulaTokens.clone();
/** get the definition length
* @return definition length
*/
- public short getDefinitionTextLength(){
+ public short getDefinitionLength(){
return field_4_length_name_definition;
}
throw new RecordFormatException("NOT A valid Name RECORD");
}
}
-
+
/**
* called by the class that is responsible for writing this sucker.
* Subclasses should implement this so that their data is passed back in a
public int serialize( int offset, byte[] data )
{
LittleEndian.putShort( data, 0 + offset, sid );
+ short size = (short)( 15 + getTextsLength() + getNameDefinitionSize());
+ LittleEndian.putShort( data, 2 + offset, size );
// size defined below
LittleEndian.putShort( data, 4 + offset, getOptionFlag() );
data[6 + offset] = getKeyboardShortcut();
data[7 + offset] = getNameTextLength();
- LittleEndian.putShort( data, 8 + offset, getDefinitionTextLength() );
+ LittleEndian.putShort( data, 8 + offset, getDefinitionLength() );
LittleEndian.putShort( data, 10 + offset, getUnused() );
LittleEndian.putShort( data, 12 + offset, getEqualsToIndexToSheet() );
data[14 + offset] = getCustomMenuLength();
return 20 + field_13_raw_name_definition.length;
}
else
- { */
- LittleEndian.putShort( data, 2 + offset, (short) ( 15 + getTextsLength() ) );
+ { */
int start_of_name_definition = 19 + field_3_length_name_text;
}
- serializePtgs( data, start_of_name_definition + offset );
+ Ptg.serializePtgStack(field_13_name_definition, data, start_of_name_definition + offset );
int start_of_custom_menu_text = start_of_name_definition + field_4_length_name_definition;
/* } */
}
- private void serializePtgs(byte [] data, int offset) {
- int pos = offset;
-
- for (int k = 0; k < field_13_name_definition.size(); k++) {
- Ptg ptg = ( Ptg ) field_13_name_definition.get(k);
-
- ptg.writeBytes(data, pos);
- pos += ptg.getSize();
- }
- }
-
-
/** gets the length of all texts
* @return total length
*/
public int getTextsLength(){
int result;
- result = getNameTextLength() + getDefinitionTextLength() + getDescriptionTextLength() +
+ result = getNameTextLength() + getDescriptionTextLength() +
getHelpTopicLength() + getStatusBarLength();
return result;
}
+
+ private int getNameDefinitionSize() {
+ int result = 0;
+ List list = field_13_name_definition;
+
+ for (int k = 0; k < list.size(); k++)
+ {
+ Ptg ptg = ( Ptg ) list.get(k);
+
+ result += ptg.getSize();
+ }
+ return result;
+ }
/** returns the record size
*/
public int getRecordSize(){
int result;
- result = 19 + getTextsLength();
+ result = 19 + getTextsLength() + getNameDefinitionSize();
+
return result;
}
}
}
- field_13_name_definition = getParsedExpressionTokens(in, field_4_length_name_definition);
+ field_13_name_definition = Ptg.createParsedExpressionTokens(field_4_length_name_definition, in);
//Who says that this can only ever be compressed unicode???
field_14_custom_menu_text = in.readCompressedUnicode(LittleEndian.ubyteToInt(field_7_length_custom_menu));
/*} */
}
- private Stack getParsedExpressionTokens(RecordInputStream in, short size) {
- Stack stack = new Stack();
- int sizeCounter = 0;
- try {
- while (sizeCounter < size) {
- Ptg ptg = Ptg.createPtg(in);
-
- sizeCounter += ptg.getSize();
- stack.push(ptg);
- }
- } catch (java.lang.UnsupportedOperationException uoe) {
- throw new RecordFormatException(uoe.toString());
- }
- return stack;
- }
-
-
/**
* return the non static version of the id for this record.
*/
--- /dev/null
+/* ====================================================================
+ Copyright 2003-2004 Apache Software Foundation
+
+ Licensed 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.formula;
+
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.BitField;
+import org.apache.poi.util.BitFieldFactory;
+import org.apache.poi.util.StringUtil;
+
+import org.apache.poi.hssf.util.CellReference;
+import org.apache.poi.hssf.model.Workbook;
+import org.apache.poi.hssf.record.RecordFormatException;
+import org.apache.poi.hssf.record.RecordInputStream;
+import org.apache.poi.hssf.record.SSTRecord;
+import org.apache.poi.hssf.record.UnicodeString;
+
+/**
+ * ArrayPtg - handles arrays
+ *
+ * The ArrayPtg is a little wierd, the size of the Ptg when parsing initially only
+ * includes the Ptg sid and the reserved bytes. The next Ptg in the expression then follows.
+ * It is only after the "size" of all the Ptgs is met, that the ArrayPtg data is actually
+ * held after this. So Ptg.createParsedExpression keeps track of the number of
+ * ArrayPtg elements and need to parse the data upto the FORMULA record size.
+ *
+ * @author Jason Height (jheight at chariot dot net dot au)
+ */
+
+public class ArrayPtg extends Ptg
+{
+ public final static byte sid = 0x20;
+ protected byte field_1_reserved;
+ protected byte field_2_reserved;
+ protected byte field_3_reserved;
+ protected byte field_4_reserved;
+ protected byte field_5_reserved;
+ protected byte field_6_reserved;
+ protected byte field_7_reserved;
+
+
+ protected short token_1_columns;
+ protected short token_2_rows;
+ protected Object[][] token_3_arrayValues;
+
+ protected ArrayPtg() {
+ //Required for clone methods
+ }
+
+ public ArrayPtg(RecordInputStream in)
+ {
+ field_1_reserved = in.readByte();
+ field_2_reserved = in.readByte();
+ field_3_reserved = in.readByte();
+ field_4_reserved = in.readByte();
+ field_5_reserved = in.readByte();
+ field_6_reserved = in.readByte();
+ field_7_reserved = in.readByte();
+ }
+
+ /** Read in the actual token (array) values. This occurs AFTER the last
+ * Ptg in the expression.
+ */
+ public void readTokenValues(RecordInputStream in) {
+ token_1_columns = (short)(0x00ff & in.readByte());
+ token_2_rows = 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.
+ token_1_columns++;
+ token_2_rows++;
+
+ token_3_arrayValues = new Object[token_1_columns][token_2_rows];
+
+ for (int x=0;x<token_1_columns;x++) {
+ for (int y=0;y<token_2_rows;y++) {
+ byte grbit = in.readByte();
+ if (grbit == 0x01) {
+ token_3_arrayValues[x][y] = Double.valueOf(in.readDouble());
+ } else if (grbit == 0x02) {
+ //Ignore the doco, it is actually a unicode string with all the
+ //trimmings ie 16 bit size, option byte etc
+ token_3_arrayValues[x][y] = in.readUnicodeString();
+ } else throw new RecordFormatException("Unknown grbit '"+grbit+"'");
+ }
+ }
+
+ }
+
+ public String toString()
+ {
+ StringBuffer buffer = new StringBuffer("[ArrayPtg]\n");
+
+ buffer.append("columns = ").append(getColumnCount()).append("\n");
+ buffer.append("rows = ").append(getRowCount()).append("\n");
+ for (int x=0;x<getColumnCount();x++) {
+ for (int y=0;y<getRowCount();y++) {
+ Object o = token_3_arrayValues[x][y];
+ buffer.append("[").append(x).append("][").append(y).append("] = ").append(o).append("\n");
+ }
+ }
+ return buffer.toString();
+ }
+
+ public void writeBytes(byte [] array, int offset)
+ {
+ array[offset++] = (byte) (sid + ptgClass);
+ array[offset++] = field_1_reserved;
+ array[offset++] = field_2_reserved;
+ array[offset++] = field_3_reserved;
+ array[offset++] = field_4_reserved;
+ array[offset++] = field_5_reserved;
+ array[offset++] = field_6_reserved;
+ array[offset++] = field_7_reserved;
+
+ }
+ public int writeTokenValueBytes(byte [] array, int offset) {
+ int pos = 0;
+ array[pos + offset] = (byte)(token_1_columns-1);
+ pos++;
+ LittleEndian.putShort(array, pos+offset, (short)(token_2_rows-1));
+ pos += 2;
+ for (int x=0;x<getColumnCount();x++) {
+ for (int y=0;y<getRowCount();y++) {
+ Object o = token_3_arrayValues[x][y];
+ if (o instanceof Double) {
+ array[pos+offset] = 0x01;
+ pos++;
+ LittleEndian.putDouble(array, pos+offset, ((Double)o).doubleValue());
+ pos+=8;
+ } else if (o instanceof UnicodeString) {
+ array[pos+offset] = 0x02;
+ pos++;
+ UnicodeString s = (UnicodeString)o;
+ //JMH TBD Handle string continuation. Id do it now but its 4am.
+ UnicodeString.UnicodeRecordStats stats = new UnicodeString.UnicodeRecordStats();
+ s.serialize(stats, pos + offset, array);
+ pos += stats.recordSize;
+ } else throw new RuntimeException("Coding error");
+ }
+ }
+ return pos;
+ }
+
+ public void setRowCount(short row)
+ {
+ token_2_rows = row;
+ }
+
+ public short getRowCount()
+ {
+ return token_2_rows;
+ }
+
+ public void setColumnCount(short col)
+ {
+ token_1_columns = (byte)col;
+ }
+
+ public short getColumnCount()
+ {
+ return token_1_columns;
+ }
+
+ /** This size includes the size of the array Ptg plus the Array Ptg Token value size*/
+ public int getSize()
+ {
+ int size = 1+7+1+2;
+ for (int x=0;x<getColumnCount();x++) {
+ for (int y=0;y<getRowCount();y++) {
+ Object o = token_3_arrayValues[x][y];
+ if (o instanceof UnicodeString) {
+ size++;
+ UnicodeString.UnicodeRecordStats rs = new UnicodeString.UnicodeRecordStats();
+ ((UnicodeString)o).getRecordSize(rs);
+ size += rs.recordSize;
+ } else if (o instanceof Double) {
+ size += 9;
+ }
+ }
+ }
+ return size;
+ }
+
+ public String toFormulaString(Workbook book)
+ {
+ StringBuffer b = new StringBuffer();
+ b.append("{");
+ for (int x=0;x<getColumnCount();x++) {
+ for (int y=0;y<getRowCount();y++) {
+ Object o = token_3_arrayValues[x][y];
+ if (o instanceof String) {
+ b.append((String)o);
+ } else if (o instanceof Double) {
+ b.append(((Double)o).doubleValue());
+ }
+ if (y != getRowCount())
+ b.append(",");
+ }
+ if (x != getColumnCount())
+ b.append(";");
+ }
+ b.append("}");
+ return b.toString();
+ }
+
+ public byte getDefaultOperandClass() {
+ return Ptg.CLASS_ARRAY;
+ }
+
+ public Object clone() {
+ ArrayPtg ptg = new ArrayPtg();
+ ptg.field_1_reserved = field_1_reserved;
+ ptg.field_2_reserved = field_2_reserved;
+ ptg.field_3_reserved = field_3_reserved;
+ ptg.field_4_reserved = field_4_reserved;
+ ptg.field_5_reserved = field_5_reserved;
+ ptg.field_6_reserved = field_6_reserved;
+ ptg.field_7_reserved = field_7_reserved;
+
+ ptg.token_1_columns = token_1_columns;
+ ptg.token_2_rows = token_2_rows;
+ ptg.token_3_arrayValues = new Object[getColumnCount()][getRowCount()];
+ for (int x=0;x<getColumnCount();x++) {
+ for (int y=0;y<getRowCount();y++) {
+ ptg.token_3_arrayValues[x][y] = token_3_arrayValues[x][y];
+ }
+ }
+ ptg.setClass(ptgClass);
+ return ptg;
+ }
+}
--- /dev/null
+/* ====================================================================
+ Copyright 2003-2004 Apache Software Foundation
+
+ Licensed 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.formula;
+
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.BitField;
+import org.apache.poi.util.BitFieldFactory;
+import org.apache.poi.util.StringUtil;
+
+import org.apache.poi.hssf.util.CellReference;
+import org.apache.poi.hssf.model.Workbook;
+import org.apache.poi.hssf.record.RecordFormatException;
+import org.apache.poi.hssf.record.RecordInputStream;
+import org.apache.poi.hssf.record.SSTRecord;
+import org.apache.poi.hssf.record.UnicodeString;
+
+/**
+ * ArrayPtgA - handles arrays
+ *
+ * @author Jason Height (jheight at chariot dot net dot au)
+ */
+
+public class ArrayPtgA extends ArrayPtg
+{
+ public final static byte sid = 0x60;
+
+ protected ArrayPtgA() {
+ super();
+ //Required for clone methods
+ }
+
+ public ArrayPtgA(RecordInputStream in)
+ {
+ super(in);
+ }
+
+ public Object clone() {
+ ArrayPtgA ptg = new ArrayPtgA();
+ ptg.field_1_reserved = field_1_reserved;
+ ptg.field_2_reserved = field_2_reserved;
+ ptg.field_3_reserved = field_3_reserved;
+ ptg.field_4_reserved = field_4_reserved;
+ ptg.field_5_reserved = field_5_reserved;
+ ptg.field_6_reserved = field_6_reserved;
+ ptg.field_7_reserved = field_7_reserved;
+
+ ptg.token_1_columns = token_1_columns;
+ ptg.token_2_rows = token_2_rows;
+ ptg.token_3_arrayValues = new Object[getColumnCount()][getRowCount()];
+ for (int x=0;x<getColumnCount();x++) {
+ for (int y=0;y<getRowCount();y++) {
+ ptg.token_3_arrayValues[x][y] = token_3_arrayValues[x][y];
+ }
+ }
+ ptg.setClass(ptgClass);
+ return ptg;
+ }
+}
--- /dev/null
+/* ====================================================================
+ Copyright 2003-2004 Apache Software Foundation
+
+ Licensed 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.formula;
+
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.BitField;
+import org.apache.poi.util.BitFieldFactory;
+import org.apache.poi.util.StringUtil;
+
+import org.apache.poi.hssf.util.CellReference;
+import org.apache.poi.hssf.model.Workbook;
+import org.apache.poi.hssf.record.RecordFormatException;
+import org.apache.poi.hssf.record.RecordInputStream;
+import org.apache.poi.hssf.record.SSTRecord;
+import org.apache.poi.hssf.record.UnicodeString;
+
+/**
+ * ArrayPtg - handles arrays
+ *
+ * The ArrayPtg is a little wierd, the size of the Ptg when parsing initially only
+ * includes the Ptg sid and the reserved bytes. The next Ptg in the expression then follows.
+ * It is only after the "size" of all the Ptgs is met, that the ArrayPtg data is actually
+ * held after this. So Ptg.createParsedExpression keeps track of the number of
+ * ArrayPtg elements and need to parse the data upto the FORMULA record size.
+ *
+ * @author Jason Height (jheight at chariot dot net dot au)
+ */
+
+public class ArrayPtgV extends ArrayPtg
+{
+ public final static byte sid = 0x40;
+
+ protected ArrayPtgV() {
+ //Required for clone methods
+ }
+
+ public ArrayPtgV(RecordInputStream in)
+ {
+ super(in);
+ }
+
+ public Object clone() {
+ ArrayPtgV ptg = new ArrayPtgV();
+ ptg.field_1_reserved = field_1_reserved;
+ ptg.field_2_reserved = field_2_reserved;
+ ptg.field_3_reserved = field_3_reserved;
+ ptg.field_4_reserved = field_4_reserved;
+ ptg.field_5_reserved = field_5_reserved;
+ ptg.field_6_reserved = field_6_reserved;
+ ptg.field_7_reserved = field_7_reserved;
+
+ ptg.token_1_columns = token_1_columns;
+ ptg.token_2_rows = token_2_rows;
+ ptg.token_3_arrayValues = new Object[getColumnCount()][getRowCount()];
+ for (int x=0;x<getColumnCount();x++) {
+ for (int y=0;y<getRowCount();y++) {
+ ptg.token_3_arrayValues[x][y] = token_3_arrayValues[x][y];
+ }
+ }
+ ptg.setClass(ptgClass);
+ return ptg;
+ }
+}
import java.util.List;
import java.util.ArrayList;
+import java.util.Stack;
import org.apache.poi.hssf.model.Workbook;
import org.apache.poi.hssf.record.RecordInputStream;
return retval;
}
*/
+
+ public static Stack createParsedExpressionTokens(short size, RecordInputStream in )
+ {
+ Stack stack = new Stack();
+ int pos = 0;
+ List arrayPtgs = null;
+ while ( pos < size )
+ {
+ Ptg ptg = Ptg.createPtg( in );
+ if (ptg instanceof ArrayPtg) {
+ if (arrayPtgs == null)
+ arrayPtgs = new ArrayList(5);
+ arrayPtgs.add(ptg);
+ pos += 8;
+ } else pos += ptg.getSize();
+ stack.push( ptg );
+ }
+ if (arrayPtgs != null) {
+ for (int i=0;i<arrayPtgs.size();i++) {
+ ArrayPtg p = (ArrayPtg)arrayPtgs.get(i);
+ p.readTokenValues(in);
+ }
+ }
+ return stack;
+ }
- public static Ptg createPtg(RecordInputStream in)
+ private static Ptg createPtg(RecordInputStream in)
{
byte id = in.readByte();
Ptg retval = null;
case ConcatPtg.sid :
retval = new ConcatPtg(in);
break;
+
+ case ArrayPtg.sid:
+ retval = new ArrayPtg(in);
+ break;
+ case ArrayPtgV.sid:
+ retval = new ArrayPtgV(in);
+ break;
+ case ArrayPtgA.sid:
+ retval = new ArrayPtgA(in);
+ break;
case AreaPtg.sid :
retval = new AreaPtg(in);
return retval;
}
+
+ public static int serializePtgStack(Stack expression, byte[] array, int offset) {
+ int pos = 0;
+ int size = 0;
+ if (expression != null)
+ size = expression.size();
+
+ List arrayPtgs = null;
+
+ for (int k = 0; k < size; k++) {
+ Ptg ptg = ( Ptg ) expression.get(k);
+
+ ptg.writeBytes(array, pos + offset);
+ if (ptg instanceof ArrayPtg) {
+ if (arrayPtgs == null)
+ arrayPtgs = new ArrayList(5);
+ arrayPtgs.add(ptg);
+ pos += 8;
+ } 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);
+ }
+ }
+ return pos;
+ }
public abstract int getSize();
HSSFWorkbook wb = new HSSFWorkbook(in);
assertTrue("Read book fine!" , true);
}
+
+ /** Error when reading then writing ArrayValues in NameRecord's*/
+ public void test37630() throws java.io.IOException {
+ String filename = System.getProperty("HSSF.testdata.path");
+ filename=filename+"/37630.xls";
+ FileInputStream in = new FileInputStream(filename);
+ HSSFWorkbook wb = new HSSFWorkbook(in);
+ File file = TempFile.createTempFile("test37630",".xls");
+ FileOutputStream out = new FileOutputStream(file);
+ wb.write(out);
+
+ assertTrue("Read book fine!" , true);
+ }
+
+
+
}