From d45000201fac284e2137017f596167b81dc134f5 Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Sat, 30 Aug 2008 16:49:07 +0000 Subject: [PATCH] Merged revisions 638786-638802,638805-638811,638813-638814,638816-639230,639233-639241,639243-639253,639255-639486,639488-639601,639603-639835,639837-639917,639919-640056,640058-640710,640712-641156,641158-641184,641186-641795,641797-641798,641800-641933,641935-641963,641965-641966,641968-641995,641997-642230,642232-642562,642564-642565,642568-642570,642572-642573,642576-642736,642739-642877,642879,642881-642890,642892-642903,642905-642945,642947-643624,643626-643653,643655-643669,643671,643673-643830,643832-643833,643835-644342,644344-644472,644474-644508,644510-645347,645349-645351,645353-645559,645561-645565,645568-645951,645953-646193,646195-646311,646313-646404,646406-646665,646667-646853,646855-646869,646871-647151,647153-647185,647187-647277,647279-647566,647568-647573,647575,647578-647711,647714-647737,647739-647823,647825-648155,648157-648202,648204-648273,648275,648277-648302,648304-648333,648335-648588,648590-648622,648625-648673,648675-649141,649144,649146-649556,649558-649795,649799,649801-649910,649912-649913,649915-650128,650131-650132,650134-650137,650140-650914,650916-651991,651993-652284,652286-652287,652289,652291,652293-652297,652299-652328,652330-652425,652427-652445,652447-652560,652562-652933,652935,652937-652993,652995-653116,653118-653124,653126-653483,653487-653519,653522-653550,653552-653607,653609-653667,653669-653674,653676-653814,653817-653830,653832-653891,653893-653944,653946-654055,654057-654355,654357-654365,654367-654648,654651-655215,655217-655277,655279-655281,655283-655911,655913-656212,656214,656216-656251,656253-656698,656700-656756,656758-656892,656894-657135,657137-657165,657168-657179,657181-657354,657356-657357,657359-657701,657703-657874,657876-658032,658034-658284,658286,658288-658301,658303-658307,658309-658321,658323-658335,658337-658348,658351,658353-658832,658834-658983,658985,658987-659066,659068-659402,659404-659428,659430-659451,659453-659454,659456-659461,659463-659477,659479-659524,659526-659571,659574,659576-660255,660257-660262,660264-660279,660281-660343,660345-660473,660475-660827,660829-660833,660835-660888,660890-663321,663323-663435,663437-663764,663766-663854,663856-664219,664221-664489,664494-664514,664516-668013,668015-668142,668144-668152,668154,668156-668256,668258,668260-669139,669141-669455,669457-669657,669659-669808,669810-670189,670191-671321,671323-672229,672231-672549,672551-672552,672554-672561,672563-672566,672568,672571-673049,673051-673852,673854-673862,673864-673986,673988-673996,673998-674347,674349-674890,674892-674910,674912-674936,674938-674952,674954-675078,675080-675085,675087-675217,675219-675660,675662-675670,675672-675716,675718-675726,675728-675733,675735-675775,675777-675782,675784,675786-675791,675794-675852,675854-676200,676202,676204,676206-676220,676222-676309,676311-676456,676458-676994,676996-677027,677030-677040,677042-677056,677058-677375,677377-677968,677970-677971,677973,677975-677994,677996-678286,678288-678538,678540-680393,680395-680469,680471-680529,680531-680852,680854-681529,681531-681571,681573-682224,682226,682228,682231-682281,682283-682335,682337-682507,682509,682512-682517,682519-682532,682534-682619,682622-682777,682779-682998,683000-683019,683021-683022,683024-683080,683082-683092,683094-683095,683097-683127,683129-683131,683133-683166,683168-683698,683700-683705,683707-683757,683759-683787,683789-683870,683872-683879,683881-683900,683902-684066,684068-684074,684076-684222,684224-684254,684257-684281,684283-684286,684288-684292,684294-684298,684300-684301,684303-684308,684310-684317,684320,684323-684335,684337-684348,684350-684354,684356-684361,684363-684369,684371-684453,684455-684883,684885-684937,684940-684958,684960-684970,684972-684985,684987-685053,685055-685063,685065-685259,685261-685262,685264-685266,685268-685282,685285-686035,686037-686045,686047-686052,686054-686206,686208-686215,686217-686277,686279-686289,686291-686620,686622-686623,686626-686627,686629-686639,686641-686843,686845-686976,686978-687402,687404-687422,687424-687428,687430-687442,687444-688425,688427-688641,688643-688649,688651-688654,688656-688824,688826-688909,688911-689543,689545-689558,689560-689635,689637-689703,689705-689715,689717-689718,689720,689722-689972,689974-690090,690092-690093,690095-690111,690113-690258,690260-690261,690263-690517 via svnmerge from https://svn.apache.org/repos/asf/poi/trunk ........ r690404 | josh | 2008-08-29 23:08:42 +0100 (Fri, 29 Aug 2008) | 1 line Clean-up toString() and inner class ........ r690411 | josh | 2008-08-29 23:21:10 +0100 (Fri, 29 Aug 2008) | 1 line Added ArrayRecord and CellRangeAddress8Bit ........ r690461 | josh | 2008-08-30 05:34:01 +0100 (Sat, 30 Aug 2008) | 1 line Fixed decoding of operand class for ArrayPtg ........ r690517 | nick | 2008-08-30 15:47:33 +0100 (Sat, 30 Aug 2008) | 1 line Various bug fixes, and hpbf updates ........ git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@690533 13f79535-47bb-0310-9956-ffa450edef68 --- src/documentation/content/xdocs/changes.xml | 5 + .../content/xdocs/hpbf/file-format.xml | 6 + .../content/xdocs/hpbf/index.xml | 19 +- src/documentation/content/xdocs/index.xml | 4 +- src/documentation/content/xdocs/status.xml | 5 + .../org/apache/poi/ddf/EscherBSERecord.java | 16 +- .../apache/poi/hssf/record/ArrayRecord.java | 126 +++++++++ .../apache/poi/hssf/record/MulRKRecord.java | 265 ++++++++---------- .../poi/hssf/record/SelectionRecord.java | 79 ++---- .../poi/hssf/record/SharedFormulaRecord.java | 129 +++------ .../apache/poi/hssf/record/TableRecord.java | 254 +++++++---------- .../apache/poi/hssf/record/formula/Ptg.java | 74 ++--- .../poi/hssf/usermodel/HeaderFooter.java | 10 +- .../poi/hssf/util/CellRangeAddress.java | 19 +- .../poi/hssf/util/CellRangeAddress8Bit.java | 65 +++++ .../apache/poi/ss/util/CellRangeAddress.java | 132 +-------- .../poi/ss/util/CellRangeAddressBase.java | 134 +++++++++ .../org/apache/poi/hpbf/dev/PLCDumper.java | 90 ++++++ .../apache/poi/hpbf/model/QuillContents.java | 1 + .../apache/poi/hpbf/model/qcbits/QCBit.java | 13 + .../poi/hwpf/usermodel/HeaderStories.java | 7 + .../extractor/TextPublisherTextExtractor.java | 38 ++- .../poi/hpbf/model/TestEscherParts.java | 34 +++ .../poi/hwpf/extractor/TestWordExtractor.java | 4 +- .../poi/hwpf/usermodel/TestHeaderStories.java | 10 +- .../hssf/record/TestSharedFormulaRecord.java | 19 +- .../poi/hssf/record/TestTableRecord.java | 96 +++---- .../poi/hssf/record/formula/TestArrayPtg.java | 25 ++ .../hssf/usermodel/TestHSSFHeaderFooter.java | 4 +- 29 files changed, 961 insertions(+), 722 deletions(-) create mode 100644 src/java/org/apache/poi/hssf/record/ArrayRecord.java create mode 100644 src/java/org/apache/poi/hssf/util/CellRangeAddress8Bit.java create mode 100644 src/java/org/apache/poi/ss/util/CellRangeAddressBase.java create mode 100644 src/scratchpad/src/org/apache/poi/hpbf/dev/PLCDumper.java diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index 3d49ffca0..0cf94ed0d 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -64,6 +64,11 @@ Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx + Impove empty header or footer handling in HWPF HeaderStories + Avoid NPE in hssf.usermodel.HeaderFooter when stripping fields out + Avoid NPE in EscherBSERecord on older escher records + Basic text extractraction support in HPBF + Initial, low level support for Publisher files, in the form of HPBF 45699 - Fix RowRecordsAggregate to tolerate intervening MERGEDCELLS records 45698 - Fix LinkTable to tolerate multiple EXTERNSHEET records 45682 - Fix for cloning of CFRecordsAggregate diff --git a/src/documentation/content/xdocs/hpbf/file-format.xml b/src/documentation/content/xdocs/hpbf/file-format.xml index 97d5a33d7..e08ebbac0 100644 --- a/src/documentation/content/xdocs/hpbf/file-format.xml +++ b/src/documentation/content/xdocs/hpbf/file-format.xml @@ -165,6 +165,12 @@ PL 62 1a 00 00 48 00 00 00 // PL from: 1a62 (6754), len: 48 (72) (the text will then start) +

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.

