<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
</release>
<release version="3.1.1-alpha1" date="2008-??-??">
+ <action dev="POI-DEVELOPERS" type="fix">Impove empty header or footer handling in HWPF HeaderStories</action>
+ <action dev="POI-DEVELOPERS" type="fix">Avoid NPE in hssf.usermodel.HeaderFooter when stripping fields out</action>
+ <action dev="POI-DEVELOPERS" type="fix">Avoid NPE in EscherBSERecord on older escher records</action>
+ <action dev="POI-DEVELOPERS" type="add">Basic text extractraction support in HPBF</action>
+ <action dev="POI-DEVELOPERS" type="add">Initial, low level support for Publisher files, in the form of HPBF</action>
<action dev="POI-DEVELOPERS" type="fix">45699 - Fix RowRecordsAggregate to tolerate intervening MERGEDCELLS records</action>
<action dev="POI-DEVELOPERS" type="fix">45698 - Fix LinkTable to tolerate multiple EXTERNSHEET records</action>
<action dev="POI-DEVELOPERS" type="fix">45682 - Fix for cloning of CFRecordsAggregate</action>
(the text will then start)
</source>
+ <p>We think that the first 4 bytes of text describes the
+ the function of the data at the offset. The first short is
+ then the count of that type, eg the 2nd will have 1. We
+ think that the second 4 bytes of text describes the format
+ of data block at the offset. The format of the text block
+ is easy, but we're still trying to figure out the others.</p>
</section>
</body>
</document>
<title>Overview</title>
<p>HPBF is the POI Project's pure Java implementation of the Visio file format.</p>
- <p>Currently, HPBF is in the experimental stage, while we try
- to figure out the file format. Our initial aim is to provide
- a text extractor for the format, with low level code following
- after that if demand and developer interest warrant it.</p>
- <p>At this time, there is no <em>usermodel</em> api or similar.</p>
+ <p>Currently, HPBF is in an early stage, whilst we try to
+ figure out the file format. So far, we have basic text
+ extraction support, and are able to read some parts within
+ the file. Writing is not yet supported, as we are unable
+ to make sense of the Contents stream, which we think has
+ lots of offsets to other parts of the file.</p>
+ <p>Our initial aim is to provude a text extractor for the format
+ (now done), and be able to extract hyperlinks from within
+ the document (not yet supported). Additional low level
+ code to process the file format may follow, if there
+ is demand and developer interest warrant it.</p>
+ <p>At this time, there is no <em>usermodel</em> api or similar.
+ There is only low level support for certain parts of
+ the file, but by no means all of it.</p>
<p>Our current understanding of the file format is documented
<link href="file-format.html">here</link>.</p>
<note>
</section>
<section><title>HPBF for Publisher Documents</title>
<p>HPBF is our port of the Microsoft Publisher 98(-2007) file format to pure
- Java. At the moment, we are still figuring out the file format, but we hope
- to have simple text extraction shortly. Please see <link
+ Java. It currently only supports reading at a low level for around
+ half of the file parts, and simple text extraction. Please see <link
href="./hpbf/index.html">the HPBF project page for more
information</link>.</p>
</section>
<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
</release>
<release version="3.1.1-alpha1" date="2008-??-??">
+ <action dev="POI-DEVELOPERS" type="fix">Impove empty header or footer handling in HWPF HeaderStories</action>
+ <action dev="POI-DEVELOPERS" type="fix">Avoid NPE in hssf.usermodel.HeaderFooter when stripping fields out</action>
+ <action dev="POI-DEVELOPERS" type="fix">Avoid NPE in EscherBSERecord on older escher records</action>
+ <action dev="POI-DEVELOPERS" type="add">Basic text extractraction support in HPBF</action>
+ <action dev="POI-DEVELOPERS" type="add">Initial, low level support for Publisher files, in the form of HPBF</action>
<action dev="POI-DEVELOPERS" type="fix">45699 - Fix RowRecordsAggregate to tolerate intervening MERGEDCELLS records</action>
<action dev="POI-DEVELOPERS" type="fix">45698 - Fix LinkTable to tolerate multiple EXTERNSHEET records</action>
<action dev="POI-DEVELOPERS" type="fix">45682 - Fix for cloning of CFRecordsAggregate</action>
field_10_unused2 = data[pos + 34];
field_11_unused3 = data[pos + 35];
bytesRemaining -= 36;
+
int bytesRead = 0;
- if (bytesRemaining > 0)
- {
+ if (bytesRemaining > 0) {
+ // Some older escher formats skip this last record
field_12_blipRecord = (EscherBlipRecord) recordFactory.createRecord( data, pos + 36 );
bytesRead = field_12_blipRecord.fillFields( data, pos + 36, recordFactory );
}
*/
public int getRecordSize()
{
- return 8 + 1 + 1 + 16 + 2 + 4 + 4 + 4 + 1 + 1 + 1 + 1 + field_12_blipRecord.getRecordSize() + (remainingData == null ? 0 : remainingData.length);
+ int field_12_size = 0;
+ if(field_12_blipRecord != null) {
+ field_12_size = field_12_blipRecord.getRecordSize();
+ }
+ int remaining_size = 0;
+ if(remainingData != null) {
+ remaining_size = remainingData.length;
+ }
+ return 8 + 1 + 1 + 16 + 2 + 4 + 4 + 4 + 1 + 1 +
+ 1 + 1 + field_12_size + remaining_size;
}
/**
--- /dev/null
+/* ====================================================================\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to You under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.hssf.record;\r
+\r
+import org.apache.poi.hssf.record.formula.Ptg;\r
+import org.apache.poi.hssf.util.CellRangeAddress8Bit;\r
+import org.apache.poi.util.HexDump;\r
+import org.apache.poi.util.LittleEndian;\r
+\r
+/**\r
+ * ARRAY (0x0221)<p/>\r
+ * \r
+ * Treated in a similar way to SharedFormulaRecord\r
+ * \r
+ * @author Josh Micich\r
+ */ \r
+public final class ArrayRecord extends Record {\r
+\r
+ public final static short sid = 0x0221;\r
+ private static final int OPT_ALWAYS_RECALCULATE = 0x0001;\r
+ private static final int OPT_CALCULATE_ON_OPEN = 0x0002;\r
+\r
+ private CellRangeAddress8Bit _range;\r
+ \r
+ private int _options;\r
+ private int _field3notUsed;\r
+ private Ptg[] _formulaTokens;\r
+\r
+ public ArrayRecord(RecordInputStream in) {\r
+ super(in);\r
+ }\r
+\r
+ public boolean isAlwaysRecalculate() {\r
+ return (_options & OPT_ALWAYS_RECALCULATE) != 0;\r
+ }\r
+ public boolean isCalculateOnOpen() {\r
+ return (_options & OPT_CALCULATE_ON_OPEN) != 0;\r
+ }\r
+\r
+ protected void validateSid(short id) {\r
+ if (id != sid) {\r
+ throw new RecordFormatException("NOT A valid Array RECORD");\r
+ }\r
+ }\r
+\r
+ private int getDataSize(){\r
+ return CellRangeAddress8Bit.ENCODED_SIZE \r
+ + 2 + 4\r
+ + getFormulaSize();\r
+ }\r
+\r
+ public int serialize( int offset, byte[] data ) {\r
+ int dataSize = getDataSize();\r
+\r
+ LittleEndian.putShort(data, 0 + offset, sid);\r
+ LittleEndian.putUShort(data, 2 + offset, dataSize);\r
+\r
+ int pos = offset+4;\r
+ _range.serialize(pos, data);\r
+ pos += CellRangeAddress8Bit.ENCODED_SIZE;\r
+ LittleEndian.putUShort(data, pos, _options);\r
+ pos+=2;\r
+ LittleEndian.putInt(data, pos, _field3notUsed);\r
+ pos+=4;\r
+ int tokenSize = Ptg.getEncodedSizeWithoutArrayData(_formulaTokens);\r
+ LittleEndian.putUShort(data, pos, tokenSize);\r
+ pos+=2;\r
+ Ptg.serializePtgs(_formulaTokens, data, pos);\r
+ return dataSize + 4;\r
+ }\r
+\r
+ private int getFormulaSize() {\r
+ int result = 0;\r
+ for (int i = 0; i < _formulaTokens.length; i++) {\r
+ result += _formulaTokens[i].getSize();\r
+ }\r
+ return result;\r
+ }\r
+\r
+\r
+ public int getRecordSize(){\r
+ return 4 + getDataSize();\r
+ }\r
+\r
+\r
+ protected void fillFields(RecordInputStream in) {\r
+ _range = new CellRangeAddress8Bit(in);\r
+ _options = in.readUShort();\r
+ _field3notUsed = in.readInt();\r
+ int formulaLen = in.readUShort();\r
+ _formulaTokens = Ptg.readTokens(formulaLen, in);\r
+ }\r
+\r
+ public short getSid() {\r
+ return sid;\r
+ }\r
+\r
+ public String toString() {\r
+ StringBuffer sb = new StringBuffer();\r
+ sb.append(getClass().getName()).append(" [ARRAY]\n");\r
+ sb.append(" range=").append(_range.toString()).append("\n");\r
+ sb.append(" options=").append(HexDump.shortToHex(_options)).append("\n");\r
+ sb.append(" notUsed=").append(HexDump.intToHex(_field3notUsed)).append("\n");\r
+ sb.append(" formula:").append("\n");\r
+ for (int i = 0; i < _formulaTokens.length; i++) {\r
+ sb.append(_formulaTokens[i].toString());\r
+ }\r
+ sb.append("]");\r
+ return sb.toString();\r
+ }\r
+}\r
-
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
-
-/*
- * MulRKRecord.java
- *
- * Created on November 9, 2001, 4:53 PM
- */
package org.apache.poi.hssf.record;
-import java.util.ArrayList;
-
import org.apache.poi.hssf.util.RKUtil;
+import org.apache.poi.util.HexDump;
/**
+ * MULRK (0x00BD) <p/>
+ *
* Used to store multiple RK numbers on a row. 1 MulRk = Multiple Cell values.
* HSSF just converts this into multiple NUMBER records. READ-ONLY SUPPORT!<P>
* REFERENCE: PG 330 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 MulRKRecord
- extends Record
-{
- public final static short sid = 0xbd;
- //private short field_1_row;
- private int field_1_row;
- private short field_2_first_col;
- private ArrayList field_3_rks;
- private short field_4_last_col;
-
- /** Creates new MulRKRecord */
-
- public MulRKRecord()
- {
- }
-
- /**
- * Constructs a MulRK record and sets its fields appropriately.
- *
- * @param in the RecordInputstream to read the record from
- */
-
- public MulRKRecord(RecordInputStream in)
- {
- super(in);
- }
-
- //public short getRow()
- public int getRow()
- {
- return field_1_row;
- }
-
- /**
- * starting column (first cell this holds in the row)
- * @return first column number
- */
-
- public short getFirstColumn()
- {
- return field_2_first_col;
- }
-
- /**
- * ending column (last cell this holds in the row)
- * @return first column number
- */
-
- public short getLastColumn()
- {
- return field_4_last_col;
- }
-
- /**
- * get the number of columns this contains (last-first +1)
- * @return number of columns (last - first +1)
- */
-
- public int getNumColumns()
- {
- return field_4_last_col - field_2_first_col + 1;
- }
-
- /**
- * returns the xf index for column (coffset = column - field_2_first_col)
- * @return the XF index for the column
- */
-
- public short getXFAt(int coffset)
- {
- return (( RkRec ) field_3_rks.get(coffset)).xf;
- }
-
- /**
- * returns the rk number for column (coffset = column - field_2_first_col)
- * @return the value (decoded into a double)
- */
-
- public double getRKNumberAt(int coffset)
- {
- return RKUtil.decodeNumber((( RkRec ) field_3_rks.get(coffset)).rk);
- }
-
- /**
- * @param in the RecordInputstream to read the record from
- */
- protected void fillFields(RecordInputStream in)
- {
- //field_1_row = LittleEndian.getShort(data, 0 + offset);
- field_1_row = in.readUShort();
- field_2_first_col = in.readShort();
- field_3_rks = parseRKs(in);
- field_4_last_col = in.readShort();
- }
-
- private ArrayList parseRKs(RecordInputStream in)
- {
- ArrayList retval = new ArrayList();
- while ((in.remaining()-2) > 0) {
- RkRec rec = new RkRec();
-
- rec.xf = in.readShort();
- rec.rk = in.readInt();
- retval.add(rec);
- }
- return retval;
- }
-
- public String toString()
- {
- StringBuffer buffer = new StringBuffer();
-
- buffer.append("[MULRK]\n");
- buffer.append("firstcol = ")
- .append(Integer.toHexString(getFirstColumn())).append("\n");
- buffer.append(" lastcol = ")
- .append(Integer.toHexString(getLastColumn())).append("\n");
- for (int k = 0; k < getNumColumns(); k++)
- {
- buffer.append("xf").append(k).append(" = ")
- .append(Integer.toHexString(getXFAt(k))).append("\n");
- buffer.append("rk").append(k).append(" = ")
- .append(getRKNumberAt(k)).append("\n");
- }
- buffer.append("[/MULRK]\n");
- return buffer.toString();
- }
-
- /**
- * called by constructor, should throw runtime exception in the event of a
- * record passed with a differing ID.
- *
- * @param id alleged id for this record
- */
-
- protected void validateSid(short id)
- {
- if (id != sid)
- {
- throw new RecordFormatException("Not a MulRKRecord!");
- }
- }
-
- public short getSid()
- {
- return sid;
- }
-
- public int serialize(int offset, byte [] data)
- {
- throw new RecordFormatException(
- "Sorry, you can't serialize a MulRK in this release");
- }
-}
-
-class RkRec
-{
- public short xf;
- public int rk;
+public final class MulRKRecord extends Record {
+ public final static short sid = 0x00BD;
+
+ private int field_1_row;
+ private short field_2_first_col;
+ private RkRec[] field_3_rks;
+ private short field_4_last_col;
+
+ /**
+ * Constructs a MulRK record and sets its fields appropriately.
+ *
+ * @param in the RecordInputstream to read the record from
+ */
+ public MulRKRecord(RecordInputStream in) {
+ super(in);
+ }
+
+ public int getRow() {
+ return field_1_row;
+ }
+
+ /**
+ * starting column (first cell this holds in the row)
+ * @return first column number
+ */
+ public short getFirstColumn() {
+ return field_2_first_col;
+ }
+
+ /**
+ * ending column (last cell this holds in the row)
+ * @return first column number
+ */
+ public short getLastColumn() {
+ return field_4_last_col;
+ }
+
+ /**
+ * get the number of columns this contains (last-first +1)
+ * @return number of columns (last - first +1)
+ */
+ public int getNumColumns() {
+ return field_4_last_col - field_2_first_col + 1;
+ }
+
+ /**
+ * returns the xf index for column (coffset = column - field_2_first_col)
+ * @return the XF index for the column
+ */
+ public short getXFAt(int coffset) {
+ return field_3_rks[coffset].xf;
+ }
+
+ /**
+ * returns the rk number for column (coffset = column - field_2_first_col)
+ * @return the value (decoded into a double)
+ */
+ public double getRKNumberAt(int coffset) {
+ return RKUtil.decodeNumber(field_3_rks[coffset].rk);
+ }
+
+ /**
+ * @param in the RecordInputstream to read the record from
+ */
+ protected void fillFields(RecordInputStream in) {
+ field_1_row = in.readUShort();
+ field_2_first_col = in.readShort();
+ field_3_rks = RkRec.parseRKs(in);
+ field_4_last_col = in.readShort();
+ }
+
+
+ public String toString() {
+ StringBuffer buffer = new StringBuffer();
+
+ buffer.append("[MULRK]\n");
+ buffer.append(" .row = ").append(HexDump.shortToHex(getRow())).append("\n");
+ buffer.append(" .firstcol= ").append(HexDump.shortToHex(getFirstColumn())).append("\n");
+ buffer.append(" .lastcol = ").append(HexDump.shortToHex(getLastColumn())).append("\n");
+
+ for (int k = 0; k < getNumColumns(); k++) {
+ buffer.append(" xf[").append(k).append("] = ").append(HexDump.shortToHex(getXFAt(k))).append("\n");
+ buffer.append(" rk[").append(k).append("] = ").append(getRKNumberAt(k)).append("\n");
+ }
+ buffer.append("[/MULRK]\n");
+ return buffer.toString();
+ }
+
+ /**
+ * called by constructor, should throw runtime exception in the event of a
+ * record passed with a differing ID.
+ *
+ * @param id alleged id for this record
+ */
+
+ protected void validateSid(short id)
+ {
+ if (id != sid)
+ {
+ throw new RecordFormatException("Not a MulRKRecord!");
+ }
+ }
+
+ public short getSid()
+ {
+ return sid;
+ }
+
+ public int serialize(int offset, byte [] data)
+ {
+ throw new RecordFormatException(
+ "Sorry, you can't serialize a MulRK in this release");
+ }
+
+ private static final class RkRec {
+ public static final int ENCODED_SIZE = 6;
+ public final short xf;
+ public final int rk;
+
+ private RkRec(RecordInputStream in) {
+ xf = in.readShort();
+ rk = in.readInt();
+ }
+
+ public static RkRec[] parseRKs(RecordInputStream in) {
+ int nItems = (in.remaining()-2) / ENCODED_SIZE;
+ RkRec[] retval = new RkRec[nItems];
+ for (int i=0; i<nItems; i++) {
+ retval[i] = new RkRec(in);
+ }
+ return retval;
+ }
+ }
}
package org.apache.poi.hssf.record;
+import org.apache.poi.hssf.util.CellRangeAddress8Bit;
import org.apache.poi.util.LittleEndian;
/**
private int field_2_row_active_cell;
private int field_3_col_active_cell;
private int field_4_active_cell_ref_index;
- private Reference[] field_6_refs;
-
- /**
- * Note - column values are 8-bit so cannot use <tt>CellRangeAddressList</tt>
- */
- public class Reference {
- /* package */ static final int ENCODED_SIZE = 6;
- private int _firstRow;
- private int _lastRow;
- private int _firstCol;
- private int _lastCol;
-
- /* package */ Reference(int firstRow, int lastRow, int firstColumn, int lastColumn) {
- _firstRow = firstRow;
- _lastRow = lastRow;
- _firstCol = firstColumn;
- _lastCol = lastColumn;
- }
- /* package */ Reference(RecordInputStream in) {
- this(in.readUShort(), in.readUShort(), in.readUByte(), in.readUByte());
- }
- public void serialize(int offset, byte[] data) {
- LittleEndian.putUShort(data, offset + 0, _firstRow);
- LittleEndian.putUShort(data, offset + 2, _lastRow);
- LittleEndian.putByte(data, offset + 4, _firstCol);
- LittleEndian.putByte(data, offset + 6, _lastCol);
- }
-
- public int getFirstRow() {
- return _firstRow;
- }
- public int getLastRow() {
- return _lastRow;
- }
- public int getFirstColumn() {
- return _firstCol;
- }
- public int getLastColumn() {
- return _lastCol;
- }
- }
+ private CellRangeAddress8Bit[] field_6_refs;
/**
* Creates a default selection record (cell A1, in pane ID 3)
*/
public SelectionRecord(int activeCellRow, int activeCellCol) {
- field_1_pane = 3; // pane id 3 is always present. see OOO sec 5.75 'PANE'
- field_2_row_active_cell = activeCellRow;
- field_3_col_active_cell = activeCellCol;
- field_4_active_cell_ref_index = 0;
- field_6_refs = new Reference[] {
- new Reference(activeCellRow, activeCellRow, activeCellCol, activeCellCol),
- };
+ field_1_pane = 3; // pane id 3 is always present. see OOO sec 5.75 'PANE'
+ field_2_row_active_cell = activeCellRow;
+ field_3_col_active_cell = activeCellCol;
+ field_4_active_cell_ref_index = 0;
+ field_6_refs = new CellRangeAddress8Bit[] {
+ new CellRangeAddress8Bit(activeCellRow, activeCellRow, activeCellCol, activeCellCol),
+ };
}
/**
* Constructs a Selection record and sets its fields appropriately.
* @param in the RecordInputstream to read the record from
*/
-
public SelectionRecord(RecordInputStream in) {
super(in);
}
field_4_active_cell_ref_index = in.readShort();
int field_5_num_refs = in.readUShort();
- field_6_refs = new Reference[field_5_num_refs];
+ field_6_refs = new CellRangeAddress8Bit[field_5_num_refs];
for (int i = 0; i < field_6_refs.length; i++) {
- field_6_refs[i] = new Reference(in);
- }
+ field_6_refs[i] = new CellRangeAddress8Bit(in);
+ }
}
/**
return (short)field_4_active_cell_ref_index;
}
- public String toString()
- {
+ public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("[SELECTION]\n");
return buffer.toString();
}
private int getDataSize() {
- return 9 // 1 byte + 4 shorts
- + field_6_refs.length * Reference.ENCODED_SIZE;
+ return 9 // 1 byte + 4 shorts
+ + CellRangeAddress8Bit.getEncodedSize(field_6_refs.length);
}
public int serialize(int offset, byte [] data) {
- int dataSize = getDataSize();
+ int dataSize = getDataSize();
LittleEndian.putUShort(data, 0 + offset, sid);
LittleEndian.putUShort(data, 2 + offset, dataSize);
LittleEndian.putByte(data, 4 + offset, getPane());
int nRefs = field_6_refs.length;
LittleEndian.putUShort(data, 11 + offset, nRefs);
for (int i = 0; i < field_6_refs.length; i++) {
- Reference r = field_6_refs[i];
- r.serialize(offset + 13 + i * Reference.ENCODED_SIZE, data);
- }
+ CellRangeAddress8Bit r = field_6_refs[i];
+ r.serialize(offset + 13 + i * CellRangeAddress8Bit.ENCODED_SIZE, data);
+ }
return 4 + dataSize;
}
package org.apache.poi.hssf.record;
-import java.util.List;
-import java.util.Stack;
-
import org.apache.poi.hssf.record.formula.AreaNPtg;
import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.RefNPtg;
import org.apache.poi.hssf.record.formula.RefPtg;
+import org.apache.poi.hssf.util.CellRangeAddress8Bit;
+import org.apache.poi.util.HexDump;
/**
- * Title: SharedFormulaRecord
+ * Title: SHAREDFMLA (0x04BC) SharedFormulaRecord
* Description: Primarily used as an excel optimization so that multiple similar formulas
* are not written out too many times. We should recognize this record and
* serialize as is since this is used when reading templates.
public final class SharedFormulaRecord extends Record {
public final static short sid = 0x04BC;
- private int field_1_first_row;
- private int field_2_last_row;
- private short field_3_first_column;
- private short field_4_last_column;
- private int field_5_reserved;
- private short field_6_expression_len;
- private Stack field_7_parsed_expr;
+ private CellRangeAddress8Bit _range;
+ private int field_5_reserved;
+ private Ptg[] field_7_parsed_expr;
- public SharedFormulaRecord()
- {
+ public SharedFormulaRecord() {
+ _range = new CellRangeAddress8Bit(0, 0, 0, 0);
+ field_7_parsed_expr = Ptg.EMPTY_PTG_ARRAY;
}
/**
* @param in the RecordInputstream to read the record from
*/
-
- public SharedFormulaRecord(RecordInputStream in)
- {
+ public SharedFormulaRecord(RecordInputStream in) {
super(in);
}
- protected void validateSid(short id)
- {
- if (id != this.sid)
- {
+ protected void validateSid(short id) {
+ if (id != this.sid) {
throw new RecordFormatException("Not a valid SharedFormula");
}
}
public int getFirstRow() {
- return field_1_first_row;
+ return _range.getFirstRow();
}
public int getLastRow() {
- return field_2_last_row;
+ return _range.getLastRow();
}
public short getFirstColumn() {
- return field_3_first_column;
+ return (short) _range.getFirstColumn();
}
public short getLastColumn() {
- return field_4_last_column;
- }
-
- public short getExpressionLength()
- {
- return field_6_expression_len;
+ return (short) _range.getLastColumn();
}
/**
- * spit the record out AS IS. no interperatation or identification
+ * spit the record out AS IS. no interpretation or identification
*/
public int serialize(int offset, byte [] data)
{
StringBuffer buffer = new StringBuffer();
- buffer.append("[SHARED FORMULA RECORD:" + Integer.toHexString(sid) + "]\n");
- buffer.append(" .id = ").append(Integer.toHexString(sid))
- .append("\n");
- buffer.append(" .first_row = ")
- .append(Integer.toHexString(getFirstRow())).append("\n");
- buffer.append(" .last_row = ")
- .append(Integer.toHexString(getLastRow()))
- .append("\n");
- buffer.append(" .first_column = ")
- .append(Integer.toHexString(getFirstColumn())).append("\n");
- buffer.append(" .last_column = ")
- .append(Integer.toHexString(getLastColumn()))
- .append("\n");
- buffer.append(" .reserved = ")
- .append(Integer.toHexString(field_5_reserved))
- .append("\n");
- buffer.append(" .expressionlength= ").append(getExpressionLength())
- .append("\n");
-
- buffer.append(" .numptgsinarray = ").append(field_7_parsed_expr.size())
- .append("\n");
-
- for (int k = 0; k < field_7_parsed_expr.size(); k++ ) {
- buffer.append("Formula ")
- .append(k)
- .append("\n")
- .append(field_7_parsed_expr.get(k).toString())
- .append("\n");
+ buffer.append("[SHARED FORMULA (").append(HexDump.intToHex(sid)).append("]\n");
+ buffer.append(" .range = ").append(_range.toString()).append("\n");
+ buffer.append(" .reserved = ").append(HexDump.shortToHex(field_5_reserved)).append("\n");
+
+ for (int k = 0; k < field_7_parsed_expr.length; k++ ) {
+ buffer.append("Formula[").append(k).append("]");
+ buffer.append(field_7_parsed_expr[k].toString()).append("\n");
}
- buffer.append("[/SHARED FORMULA RECORD]\n");
+ buffer.append("[/SHARED FORMULA]\n");
return buffer.toString();
}
- public short getSid()
- {
+ public short getSid() {
return sid;
}
- protected void fillFields(RecordInputStream in)
- {
- field_1_first_row = in.readUShort();
- field_2_last_row = in.readUShort();
- field_3_first_column = in.readUByte();
- field_4_last_column = in.readUByte();
- field_5_reserved = in.readShort();
- field_6_expression_len = in.readShort();
- field_7_parsed_expr = getParsedExpressionTokens(in);
- }
-
- private Stack getParsedExpressionTokens(RecordInputStream in)
- {
- Stack stack = new Stack();
-
- while (in.remaining() != 0) {
- Ptg ptg = Ptg.createPtg(in);
- stack.push(ptg);
- }
- return stack;
+ protected void fillFields(RecordInputStream in) {
+ _range = new CellRangeAddress8Bit(in);
+ field_5_reserved = in.readShort();
+ int field_6_expression_len = in.readShort();
+ field_7_parsed_expr = Ptg.readTokens(field_6_expression_len, in);
}
/**
* Creates a non shared formula from the shared formula
* counter part
*/
- protected static Stack convertSharedFormulas(Stack ptgs, int formulaRow, int formulaColumn) {
+ protected static Ptg[] convertSharedFormulas(Ptg[] ptgs, int formulaRow, int formulaColumn) {
if(false) {
/*
* TODO - (May-2008) Stop converting relative ref Ptgs in shared formula records.
*/
return ptgs;
}
- Stack newPtgStack = new Stack();
+ Ptg[] newPtgStack = new Ptg[ptgs.length];
- if (ptgs != null)
- for (int k = 0; k < ptgs.size(); k++) {
- Ptg ptg = (Ptg) ptgs.get(k);
+ for (int k = 0; k < ptgs.length; k++) {
+ Ptg ptg = ptgs[k];
byte originalOperandClass = -1;
if (!ptg.isBaseToken()) {
originalOperandClass = ptg.getPtgClass();
areaNPtg.isLastRowRelative(),
areaNPtg.isFirstColRelative(),
areaNPtg.isLastColRelative());
+ } else {
+ if (false) {// do we need a ptg clone here?
+ ptg = ptg.copy();
+ }
}
if (!ptg.isBaseToken()) {
ptg.setClass(originalOperandClass);
}
- newPtgStack.add(ptg);
+ newPtgStack[k] = ptg;
}
return newPtgStack;
}
final int formulaRow = formula.getRow();
final int formulaColumn = formula.getColumn();
- List ptgList = convertSharedFormulas(field_7_parsed_expr, formulaRow, formulaColumn);
- Ptg[] ptgs = new Ptg[ptgList.size()];
- ptgList.toArray(ptgs);
+ Ptg[] ptgs = convertSharedFormulas(field_7_parsed_expr, formulaRow, formulaColumn);
formula.setParsedExpression(ptgs);
//Now its not shared!
formula.setSharedFormula(false);
package org.apache.poi.hssf.record;
import org.apache.poi.hssf.record.formula.TblPtg;
+import org.apache.poi.hssf.util.CellRangeAddress8Bit;
+import org.apache.poi.hssf.util.CellReference;
import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory;
+import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian;
/**
+ * DATATABLE (0x0236)<p/>
+ *
* TableRecord - The record specifies a data table.
* This record is preceded by a single Formula record that
* defines the first cell in the data table, which should
* only contain a single Ptg, {@link TblPtg}.
- *
+ *
* See p536 of the June 08 binary docs
*/
public final class TableRecord extends Record {
- public static final short sid = 566;
-
- private static final BitField alwaysCalc = BitFieldFactory.getInstance(0x0001);
- private static final BitField reserved1 = BitFieldFactory.getInstance(0x0002);
- private static final BitField rowOrColInpCell = BitFieldFactory.getInstance(0x0004);
- private static final BitField oneOrTwoVar = BitFieldFactory.getInstance(0x0008);
- private static final BitField rowDeleted = BitFieldFactory.getInstance(0x0010);
- private static final BitField colDeleted = BitFieldFactory.getInstance(0x0020);
- private static final BitField reserved2 = BitFieldFactory.getInstance(0x0040);
- private static final BitField reserved3 = BitFieldFactory.getInstance(0x0080);
-
- private short field_1_ref_rowFirst;
- private short field_2_ref_rowLast;
- private short field_3_ref_colFirst;
- private short field_4_ref_colLast;
-
- private byte field_5_flags;
- private byte field_6_res;
- private short field_7_rowInputRow;
- private short field_8_colInputRow;
- private short field_9_rowInputCol;
- private short field_10_colInputCol;
-
+ public static final short sid = 0x0236;
+
+ private static final BitField alwaysCalc = BitFieldFactory.getInstance(0x0001);
+ private static final BitField reserved1 = BitFieldFactory.getInstance(0x0002);
+ private static final BitField rowOrColInpCell = BitFieldFactory.getInstance(0x0004);
+ private static final BitField oneOrTwoVar = BitFieldFactory.getInstance(0x0008);
+ private static final BitField rowDeleted = BitFieldFactory.getInstance(0x0010);
+ private static final BitField colDeleted = BitFieldFactory.getInstance(0x0020);
+ private static final BitField reserved2 = BitFieldFactory.getInstance(0x0040);
+ private static final BitField reserved3 = BitFieldFactory.getInstance(0x0080);
+
+ private CellRangeAddress8Bit _range;
+
+ private int field_5_flags;
+ private int field_6_res;
+ private int field_7_rowInputRow;
+ private int field_8_colInputRow;
+ private int field_9_rowInputCol;
+ private int field_10_colInputCol;
+
protected void fillFields(RecordInputStream in) {
- field_1_ref_rowFirst = in.readShort();
- field_2_ref_rowLast = in.readShort();
- field_3_ref_colFirst = in.readUByte();
- field_4_ref_colLast = in.readUByte();
+ _range = new CellRangeAddress8Bit(in);
field_5_flags = in.readByte();
field_6_res = in.readByte();
field_7_rowInputRow = in.readShort();
field_9_rowInputCol = in.readShort();
field_10_colInputCol = in.readShort();
}
-
- public TableRecord(RecordInputStream in) {
- super(in);
- }
- public TableRecord() {
- super();
- }
-
-
- public short getRowFirst() {
- return field_1_ref_rowFirst;
- }
- public void setRowFirst(short field_1_ref_rowFirst) {
- this.field_1_ref_rowFirst = field_1_ref_rowFirst;
- }
- public short getRowLast() {
- return field_2_ref_rowLast;
+ public TableRecord(RecordInputStream in) {
+ super(in);
}
- public void setRowLast(short field_2_ref_rowLast) {
- this.field_2_ref_rowLast = field_2_ref_rowLast;
+ public TableRecord(CellRangeAddress8Bit range) {
+ _range = range;
+ field_6_res = 0;
}
- public short getColFirst() {
- return field_3_ref_colFirst;
- }
- public void setColFirst(short field_3_ref_colFirst) {
- this.field_3_ref_colFirst = field_3_ref_colFirst;
- }
-
- public short getColLast() {
- return field_4_ref_colLast;
- }
- public void setColLast(short field_4_ref_colLast) {
- this.field_4_ref_colLast = field_4_ref_colLast;
+ public CellRangeAddress8Bit getRange() {
+ return _range;
}
- public byte getFlags() {
+ public int getFlags() {
return field_5_flags;
}
- public void setFlags(byte field_5_flags) {
- this.field_5_flags = field_5_flags;
- }
-
- public byte getReserved() {
- return field_6_res;
- }
- public void setReserved(byte field_6_res) {
- this.field_6_res = field_6_res;
+ public void setFlags(int flags) {
+ field_5_flags = flags;
}
- public short getRowInputRow() {
+ public int getRowInputRow() {
return field_7_rowInputRow;
}
- public void setRowInputRow(short field_7_rowInputRow) {
- this.field_7_rowInputRow = field_7_rowInputRow;
+ public void setRowInputRow(int rowInputRow) {
+ field_7_rowInputRow = rowInputRow;
}
- public short getColInputRow() {
+ public int getColInputRow() {
return field_8_colInputRow;
}
- public void setColInputRow(short field_8_colInputRow) {
- this.field_8_colInputRow = field_8_colInputRow;
+ public void setColInputRow(int colInputRow) {
+ field_8_colInputRow = colInputRow;
}
- public short getRowInputCol() {
+ public int getRowInputCol() {
return field_9_rowInputCol;
}
- public void setRowInputCol(short field_9_rowInputCol) {
- this.field_9_rowInputCol = field_9_rowInputCol;
+ public void setRowInputCol(int rowInputCol) {
+ field_9_rowInputCol = rowInputCol;
}
- public short getColInputCol() {
+ public int getColInputCol() {
return field_10_colInputCol;
}
- public void setColInputCol(short field_10_colInputCol) {
- this.field_10_colInputCol = field_10_colInputCol;
+ public void setColInputCol(int colInputCol) {
+ field_10_colInputCol = colInputCol;
}
-
-
+
+
public boolean isAlwaysCalc() {
return alwaysCalc.isSet(field_5_flags);
}
public void setAlwaysCalc(boolean flag) {
- field_5_flags = alwaysCalc.setByteBoolean(field_5_flags, flag);
+ field_5_flags = alwaysCalc.setBoolean(field_5_flags, flag);
}
-
+
public boolean isRowOrColInpCell() {
return rowOrColInpCell.isSet(field_5_flags);
}
public void setRowOrColInpCell(boolean flag) {
- field_5_flags = rowOrColInpCell.setByteBoolean(field_5_flags, flag);
+ field_5_flags = rowOrColInpCell.setBoolean(field_5_flags, flag);
}
-
+
public boolean isOneNotTwoVar() {
return oneOrTwoVar.isSet(field_5_flags);
}
public void setOneNotTwoVar(boolean flag) {
- field_5_flags = oneOrTwoVar.setByteBoolean(field_5_flags, flag);
+ field_5_flags = oneOrTwoVar.setBoolean(field_5_flags, flag);
}
-
+
public boolean isColDeleted() {
return colDeleted.isSet(field_5_flags);
}
public void setColDeleted(boolean flag) {
- field_5_flags = colDeleted.setByteBoolean(field_5_flags, flag);
+ field_5_flags = colDeleted.setBoolean(field_5_flags, flag);
}
-
+
public boolean isRowDeleted() {
return rowDeleted.isSet(field_5_flags);
}
public void setRowDeleted(boolean flag) {
- field_5_flags = rowDeleted.setByteBoolean(field_5_flags, flag);
+ field_5_flags = rowDeleted.setBoolean(field_5_flags, flag);
}
-
+
public short getSid() {
return sid;
}
public int serialize(int offset, byte[] data) {
- LittleEndian.putShort(data, 0 + offset, sid);
- LittleEndian.putShort(data, 2 + offset, ( short ) (16));
-
- LittleEndian.putShort(data, 4 + offset, field_1_ref_rowFirst);
- LittleEndian.putShort(data, 6 + offset, field_2_ref_rowLast);
- LittleEndian.putByte(data, 8 + offset, field_3_ref_colFirst);
- LittleEndian.putByte(data, 9 + offset, field_4_ref_colLast);
- LittleEndian.putByte(data, 10 + offset, field_5_flags);
- LittleEndian.putByte(data, 11 + offset, field_6_res);
- LittleEndian.putShort(data, 12 + offset, field_7_rowInputRow);
- LittleEndian.putShort(data, 14 + offset, field_8_colInputRow);
- LittleEndian.putShort(data, 16 + offset, field_9_rowInputCol);
- LittleEndian.putShort(data, 18 + offset, field_10_colInputCol);
-
- return getRecordSize();
+ int dataSize = getDataSize();
+ LittleEndian.putShort(data, 0 + offset, sid);
+ LittleEndian.putUShort(data, 2 + offset, dataSize);
+
+ _range.serialize(4 + offset, data);
+ LittleEndian.putByte(data, 10 + offset, field_5_flags);
+ LittleEndian.putByte(data, 11 + offset, field_6_res);
+ LittleEndian.putUShort(data, 12 + offset, field_7_rowInputRow);
+ LittleEndian.putUShort(data, 14 + offset, field_8_colInputRow);
+ LittleEndian.putUShort(data, 16 + offset, field_9_rowInputCol);
+ LittleEndian.putUShort(data, 18 + offset, field_10_colInputCol);
+
+ return 4 + dataSize;
+ }
+ private int getDataSize() {
+ return CellRangeAddress8Bit.ENCODED_SIZE
+ + 2 // 2 byte fields
+ + 8; // 4 short fields
}
+
public int getRecordSize() {
- return 4+16;
+ return 4+getDataSize();
}
-
+
protected void validateSid(short id) {
- if (id != sid)
- {
- throw new RecordFormatException("NOT A TABLE RECORD");
- }
- }
-
- public String toString()
- {
- StringBuffer buffer = new StringBuffer();
- buffer.append("[TABLE]\n");
- buffer.append(" .row from = ")
- .append(Integer.toHexString(field_1_ref_rowFirst)).append("\n");
- buffer.append(" .row to = ")
- .append(Integer.toHexString(field_2_ref_rowLast)).append("\n");
- buffer.append(" .column from = ")
- .append(Integer.toHexString(field_3_ref_colFirst)).append("\n");
- buffer.append(" .column to = ")
- .append(Integer.toHexString(field_4_ref_colLast)).append("\n");
-
- buffer.append(" .flags = ")
- .append(Integer.toHexString(field_5_flags)).append("\n");
- buffer.append(" .always calc =")
- .append(isAlwaysCalc()).append("\n");
-
- buffer.append(" .reserved = ")
- .append(Integer.toHexString(field_6_res)).append("\n");
- buffer.append(" .row input row = ")
- .append(Integer.toHexString(field_7_rowInputRow)).append("\n");
- buffer.append(" .col input row = ")
- .append(Integer.toHexString(field_8_colInputRow)).append("\n");
- buffer.append(" .row input col = ")
- .append(Integer.toHexString(field_9_rowInputCol)).append("\n");
- buffer.append(" .col input col = ")
- .append(Integer.toHexString(field_10_colInputCol)).append("\n");
- buffer.append("[/TABLE]\n");
- return buffer.toString();
- }
+ if (id != sid)
+ {
+ throw new RecordFormatException("NOT A TABLE RECORD");
+ }
+ }
+
+ public String toString() {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("[TABLE]\n");
+ buffer.append(" .range = ").append(_range.toString()).append("\n");
+ buffer.append(" .flags = ") .append(HexDump.byteToHex(field_5_flags)).append("\n");
+ buffer.append(" .alwaysClc= ").append(isAlwaysCalc()).append("\n");
+ buffer.append(" .reserved = ").append(HexDump.intToHex(field_6_res)).append("\n");
+ CellReference crRowInput = cr(field_7_rowInputRow, field_8_colInputRow);
+ CellReference crColInput = cr(field_9_rowInputCol, field_10_colInputCol);
+ buffer.append(" .rowInput = ").append(crRowInput.formatAsString()).append("\n");
+ buffer.append(" .colInput = ").append(crColInput.formatAsString()).append("\n");
+ buffer.append("[/TABLE]\n");
+ return buffer.toString();
+ }
+
+ private static CellReference cr(int rowIx, int colIxAndFlags) {
+ int colIx = colIxAndFlags & 0x00FF;
+ boolean isRowAbs = (colIxAndFlags & 0x8000) == 0;
+ boolean isColAbs = (colIxAndFlags & 0x4000) == 0;
+ return new CellReference(rowIx, colIx, isRowAbs, isColAbs);
+ }
}
public abstract class Ptg implements Cloneable {
public static final Ptg[] EMPTY_PTG_ARRAY = { };
- /* convert infix order ptg list to rpn order ptg list
- * @return List ptgs in RPN order
- * @param infixPtgs List of ptgs in infix order
- */
-
- /* DO NOT REMOVE
- *we keep this method in case we wish to change the way we parse
- *It needs a getPrecedence in OperationsPtg
-
- public static List ptgsToRpn(List infixPtgs) {
- java.util.Stack operands = new java.util.Stack();
- java.util.List retval = new java.util.Stack();
-
- java.util.ListIterator i = infixPtgs.listIterator();
- Object p;
- OperationPtg o ;
- boolean weHaveABracket = false;
- while (i.hasNext()) {
- p=i.next();
- if (p instanceof OperationPtg) {
- if (p instanceof ParenthesisPtg) {
- if (!weHaveABracket) {
- operands.push(p);
- weHaveABracket = true;
- } else {
- o = (OperationPtg) operands.pop();
- while (!(o instanceof ParenthesisPtg)) {
- retval.add(o);
- }
- weHaveABracket = false;
- }
- } else {
-
- while (!operands.isEmpty() && ((OperationPtg) operands.peek()).getPrecedence() >= ((OperationPtg) p).getPrecedence() ) { //TODO handle ^ since it is right associative
- retval.add(operands.pop());
- }
- operands.push(p);
- }
- } else {
- retval.add(p);
- }
- }
- while (!operands.isEmpty()) {
- if (operands.peek() instanceof ParenthesisPtg ){
- //throw some error
- } else {
- retval.add(operands.pop());
- }
- }
- return retval;
- }
- */
/**
* Reads <tt>size</tt> bytes of the input stream, to create an array of <tt>Ptg</tt>s.
Ptg retval = createClassifiedPtg(id, in);
- if (id > 0x60) {
+ if (id >= 0x60) {
retval.setClass(CLASS_ARRAY);
- } else if (id > 0x40) {
+ } else if (id >= 0x40) {
retval.setClass(CLASS_VALUE);
} else {
retval.setClass(CLASS_REF);
}
- return retval;
+ return retval;
}
private static Ptg createClassifiedPtg(byte id, RecordInputStream in) {
public final byte getPtgClass() {
return ptgClass;
}
+
+ /**
+ * Debug / diagnostic method to get this token's 'operand class' type.
+ * @return 'R' for 'reference', 'V' for 'value', 'A' for 'array' and '.' for base tokens
+ */
+ public final char getRVAType() {
+ if (isBaseToken()) {
+ return '.';
+ }
+ switch (ptgClass) {
+ case Ptg.CLASS_REF: return 'R';
+ case Ptg.CLASS_VALUE: return 'V';
+ case Ptg.CLASS_ARRAY: return 'A';
+ }
+ throw new RuntimeException("Unknown operand class (" + ptgClass + ")");
+ }
public abstract byte getDefaultOperandClass();
public static String stripFields(String text) {
int pos;
+ // Check we really got something to work on
+ if(text == null || text.length() == 0) {
+ return text;
+ }
+
// Firstly, do the easy ones which are static
for(int i=0; i<Field.ALL_FIELDS.size(); i++) {
String seq = ((Field)Field.ALL_FIELDS.get(i)).sequence;
}
// Now do the tricky, dynamic ones
+ // These are things like font sizes and font names
text = text.replaceAll("\\&\\d+", "");
text = text.replaceAll("\\&\".*?,.*?\"", "");
public static final Field TIME_FIELD = new Field("&T");
public static final Field NUM_PAGES_FIELD = new Field("&N");
- public static final Field PICTURE_FIELD = new Field("&P");
+ public static final Field PICTURE_FIELD = new Field("&G");
- public static final PairField BOLD_FIELD = new PairField("&B"); // PAID
+ public static final PairField BOLD_FIELD = new PairField("&B");
public static final PairField ITALIC_FIELD = new PairField("&I");
public static final PairField STRIKETHROUGH_FIELD = new PairField("&S");
public static final PairField SUBSCRIPT_FIELD = new PairField("&Y");
* @author Dragos Buleandra (dragos.buleandra@trade2b.ro)\r
*/\r
public class CellRangeAddress extends org.apache.poi.ss.util.CellRangeAddress {\r
+ /*\r
+ * TODO - replace org.apache.poi.hssf.util.Region\r
+ */\r
+ public static final int ENCODED_SIZE = 8;\r
+\r
public CellRangeAddress(int firstRow, int lastRow, int firstCol, int lastCol) {\r
super(firstRow, lastRow, firstCol, lastCol);\r
}\r
- public CellRangeAddress() {\r
- super();\r
- }\r
public CellRangeAddress(RecordInputStream in) {\r
+ super(readUShortAndCheck(in), in.readUShort(), in.readUShort(), in.readUShort());\r
+ }\r
+\r
+ private static int readUShortAndCheck(RecordInputStream in) {\r
if (in.remaining() < ENCODED_SIZE) {\r
// Ran out of data\r
throw new RuntimeException("Ran out of data reading CellRangeAddress");\r
- } \r
- _firstRow = in.readUShort();\r
- _lastRow = in.readUShort();\r
- _firstCol = in.readUShort();\r
- _lastCol = in.readUShort();\r
+ }\r
+ return in.readUShort();\r
}\r
}\r
--- /dev/null
+/* ====================================================================\r
+ Copyright 2002-2004 Apache Software Foundation\r
+\r
+ Licensed under the Apache License, Version 2.0 (the "License");\r
+ you may not use this file except in compliance with the License.\r
+ You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.hssf.util;\r
+\r
+import org.apache.poi.hssf.record.RecordInputStream;\r
+import org.apache.poi.ss.util.CellRangeAddressBase;\r
+import org.apache.poi.util.LittleEndian;\r
+\r
+/**\r
+ * See OOO documentation: excelfileformat.pdf sec 2.5.14 - 'Cell Range Address'<p/>\r
+ * \r
+ * Like {@link CellRangeAddress} except column fields are 8-bit.\r
+ * \r
+ * @author Josh Micich\r
+ */\r
+public final class CellRangeAddress8Bit extends CellRangeAddressBase {\r
+\r
+ public static final int ENCODED_SIZE = 6;\r
+\r
+ public CellRangeAddress8Bit(int firstRow, int lastRow, int firstCol, int lastCol) {\r
+ super(firstRow, lastRow, firstCol, lastCol);\r
+ }\r
+\r
+ public CellRangeAddress8Bit(RecordInputStream in) {\r
+ super(readUShortAndCheck(in), in.readUShort(), in.readUByte(), in.readUByte());\r
+ }\r
+\r
+ private static int readUShortAndCheck(RecordInputStream in) {\r
+ if (in.remaining() < ENCODED_SIZE) {\r
+ // Ran out of data\r
+ throw new RuntimeException("Ran out of data reading CellRangeAddress");\r
+ }\r
+ return in.readUShort();\r
+ }\r
+\r
+ public int serialize(int offset, byte[] data) {\r
+ LittleEndian.putUShort(data, offset + 0, getFirstRow());\r
+ LittleEndian.putUShort(data, offset + 2, getLastRow());\r
+ LittleEndian.putByte(data, offset + 4, getFirstColumn());\r
+ LittleEndian.putByte(data, offset + 5, getLastColumn());\r
+ return ENCODED_SIZE;\r
+ }\r
+ \r
+ public CellRangeAddress8Bit copy() {\r
+ return new CellRangeAddress8Bit(getFirstRow(), getLastRow(), getFirstColumn(), getLastColumn());\r
+ }\r
+\r
+ public static int getEncodedSize(int numberOfItems) {\r
+ return numberOfItems * ENCODED_SIZE;\r
+ }\r
+}\r
* Note - {@link SelectionRecord} uses the BIFF5 version of this structure
* @author Dragos Buleandra (dragos.buleandra@trade2b.ro)
*/
-public class CellRangeAddress {
+public class CellRangeAddress extends CellRangeAddressBase {
/*
* TODO - replace org.apache.poi.hssf.util.Region
*/
public static final int ENCODED_SIZE = 8;
- /** max 65536 rows in BIFF8 */
- public static final int LAST_ROW_INDEX = 0x00FFFF;
- /** max 256 columns in BIFF8 */
- public static final int LAST_COLUMN_INDEX = 0x00FF;
-
-
- protected int _firstRow;
- protected int _firstCol;
- protected int _lastRow;
- protected int _lastCol;
-
- protected CellRangeAddress() {}
public CellRangeAddress(int firstRow, int lastRow, int firstCol, int lastCol) {
- if(!isValid(firstRow, lastRow, firstCol, lastCol)) {
- throw new IllegalArgumentException("invalid cell range (" + firstRow + ", " + lastRow
- + ", " + firstCol + ", " + lastCol + ")");
- }
- _firstRow = firstRow;
- _lastRow = convertM1ToMax(lastRow, LAST_ROW_INDEX);
- _firstCol = firstCol;
- _lastCol = convertM1ToMax(lastCol, LAST_COLUMN_INDEX);
- }
-
- private static boolean isValid(int firstRow, int lastRow, int firstColumn, int lastColumn)
- {
- if(lastRow < 0 || lastRow > LAST_ROW_INDEX) {
- return false;
- }
- if(firstRow < 0 || firstRow > LAST_ROW_INDEX) {
- return false;
- }
-
- if(lastColumn < 0 || lastColumn > LAST_COLUMN_INDEX) {
- return false;
- }
- if(firstColumn < 0 || firstColumn > LAST_COLUMN_INDEX) {
- return false;
- }
- return true;
- }
- /**
- * Range arithmetic is easier when using a large positive number for 'max row or column'
- * instead of <tt>-1</tt>.
- */
- private static int convertM1ToMax(int lastIx, int maxIndex) {
- if(lastIx < 0) {
- return maxIndex;
- }
- return lastIx;
+ super(firstRow, lastRow, firstCol, lastCol);
}
- public boolean isFullColumnRange() {
- return _firstRow == 0 && _lastRow == LAST_ROW_INDEX;
- }
- public boolean isFullRowRange() {
- return _firstCol == 0 && _lastCol == LAST_COLUMN_INDEX;
- }
-
- /**
- * @return column number for the upper left hand corner
- */
- public int getFirstColumn() {
- return _firstCol;
- }
-
- /**
- * @return row number for the upper left hand corner
- */
- public int getFirstRow() {
- return _firstRow;
- }
-
- /**
- * @return column number for the lower right hand corner
- */
- public int getLastColumn() {
- return _lastCol;
- }
-
- /**
- * @return row number for the lower right hand corner
- */
- public int getLastRow() {
- return _lastRow;
- }
-
- /**
- * @param _firstCol column number for the upper left hand corner
- */
- public void setFirstColumn(int firstCol) {
- _firstCol = firstCol;
- }
-
- /**
- * @param rowFrom row number for the upper left hand corner
- */
- public void setFirstRow(int firstRow) {
- _firstRow = firstRow;
- }
-
- /**
- * @param colTo column number for the lower right hand corner
- */
- public void setLastColumn(int lastCol) {
- _lastCol = lastCol;
- }
-
- /**
- * @param rowTo row number for the lower right hand corner
- */
- public void setLastRow(int lastRow) {
- _lastRow = lastRow;
+ public int serialize(int offset, byte[] data) {
+ LittleEndian.putUShort(data, offset + 0, getFirstRow());
+ LittleEndian.putUShort(data, offset + 2, getLastRow());
+ LittleEndian.putUShort(data, offset + 4, getFirstColumn());
+ LittleEndian.putUShort(data, offset + 6, getLastColumn());
+ return ENCODED_SIZE;
}
public CellRangeAddress copy() {
- return new CellRangeAddress(_firstRow, _lastRow, _firstCol, _lastCol);
+ return new CellRangeAddress(getFirstRow(), getLastRow(), getFirstColumn(), getLastColumn());
}
public static int getEncodedSize(int numberOfItems) {
return numberOfItems * ENCODED_SIZE;
}
-
- public String toString() {
- return getClass().getName() + " ["+_firstRow+", "+_lastRow+", "+_firstCol+", "+_lastCol+"]";
- }
-
- public int serialize(int offset, byte[] data) {
- LittleEndian.putUShort(data, offset + 0, _firstRow);
- LittleEndian.putUShort(data, offset + 2, _lastRow);
- LittleEndian.putUShort(data, offset + 4, _firstCol);
- LittleEndian.putUShort(data, offset + 6, _lastCol);
- return ENCODED_SIZE;
- }
}
--- /dev/null
+/* ====================================================================
+ Copyright 2002-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.ss.util;
+
+
+/**
+ * See OOO documentation: excelfileformat.pdf sec 2.5.14 - 'Cell Range Address'<p/>
+ *
+ * Common subclass of 8-bit and 16-bit versions
+ *
+ * @author Josh Micich
+ */
+public abstract class CellRangeAddressBase {
+
+ /** max 65536 rows in BIFF8 */
+ private static final int LAST_ROW_INDEX = 0x00FFFF;
+ /** max 256 columns in BIFF8 */
+ private static final int LAST_COLUMN_INDEX = 0x00FF;
+
+ private int _firstRow;
+ private int _firstCol;
+ private int _lastRow;
+ private int _lastCol;
+
+ protected CellRangeAddressBase(int firstRow, int lastRow, int firstCol, int lastCol) {
+ if(!isValid(firstRow, lastRow, firstCol, lastCol)) {
+ throw new IllegalArgumentException("invalid cell range (" + firstRow + ", " + lastRow
+ + ", " + firstCol + ", " + lastCol + ")");
+ }
+ _firstRow = firstRow;
+ _lastRow =lastRow;
+ _firstCol = firstCol;
+ _lastCol = lastCol;
+ }
+ private static boolean isValid(int firstRow, int lastRow, int firstColumn, int lastColumn)
+ {
+ if(lastRow < 0 || lastRow > LAST_ROW_INDEX) {
+ return false;
+ }
+ if(firstRow < 0 || firstRow > LAST_ROW_INDEX) {
+ return false;
+ }
+
+ if(lastColumn < 0 || lastColumn > LAST_COLUMN_INDEX) {
+ return false;
+ }
+ if(firstColumn < 0 || firstColumn > LAST_COLUMN_INDEX) {
+ return false;
+ }
+ return true;
+ }
+
+
+ public final boolean isFullColumnRange() {
+ return _firstRow == 0 && _lastRow == LAST_ROW_INDEX;
+ }
+ public final boolean isFullRowRange() {
+ return _firstCol == 0 && _lastCol == LAST_COLUMN_INDEX;
+ }
+
+ /**
+ * @return column number for the upper left hand corner
+ */
+ public final int getFirstColumn() {
+ return _firstCol;
+ }
+
+ /**
+ * @return row number for the upper left hand corner
+ */
+ public final int getFirstRow() {
+ return _firstRow;
+ }
+
+ /**
+ * @return column number for the lower right hand corner
+ */
+ public final int getLastColumn() {
+ return _lastCol;
+ }
+
+ /**
+ * @return row number for the lower right hand corner
+ */
+ public final int getLastRow() {
+ return _lastRow;
+ }
+
+ /**
+ * @param _firstCol column number for the upper left hand corner
+ */
+ public final void setFirstColumn(int firstCol) {
+ _firstCol = firstCol;
+ }
+
+ /**
+ * @param rowFrom row number for the upper left hand corner
+ */
+ public final void setFirstRow(int firstRow) {
+ _firstRow = firstRow;
+ }
+
+ /**
+ * @param colTo column number for the lower right hand corner
+ */
+ public final void setLastColumn(int lastCol) {
+ _lastCol = lastCol;
+ }
+
+ /**
+ * @param rowTo row number for the lower right hand corner
+ */
+ public final void setLastRow(int lastRow) {
+ _lastRow = lastRow;
+ }
+
+ public final String toString() {
+ return getClass().getName() + " ["+_firstRow+", "+_lastRow+", "+_firstCol+", "+_lastCol+"]";
+ }
+}
--- /dev/null
+/* ====================================================================
+ 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.hpbf.dev;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.poi.ddf.DefaultEscherRecordFactory;
+import org.apache.poi.ddf.EscherRecord;
+import org.apache.poi.hpbf.HPBFDocument;
+import org.apache.poi.hpbf.model.QuillContents;
+import org.apache.poi.hpbf.model.qcbits.QCBit;
+import org.apache.poi.poifs.filesystem.DirectoryNode;
+import org.apache.poi.poifs.filesystem.DocumentEntry;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.StringUtil;
+
+/**
+ * For dumping out the PLC contents of QC Bits of a
+ * HPBF (Publisher) file, while we try to figure out
+ * what the format of them is.
+ */
+public class PLCDumper {
+ private HPBFDocument doc;
+ private QuillContents qc;
+
+ public PLCDumper(HPBFDocument doc) {
+ this.doc = doc;
+ qc = doc.getQuillContents();
+ }
+ public PLCDumper(POIFSFileSystem fs) throws IOException {
+ this(new HPBFDocument(fs));
+ }
+ public PLCDumper(InputStream inp) throws IOException {
+ this(new POIFSFileSystem(inp));
+ }
+
+ public static void main(String[] args) throws Exception {
+ if(args.length < 1) {
+ System.err.println("Use:");
+ System.err.println(" PLCDumper <filename>");
+ System.exit(1);
+ }
+ PLCDumper dump = new PLCDumper(
+ new FileInputStream(args[0])
+ );
+
+ System.out.println("Dumping " + args[0]);
+ dump.dumpPLC();
+ }
+
+ private void dumpPLC() {
+ QuillContents qc = doc.getQuillContents();
+ QCBit[] bits = qc.getBits();
+
+ for(int i=0; i<bits.length; i++) {
+ if(bits[i] == null) continue;
+ if(bits[i].getBitType().equals("PLC ")) {
+ dumpBit(bits[i], i);
+ }
+ }
+ }
+
+ private void dumpBit(QCBit bit, int index) {
+ System.out.println("");
+ System.out.println("Dumping " + bit.getBitType() + " bit at " + index);
+ System.out.println(" Is a " + bit.getThingType() + ", number is " + bit.getOptA());
+ System.out.println(" Starts at " + bit.getDataOffset() + " (" + Integer.toHexString(bit.getDataOffset()) + ")");
+ System.out.println(" Runs for " + bit.getLength() + " (" + Integer.toHexString(bit.getLength()) + ")");
+
+ System.out.println(HexDump.dump(bit.getData(), 0, 0));
+ }
+}
bits[i].setOptA(optA);
bits[i].setOptB(optB);
bits[i].setOptC(optC);
+ bits[i].setDataOffset(from);
} else {
// Doesn't have data
}
protected int optB;
protected int optC;
+ protected int dataOffset;
+
public QCBit(String thingType, String bitType, byte[] data) {
this.thingType = thingType;
this.bitType = bitType;
public void setOptC(int optC) {
this.optC = optC;
}
+
+ public int getDataOffset() {
+ return dataOffset;
+ }
+ public void setDataOffset(int offset) {
+ this.dataOffset = offset;
+ }
+
+ public int getLength() {
+ return data.length;
+ }
}
if(stripFields) {
return Range.stripFields(text);
}
+ // If you create a header/footer, then remove it again, word
+ // will leave \r\r. Turn these back into an empty string,
+ // which is more what you'd expect
+ if(text.equals("\r\r")) {
+ return "";
+ }
+
return text;
}
assertEquals(
"This is some text on the first page\n" +
-"It’s in times new roman, font size 10, all normal\n" +
+"It\u2019s in times new roman, font size 10, all normal\n" +
"" +
"This is in bold and italic\n" +
-"It’s Arial, 20 point font\n" +
-"It’s in the second textbox on the first page\n" +
+"It\u2019s Arial, 20 point font\n" +
+"It\u2019s in the second textbox on the first page\n" +
"" +
"This is the second page\n\n" +
"" +
, text
);
}
+
+ /**
+ * We have the same file saved for Publisher 98, Publisher
+ * 2000 and Publisher 2007. Check they all agree.
+ * @throws Exception
+ */
+ public void testMultipleVersions() throws Exception {
+ File f;
+ HPBFDocument doc;
+
+ f = new File(dir, "Sample.pub");
+ doc = new HPBFDocument(
+ new FileInputStream(f)
+ );
+ String s2007 = (new PublisherTextExtractor(doc)).getText();
+
+ f = new File(dir, "Sample2000.pub");
+ doc = new HPBFDocument(
+ new FileInputStream(f)
+ );
+ String s2000 = (new PublisherTextExtractor(doc)).getText();
+
+ f = new File(dir, "Sample98.pub");
+ doc = new HPBFDocument(
+ new FileInputStream(f)
+ );
+ String s98 = (new PublisherTextExtractor(doc)).getText();
+
+ // Check they all agree
+ assertEquals(s2007, s2000);
+ assertEquals(s2007, s98);
+ }
}
// TODO - check the contents
}
+
+ public void testComplex() throws Exception {
+ File f = new File(dir, "SampleBrochure.pub");
+ HPBFDocument doc = new HPBFDocument(
+ new FileInputStream(f)
+ );
+
+ EscherStm es = doc.getEscherStm();
+ EscherDelayStm eds = doc.getEscherDelayStm();
+
+ assertNotNull(es);
+ assertNotNull(eds);
+
+ assertEquals(30, es.getEscherRecords().length);
+ assertEquals(19, eds.getEscherRecords().length);
+
+ // TODO - check contents
+
+
+ // Now do another complex file
+ f = new File(dir, "SampleNewsletter.pub");
+ doc = new HPBFDocument(
+ new FileInputStream(f)
+ );
+
+ es = doc.getEscherStm();
+ eds = doc.getEscherDelayStm();
+
+ assertNotNull(es);
+ assertNotNull(eds);
+
+ assertEquals(51, es.getEscherRecords().length);
+ assertEquals(92, eds.getEscherRecords().length);
+ }
}
extractor = new WordExtractor(doc);
assertEquals(
- "\n\nThis is a simple header, with a \u20ac euro symbol in it.\n\n",
+ "This is a simple header, with a \u20ac euro symbol in it.\n\n",
extractor.getHeaderText()
);
text = extractor.getText();
extractor = new WordExtractor(doc);
assertEquals(
- "\n\nThe footer, with Moli\u00e8re, has Unicode in it.\n",
+ "The footer, with Moli\u00e8re, has Unicode in it.\n",
extractor.getFooterText()
);
text = extractor.getText();
assertEquals("", hs.getFirstHeader());
assertEquals("", hs.getEvenHeader());
- assertEquals("\r\r", hs.getOddHeader());
+ assertEquals("", hs.getOddHeader()); // Was \r\r but gets emptied
assertEquals("", hs.getFirstFooter());
public void testUnicode() throws Exception {
HeaderStories hs = new HeaderStories(unicode);
- assertEquals("\r\r", hs.getFirstHeader());
- assertEquals("\r\r", hs.getEvenHeader());
+ assertEquals("", hs.getFirstHeader());
+ assertEquals("", hs.getEvenHeader());
assertEquals("This is a simple header, with a \u20ac euro symbol in it.\r\r\r", hs.getOddHeader());
- assertEquals("\r\r", hs.getFirstFooter());
- assertEquals("\r\r", hs.getEvenFooter());
+ assertEquals("", hs.getFirstFooter());
+ assertEquals("", hs.getEvenFooter());
assertEquals("The footer, with Moli\u00e8re, has Unicode in it.\r\r", hs.getOddFooter());
}
package org.apache.poi.hssf.record;
-import java.util.List;
-import java.util.Stack;
-
import junit.framework.AssertionFailedError;
import junit.framework.ComparisonFailure;
import junit.framework.TestCase;
public void testConvertSharedFormulasOperandClasses_bug45123() {
TestcaseRecordInputStream in = new TestcaseRecordInputStream(0, SHARED_FORMULA_WITH_REF_ARRAYS_DATA);
- short encodedLen = in.readShort();
- Stack sharedFormula = Ptg.createParsedExpressionTokens(encodedLen, in);
+ int encodedLen = in.readUShort();
+ Ptg[] sharedFormula = Ptg.readTokens(encodedLen, in);
- Stack convertedFormula = SharedFormulaRecord.convertSharedFormulas(sharedFormula, 100, 200);
+ Ptg[] convertedFormula = SharedFormulaRecord.convertSharedFormulas(sharedFormula, 100, 200);
- RefPtg refPtg = (RefPtg) convertedFormula.get(1);
+ RefPtg refPtg = (RefPtg) convertedFormula[1];
assertEquals("$C101", refPtg.toFormulaString(null));
if (refPtg.getPtgClass() == Ptg.CLASS_REF) {
throw new AssertionFailedError("Identified bug 45123");
}
- confirmOperandClasses(toPtgArray(sharedFormula), toPtgArray(convertedFormula));
+ confirmOperandClasses(sharedFormula, convertedFormula);
}
private static void confirmOperandClasses(Ptg[] originalPtgs, Ptg[] convertedPtgs) {
}
}
}
-
- private static Ptg[] toPtgArray(List list) {
- Ptg[] result = new Ptg[list.size()];
- list.toArray(result);
- return result;
- }
}
-
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
-
+
package org.apache.poi.hssf.record;
+import org.apache.poi.hssf.util.CellRangeAddress8Bit;
import junit.framework.TestCase;
* class works correctly. Test data taken directly from a real
* Excel file.
*/
-public class TestTableRecord
- extends TestCase
-{
+public final class TestTableRecord extends TestCase {
byte[] header = new byte[] {
- 0x36, 02, 0x10, 00, // sid=x236, 16 bytes long
+ 0x36, 02, 0x10, 00, // sid=x236, 16 bytes long
+ };
+ byte[] data = new byte[] {
+ 03, 00, // from row 3
+ 8, 00, // to row 8
+ 04, // from col 4
+ 06, // to col 6
+ 00, 00, // no flags set
+ 04, 00, // row inp row 4
+ 01, 00, // col inp row 1
+ 0x76, 0x40, // row inp col 0x4076 (!)
+ 00, 00 // col inp col 0
};
- byte[] data = new byte[] {
- 03, 00, // from row 3
- 8, 00, // to row 8
- 04, // from col 4
- 06, // to col 6
- 00, 00, // no flags set
- 04, 00, // row inp row 4
- 01, 00, // col inp row 1
- 0x76, 0x40, // row inp col 0x4076 (!)
- 00, 00 // col inp col 0
- };
-
- public TestTableRecord(String name)
- {
- super(name);
- }
- public void testLoad()
- throws Exception
- {
+ public void testLoad() {
- TableRecord record = new TableRecord(new TestcaseRecordInputStream((short)0x236, (short)data.length, data));
+ TableRecord record = new TableRecord(new TestcaseRecordInputStream((short)0x236, (short)data.length, data));
- assertEquals(3, record.getRowFirst());
- assertEquals(8, record.getRowLast());
- assertEquals(4, record.getColFirst());
- assertEquals(6, record.getColLast());
- assertEquals(0, record.getFlags());
- assertEquals(4, record.getRowInputRow());
- assertEquals(1, record.getColInputRow());
- assertEquals(0x4076, record.getRowInputCol());
- assertEquals(0, record.getColInputCol());
+ CellRangeAddress8Bit range = record.getRange();
+ assertEquals(3, range.getFirstRow());
+ assertEquals(8, range.getLastRow());
+ assertEquals(4, range.getFirstColumn());
+ assertEquals(6, range.getLastColumn());
+ assertEquals(0, record.getFlags());
+ assertEquals(4, record.getRowInputRow());
+ assertEquals(1, record.getColInputRow());
+ assertEquals(0x4076, record.getRowInputCol());
+ assertEquals(0, record.getColInputCol());
- assertEquals( 16 + 4, record.getRecordSize() );
- record.validateSid((short)0x236);
- }
+ assertEquals( 16 + 4, record.getRecordSize() );
+ record.validateSid((short)0x236);
+ }
public void testStore()
{
// .col input col = 0
// [/TABLE]
- TableRecord record = new TableRecord();
- record.setRowFirst((short)3);
- record.setRowLast((short)8);
- record.setColFirst((short)4);
- record.setColLast((short)6);
- record.setFlags((byte)0);
- record.setReserved((byte)0);
- record.setRowInputRow((short)4);
- record.setColInputRow((short)1);
- record.setRowInputCol((short)0x4076);
- record.setColInputCol((short)0);
+ CellRangeAddress8Bit crab = new CellRangeAddress8Bit(3, 8, 4, 6);
+ TableRecord record = new TableRecord(crab);
+ record.setFlags((byte)0);
+ record.setRowInputRow(4);
+ record.setColInputRow(1);
+ record.setRowInputCol(0x4076);
+ record.setColInputCol(0);
- byte [] recordBytes = record.serialize();
- assertEquals(recordBytes.length - 4, data.length);
- for (int i = 0; i < data.length; i++)
- assertEquals("At offset " + i, data[i], recordBytes[i+4]);
- }
+ byte [] recordBytes = record.serialize();
+ assertEquals(recordBytes.length - 4, data.length);
+ for (int i = 0; i < data.length; i++)
+ assertEquals("At offset " + i, data[i], recordBytes[i+4]);
+ }
}
import java.util.Arrays;
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.record.UnicodeString;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
}
assertEquals("{TRUE,\"ABCD\";\"E\",0.0;FALSE,\"FG\"}", actualFormula);
}
+
+ /**
+ * worth checking since AttrPtg.sid=0x20 and Ptg.CLASS_* = (0x00, 0x20, and 0x40)
+ */
+ public void testOperandClassDecoding() {
+ confirmOperandClassDecoding(Ptg.CLASS_REF);
+ confirmOperandClassDecoding(Ptg.CLASS_VALUE);
+ confirmOperandClassDecoding(Ptg.CLASS_ARRAY);
+ }
+
+ private static void confirmOperandClassDecoding(byte operandClass) {
+ byte[] fullData = new byte[ENCODED_PTG_DATA.length + ENCODED_CONSTANT_DATA.length];
+ System.arraycopy(ENCODED_PTG_DATA, 0, fullData, 0, ENCODED_PTG_DATA.length);
+ System.arraycopy(ENCODED_CONSTANT_DATA, 0, fullData, ENCODED_PTG_DATA.length, ENCODED_CONSTANT_DATA.length);
+
+ // Force encoded operand class for tArray
+ fullData[0] = (byte) (ArrayPtg.sid + operandClass);
+
+ RecordInputStream in = new TestcaseRecordInputStream(ArrayPtg.sid, fullData);
+
+ Ptg[] ptgs = Ptg.readTokens(ENCODED_PTG_DATA.length, in);
+ ArrayPtg aPtg = (ArrayPtg) ptgs[0];
+ assertEquals(operandClass, aPtg.getPtgClass());
+ }
}
assertTrue(head.areFieldsStripped());
// Now even more complex
- head.setCenter("HEADER TEXT &P&N&D&T&Z&F&F&A&G");
- assertEquals("HEADER TEXT &G", head.getCenter());
+ head.setCenter("HEADER TEXT &P&N&D&T&Z&F&F&A&G&X END");
+ assertEquals("HEADER TEXT END", head.getCenter());
}
/**