From 84e47bd7bbfc7de0694599f904cf43d7097b0bc8 Mon Sep 17 00:00:00 2001 From: Jason Height Date: Sat, 21 Jan 2006 05:40:07 +0000 Subject: [PATCH] Bug37630: SQUASHED! Array Ptgs now implemented (at least the read and write functionality. No means to modify (yet!)) git-svn-id: https://svn.apache.org/repos/asf/jakarta/poi/trunk@370987 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/poi/hssf/record/FormulaRecord.java | 31 +-- .../hssf/record/LinkedDataFormulaField.java | 22 +- .../apache/poi/hssf/record/NameRecord.java | 62 ++--- .../poi/hssf/record/formula/ArrayPtg.java | 246 ++++++++++++++++++ .../poi/hssf/record/formula/ArrayPtgA.java | 72 +++++ .../poi/hssf/record/formula/ArrayPtgV.java | 77 ++++++ .../apache/poi/hssf/record/formula/Ptg.java | 66 ++++- .../org/apache/poi/hssf/data/37630.xls | Bin 0 -> 14848 bytes .../apache/poi/hssf/usermodel/TestBugs.java | 16 ++ 9 files changed, 504 insertions(+), 88 deletions(-) create mode 100644 src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java create mode 100644 src/java/org/apache/poi/hssf/record/formula/ArrayPtgA.java create mode 100644 src/java/org/apache/poi/hssf/record/formula/ArrayPtgV.java create mode 100644 src/testcases/org/apache/poi/hssf/data/37630.xls diff --git a/src/java/org/apache/poi/hssf/record/FormulaRecord.java b/src/java/org/apache/poi/hssf/record/FormulaRecord.java index 11c716556..a3b189d47 100644 --- a/src/java/org/apache/poi/hssf/record/FormulaRecord.java +++ b/src/java/org/apache/poi/hssf/record/FormulaRecord.java @@ -100,26 +100,12 @@ public class FormulaRecord 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) { @@ -330,7 +316,7 @@ public class FormulaRecord //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); } @@ -368,19 +354,6 @@ public class FormulaRecord 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()) diff --git a/src/java/org/apache/poi/hssf/record/LinkedDataFormulaField.java b/src/java/org/apache/poi/hssf/record/LinkedDataFormulaField.java index c16f25458..45921f86c 100644 --- a/src/java/org/apache/poi/hssf/record/LinkedDataFormulaField.java +++ b/src/java/org/apache/poi/hssf/record/LinkedDataFormulaField.java @@ -49,7 +49,7 @@ public class LinkedDataFormulaField public int fillField( RecordInputStream in ) { short tokenSize = in.readShort(); - formulaTokens = getParsedExpressionTokens(tokenSize, in); + formulaTokens = Ptg.createParsedExpressionTokens(tokenSize, in); return tokenSize + 2; } @@ -80,12 +80,7 @@ public class LinkedDataFormulaField 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; } @@ -103,19 +98,6 @@ public class LinkedDataFormulaField } } - 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(); diff --git a/src/java/org/apache/poi/hssf/record/NameRecord.java b/src/java/org/apache/poi/hssf/record/NameRecord.java index 576385cfb..9217a71b6 100644 --- a/src/java/org/apache/poi/hssf/record/NameRecord.java +++ b/src/java/org/apache/poi/hssf/record/NameRecord.java @@ -331,7 +331,7 @@ public class NameRecord extends Record { /** get the definition length * @return definition length */ - public short getDefinitionTextLength(){ + public short getDefinitionLength(){ return field_4_length_name_definition; } @@ -488,7 +488,7 @@ public class NameRecord extends Record { 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 @@ -501,11 +501,13 @@ public class NameRecord extends Record { 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(); @@ -525,8 +527,7 @@ public class NameRecord extends Record { 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; @@ -539,7 +540,7 @@ public class NameRecord extends Record { } - 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; @@ -558,37 +559,39 @@ public class NameRecord extends Record { /* } */ } - 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; } @@ -733,7 +736,7 @@ public class NameRecord extends Record { } } - 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)); @@ -746,23 +749,6 @@ public class NameRecord extends Record { /*} */ } - 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. */ diff --git a/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java b/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java new file mode 100644 index 000000000..568b3a3cb --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java @@ -0,0 +1,246 @@ +/* ==================================================================== + 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;xbNXt=*zlO%4JSO*qr*pdsvVUkuH(5^;7EzM^M-dp8 zmJ?ZCh99PURPmt|ECs$n30_GZmlF6^au)ZX5?Ir@x4$p5y1RF0&tT?kr)ymwIz8=! zl?$)UD~BhtQXG#!UM3bGZMT&8PVaG7*1K|o zlpfx4ZN74ng_wp9M1Sa^gL=6Grp&tr1L#F3cH`{rtxMIS43mKR+4ZxUXU$sBnr$K0 zrDn=~SR31S=lbsLcf}RzP$3~1ZajZMyuEDIrDh?Gx?+tHR-D@9Xu<-ZL!CEeTu(Y$ zqP`S1^Ly)C>SbR?i{H^wmuf~M=4!)&S~kWiFVb&AGvw679Z3g9_=+S2ckS&3hf6RI zm&F{ib>2UlI=At5Gw>`fE(D^Zu{GPkNM{@CQkUR4%qZF)#i>ZC9?xTDK}ns(?Z~#8 znu`}uKT>OiwY=(59Kj+v8uh{mHn!$BvkmS!d`IaFjyW8rwp^U4e!e&&i zH=L9I%3Y_I>$Blh`lHF!>Z$b4XSs)r9$r2%p2wm%{612CbQJlEqsWhpA|Lk;`I99n zRGHEtt*cGBt7}Zk)sswxt6|{k$);>5kY}=oN99R>afSA>92rJ97$5k@HiL|Ftf72!MRqr^R7fzJ0 z&Kak&iCO^R*FJEtcI(MX1TwTcSh>}kDvboA7NU}epV}$$^N=P~5hfFA2{Q;8LOo$N z;Zi~ap^4B;m`_+hSV&kzSWH+#xSVhWVJTr5;Yz|)gjT}Ugf_x*LOa3FgCXD=N(i`? z5(2KHgn$*45U`RG0#;E%!1a_6u$mGAZlHvKHIxu=BP9gfLJSXU z60n6j1VgX{+)f>WAy@+Lpbo(hECF{?hhPYnfGl+ghF}S}i#h~Dumo(S4#5yC0UgvK z7=k4r$m?VXJ|Q3&LXKbwApxDVL+}X!!4Pr;O9%69R%Eslk6$r!r(Ujf>7qf5{77-_Rc&E4YCY!&cYBon6ns$ zn3r>lf63Us%&mVF_e0NK_~h^uFSSlWR!i22 z)JDlV5!)?U)(f#;@`aKQN|sY9D|a$0k^G%BlBKBO35>Gd2=N6**>pN8U)$R?(7(5T z$6#jV{axL=;p@()exKU#_B)>A!~04foBqC+zwmT#l9C7{5lAACL?DSk5`iQFNd%Gz zBoRm=kVGJfKoWsi1Wq^qFFEnii9_?JS3UW(%>SGJcuD8~Ur5$vFkK&5Cs}_u+#*@t zsK9o~<0b3-U+3+El6BtypyZ1rb3NdIu&xihA^BA60XmQWe#&@fOK{Y>PMoN&u17?_ z4i|7y0T-iE6xER~G-wAm_Ydsat3N02b?uXN1BpqJk_aRbNFtC#Ac;T{fg}P+1d<3O z5lAACMBu!RfX?G}YOHf$`B`56gQD+RM{{cZO<(8w`u{GSHtU>Sf6vz!i*??wb9{Xj zTleccpRX+ITwm7%Y9-5`Qr!MOMMw3L^xulWdi2X@5E-mQpVR|*K+f3M(?#<32#-jw zqfA;$z`7EvqADuwpkH}F-qYa!1y#CPyboX(wz+=nimQ)_;pP z3>$DS_QDj7tta9^Z)%CbTH>pi6R&shEI>tOEYk*W+U#6y?sL$^Bv SXV#x(|Kj-FpC$C>mGn1IHd}fC literal 0 HcmV?d00001 diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java index 5b102733d..4d610d8d9 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java @@ -522,6 +522,22 @@ extends TestCase { 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); + } + + + }