diff --git a/src/documentation/content/xdocs/hpbf/index.xml b/src/documentation/content/xdocs/hpbf/index.xml index c74dc2362..01f49f061 100755 --- a/src/documentation/content/xdocs/hpbf/index.xml +++ b/src/documentation/content/xdocs/hpbf/index.xml @@ -33,11 +33,20 @@ Overview

HPBF is the POI Project's pure Java implementation of the Visio file format.

-

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.

-

At this time, there is no usermodel api or similar.

+

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.

+

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.

+

At this time, there is no usermodel api or similar. + There is only low level support for certain parts of + the file, but by no means all of it.

Our current understanding of the file format is documented here.

diff --git a/src/documentation/content/xdocs/index.xml b/src/documentation/content/xdocs/index.xml index f8336be0b..cfbcafc2d 100644 --- a/src/documentation/content/xdocs/index.xml +++ b/src/documentation/content/xdocs/index.xml @@ -160,8 +160,8 @@
HPBF for Publisher Documents

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 the HPBF project page for more information.

diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 05475a52e..d4fc73acd 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -61,6 +61,11 @@ Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx
+ Impove empty header or footer handling in HWPF HeaderStories + Avoid NPE in hssf.usermodel.HeaderFooter when stripping fields out + Avoid NPE in EscherBSERecord on older escher records + Basic text extractraction support in HPBF + Initial, low level support for Publisher files, in the form of HPBF 45699 - Fix RowRecordsAggregate to tolerate intervening MERGEDCELLS records 45698 - Fix LinkTable to tolerate multiple EXTERNSHEET records 45682 - Fix for cloning of CFRecordsAggregate diff --git a/src/java/org/apache/poi/ddf/EscherBSERecord.java b/src/java/org/apache/poi/ddf/EscherBSERecord.java index be503d73e..a1c52b4d4 100644 --- a/src/java/org/apache/poi/ddf/EscherBSERecord.java +++ b/src/java/org/apache/poi/ddf/EscherBSERecord.java @@ -87,9 +87,10 @@ public class EscherBSERecord 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 ); } @@ -168,7 +169,16 @@ public class EscherBSERecord */ 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; } /** diff --git a/src/java/org/apache/poi/hssf/record/ArrayRecord.java b/src/java/org/apache/poi/hssf/record/ArrayRecord.java new file mode 100644 index 000000000..32562a672 --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/ArrayRecord.java @@ -0,0 +1,126 @@ +/* ==================================================================== + 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.hssf.record; + +import org.apache.poi.hssf.record.formula.Ptg; +import org.apache.poi.hssf.util.CellRangeAddress8Bit; +import org.apache.poi.util.HexDump; +import org.apache.poi.util.LittleEndian; + +/** + * ARRAY (0x0221)

+ * + * Treated in a similar way to SharedFormulaRecord + * + * @author Josh Micich + */ +public final class ArrayRecord extends Record { + + public final static short sid = 0x0221; + private static final int OPT_ALWAYS_RECALCULATE = 0x0001; + private static final int OPT_CALCULATE_ON_OPEN = 0x0002; + + private CellRangeAddress8Bit _range; + + private int _options; + private int _field3notUsed; + private Ptg[] _formulaTokens; + + public ArrayRecord(RecordInputStream in) { + super(in); + } + + public boolean isAlwaysRecalculate() { + return (_options & OPT_ALWAYS_RECALCULATE) != 0; + } + public boolean isCalculateOnOpen() { + return (_options & OPT_CALCULATE_ON_OPEN) != 0; + } + + protected void validateSid(short id) { + if (id != sid) { + throw new RecordFormatException("NOT A valid Array RECORD"); + } + } + + private int getDataSize(){ + return CellRangeAddress8Bit.ENCODED_SIZE + + 2 + 4 + + getFormulaSize(); + } + + public int serialize( int offset, byte[] data ) { + int dataSize = getDataSize(); + + LittleEndian.putShort(data, 0 + offset, sid); + LittleEndian.putUShort(data, 2 + offset, dataSize); + + int pos = offset+4; + _range.serialize(pos, data); + pos += CellRangeAddress8Bit.ENCODED_SIZE; + LittleEndian.putUShort(data, pos, _options); + pos+=2; + LittleEndian.putInt(data, pos, _field3notUsed); + pos+=4; + int tokenSize = Ptg.getEncodedSizeWithoutArrayData(_formulaTokens); + LittleEndian.putUShort(data, pos, tokenSize); + pos+=2; + Ptg.serializePtgs(_formulaTokens, data, pos); + return dataSize + 4; + } + + private int getFormulaSize() { + int result = 0; + for (int i = 0; i < _formulaTokens.length; i++) { + result += _formulaTokens[i].getSize(); + } + return result; + } + + + public int getRecordSize(){ + return 4 + getDataSize(); + } + + + protected void fillFields(RecordInputStream in) { + _range = new CellRangeAddress8Bit(in); + _options = in.readUShort(); + _field3notUsed = in.readInt(); + int formulaLen = in.readUShort(); + _formulaTokens = Ptg.readTokens(formulaLen, in); + } + + public short getSid() { + return sid; + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append(getClass().getName()).append(" [ARRAY]\n"); + sb.append(" range=").append(_range.toString()).append("\n"); + sb.append(" options=").append(HexDump.shortToHex(_options)).append("\n"); + sb.append(" notUsed=").append(HexDump.intToHex(_field3notUsed)).append("\n"); + sb.append(" formula:").append("\n"); + for (int i = 0; i < _formulaTokens.length; i++) { + sb.append(_formulaTokens[i].toString()); + } + sb.append("]"); + return sb.toString(); + } +} diff --git a/src/java/org/apache/poi/hssf/record/MulRKRecord.java b/src/java/org/apache/poi/hssf/record/MulRKRecord.java index 397b97951..c301d52a8 100644 --- a/src/java/org/apache/poi/hssf/record/MulRKRecord.java +++ b/src/java/org/apache/poi/hssf/record/MulRKRecord.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -15,184 +14,152 @@ 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)

+ * * 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!

* REFERENCE: PG 330 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)

* @author Andrew C. Oliver (acoliver at apache dot org) * @version 2.0-pre */ +public final class MulRKRecord extends Record { + public final static short sid = 0x00BD; -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; + private int field_1_row; + private short field_2_first_col; + private RkRec[] field_3_rks; + private short field_4_last_col; - /** Creates new 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 MulRKRecord() - { - } + public int getRow() { + return field_1_row; + } - /** - * Constructs a MulRK record and sets its fields appropriately. - * - * @param in the RecordInputstream to read the record from - */ + /** + * starting column (first cell this holds in the row) + * @return first column number + */ + public short getFirstColumn() { + return field_2_first_col; + } - public MulRKRecord(RecordInputStream in) - { - super(in); - } + /** + * ending column (last cell this holds in the row) + * @return first column number + */ + public short getLastColumn() { + return field_4_last_col; + } - //public short getRow() - public int getRow() - { - return field_1_row; - } + /** + * 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; + } - /** - * starting column (first cell this holds in the row) - * @return first column number - */ + /** + * 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; + } - public short getFirstColumn() - { - return field_2_first_col; - } + /** + * 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); + } - /** - * ending column (last cell this holds in the row) - * @return first column number - */ + /** + * @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 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 String toString() { + StringBuffer buffer = new StringBuffer(); - public int getNumColumns() - { - return field_4_last_col - field_2_first_col + 1; - } + 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"); - /** - * returns the xf index for column (coffset = column - field_2_first_col) - * @return the XF index for the column - */ + 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(); + } - public short getXFAt(int coffset) - { - return (( RkRec ) field_3_rks.get(coffset)).xf; - } + /** + * 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 + */ - /** - * returns the rk number for column (coffset = column - field_2_first_col) - * @return the value (decoded into a double) - */ + protected void validateSid(short id) + { + if (id != sid) + { + throw new RecordFormatException("Not a MulRKRecord!"); + } + } - public double getRKNumberAt(int coffset) - { - return RKUtil.decodeNumber((( RkRec ) field_3_rks.get(coffset)).rk); - } + public short getSid() + { + return sid; + } - /** - * @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(); - } + public int serialize(int offset, byte [] data) + { + throw new RecordFormatException( + "Sorry, you can't serialize a MulRK in this release"); + } - private ArrayList parseRKs(RecordInputStream in) - { - ArrayList retval = new ArrayList(); - while ((in.remaining()-2) > 0) { - RkRec rec = new RkRec(); + private static final class RkRec { + public static final int ENCODED_SIZE = 6; + public final short xf; + public final int rk; - rec.xf = in.readShort(); - rec.rk = in.readInt(); - retval.add(rec); - } - return retval; - } + private RkRec(RecordInputStream in) { + xf = in.readShort(); + rk = in.readInt(); + } - 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 static RkRec[] parseRKs(RecordInputStream in) { + int nItems = (in.remaining()-2) / ENCODED_SIZE; + RkRec[] retval = new RkRec[nItems]; + for (int i=0; iCellRangeAddressList - */ - 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); } @@ -112,10 +72,10 @@ public final class SelectionRecord extends Record { 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); + } } /** @@ -180,8 +140,7 @@ public final class SelectionRecord extends Record { return (short)field_4_active_cell_ref_index; } - public String toString() - { + public String toString() { StringBuffer buffer = new StringBuffer(); buffer.append("[SELECTION]\n"); @@ -199,11 +158,11 @@ public final class SelectionRecord extends Record { 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()); @@ -213,9 +172,9 @@ public final class SelectionRecord extends Record { 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; } diff --git a/src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java b/src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java index 3f3a047e6..2fc6730c5 100755 --- a/src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java +++ b/src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java @@ -17,17 +17,16 @@ 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. @@ -40,58 +39,46 @@ import org.apache.poi.hssf.record.formula.RefPtg; 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) @@ -115,65 +102,28 @@ public final class SharedFormulaRecord extends Record { { 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("[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"); - 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"); + 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); } /** @@ -190,7 +140,7 @@ public final class SharedFormulaRecord extends Record { * 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. @@ -201,11 +151,10 @@ public final class SharedFormulaRecord extends Record { */ 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(); @@ -226,12 +175,16 @@ public final class SharedFormulaRecord extends Record { 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; } @@ -248,9 +201,7 @@ public final class SharedFormulaRecord extends Record { 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); diff --git a/src/java/org/apache/poi/hssf/record/TableRecord.java b/src/java/org/apache/poi/hssf/record/TableRecord.java index 237b2cb40..e9a6ae565 100644 --- a/src/java/org/apache/poi/hssf/record/TableRecord.java +++ b/src/java/org/apache/poi/hssf/record/TableRecord.java @@ -18,47 +18,46 @@ 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)

+ * * 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(); @@ -66,183 +65,146 @@ public final class TableRecord extends Record { 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 TableRecord(RecordInputStream in) { + super(in); } - public void setRowFirst(short field_1_ref_rowFirst) { - this.field_1_ref_rowFirst = field_1_ref_rowFirst; + public TableRecord(CellRangeAddress8Bit range) { + _range = range; + field_6_res = 0; } - public short getRowLast() { - return field_2_ref_rowLast; - } - public void setRowLast(short field_2_ref_rowLast) { - this.field_2_ref_rowLast = field_2_ref_rowLast; + public CellRangeAddress8Bit getRange() { + return _range; } - 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 byte getFlags() { + public int getFlags() { return field_5_flags; } - public void setFlags(byte field_5_flags) { - this.field_5_flags = field_5_flags; + public void setFlags(int flags) { + field_5_flags = flags; } - public byte getReserved() { - return field_6_res; - } - public void setReserved(byte field_6_res) { - this.field_6_res = field_6_res; - } - - 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"); - } + 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 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(); - } } diff --git a/src/java/org/apache/poi/hssf/record/formula/Ptg.java b/src/java/org/apache/poi/hssf/record/formula/Ptg.java index 43c5e86eb..296b691cd 100644 --- a/src/java/org/apache/poi/hssf/record/formula/Ptg.java +++ b/src/java/org/apache/poi/hssf/record/formula/Ptg.java @@ -43,58 +43,6 @@ import org.apache.poi.ss.usermodel.Workbook; 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 size bytes of the input stream, to create an array of Ptgs. @@ -145,15 +93,15 @@ public abstract class Ptg implements Cloneable { 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) { @@ -396,6 +344,22 @@ public abstract class Ptg implements Cloneable { 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(); diff --git a/src/java/org/apache/poi/hssf/usermodel/HeaderFooter.java b/src/java/org/apache/poi/hssf/usermodel/HeaderFooter.java index 2f0aa8a07..8e091ec00 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HeaderFooter.java +++ b/src/java/org/apache/poi/hssf/usermodel/HeaderFooter.java @@ -245,6 +245,11 @@ public abstract class HeaderFooter implements org.apache.poi.ss.usermodel.Header 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 + * + * Like {@link CellRangeAddress} except column fields are 8-bit. + * + * @author Josh Micich + */ +public final class CellRangeAddress8Bit extends CellRangeAddressBase { + + public static final int ENCODED_SIZE = 6; + + public CellRangeAddress8Bit(int firstRow, int lastRow, int firstCol, int lastCol) { + super(firstRow, lastRow, firstCol, lastCol); + } + + public CellRangeAddress8Bit(RecordInputStream in) { + super(readUShortAndCheck(in), in.readUShort(), in.readUByte(), in.readUByte()); + } + + private static int readUShortAndCheck(RecordInputStream in) { + if (in.remaining() < ENCODED_SIZE) { + // Ran out of data + throw new RuntimeException("Ran out of data reading CellRangeAddress"); + } + return in.readUShort(); + } + + public int serialize(int offset, byte[] data) { + LittleEndian.putUShort(data, offset + 0, getFirstRow()); + LittleEndian.putUShort(data, offset + 2, getLastRow()); + LittleEndian.putByte(data, offset + 4, getFirstColumn()); + LittleEndian.putByte(data, offset + 5, getLastColumn()); + return ENCODED_SIZE; + } + + public CellRangeAddress8Bit copy() { + return new CellRangeAddress8Bit(getFirstRow(), getLastRow(), getFirstColumn(), getLastColumn()); + } + + public static int getEncodedSize(int numberOfItems) { + return numberOfItems * ENCODED_SIZE; + } +} diff --git a/src/java/org/apache/poi/ss/util/CellRangeAddress.java b/src/java/org/apache/poi/ss/util/CellRangeAddress.java index e6534b34e..41c2f63ea 100644 --- a/src/java/org/apache/poi/ss/util/CellRangeAddress.java +++ b/src/java/org/apache/poi/ss/util/CellRangeAddress.java @@ -25,143 +25,29 @@ import org.apache.poi.util.LittleEndian; * 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); + super(firstRow, lastRow, firstCol, 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; - } - /** - * Range arithmetic is easier when using a large positive number for 'max row or column' - * instead of -1. - */ - private static int convertM1ToMax(int lastIx, int maxIndex) { - if(lastIx < 0) { - return maxIndex; - } - return lastIx; - } - - 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; - } } diff --git a/src/java/org/apache/poi/ss/util/CellRangeAddressBase.java b/src/java/org/apache/poi/ss/util/CellRangeAddressBase.java new file mode 100644 index 000000000..f804571c0 --- /dev/null +++ b/src/java/org/apache/poi/ss/util/CellRangeAddressBase.java @@ -0,0 +1,134 @@ +/* ==================================================================== + 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'

+ * + * 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+"]"; + } +} diff --git a/src/scratchpad/src/org/apache/poi/hpbf/dev/PLCDumper.java b/src/scratchpad/src/org/apache/poi/hpbf/dev/PLCDumper.java new file mode 100644 index 000000000..368755efc --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hpbf/dev/PLCDumper.java @@ -0,0 +1,90 @@ +/* ==================================================================== + 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 "); + 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