]> source.dussan.org Git - poi.git/commitdiff
Merged revisions 638786-638802,638805-638811,638813-638814,638816-639230,639233-63924...
authorNick Burch <nick@apache.org>
Sat, 30 Aug 2008 16:49:07 +0000 (16:49 +0000)
committerNick Burch <nick@apache.org>
Sat, 30 Aug 2008 16:49:07 +0000 (16:49 +0000)
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

29 files changed:
src/documentation/content/xdocs/changes.xml
src/documentation/content/xdocs/hpbf/file-format.xml
src/documentation/content/xdocs/hpbf/index.xml
src/documentation/content/xdocs/index.xml
src/documentation/content/xdocs/status.xml
src/java/org/apache/poi/ddf/EscherBSERecord.java
src/java/org/apache/poi/hssf/record/ArrayRecord.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/record/MulRKRecord.java
src/java/org/apache/poi/hssf/record/SelectionRecord.java
src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java
src/java/org/apache/poi/hssf/record/TableRecord.java
src/java/org/apache/poi/hssf/record/formula/Ptg.java
src/java/org/apache/poi/hssf/usermodel/HeaderFooter.java
src/java/org/apache/poi/hssf/util/CellRangeAddress.java
src/java/org/apache/poi/hssf/util/CellRangeAddress8Bit.java [new file with mode: 0644]
src/java/org/apache/poi/ss/util/CellRangeAddress.java
src/java/org/apache/poi/ss/util/CellRangeAddressBase.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hpbf/dev/PLCDumper.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hpbf/model/QuillContents.java
src/scratchpad/src/org/apache/poi/hpbf/model/qcbits/QCBit.java
src/scratchpad/src/org/apache/poi/hwpf/usermodel/HeaderStories.java
src/scratchpad/testcases/org/apache/poi/hpbf/extractor/TextPublisherTextExtractor.java
src/scratchpad/testcases/org/apache/poi/hpbf/model/TestEscherParts.java
src/scratchpad/testcases/org/apache/poi/hwpf/extractor/TestWordExtractor.java
src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestHeaderStories.java
src/testcases/org/apache/poi/hssf/record/TestSharedFormulaRecord.java
src/testcases/org/apache/poi/hssf/record/TestTableRecord.java
src/testcases/org/apache/poi/hssf/record/formula/TestArrayPtg.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFHeaderFooter.java

index 3d49ffca0ccfcecab58f9f43f960a5af6df04cf3..0cf94ed0d511046a4b45e031c075dbecae147fb3 100644 (file)
            <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>
index 97d5a33d7c9c2cbf3c8f3cd0417a6bde508860fc..e08ebbac04db850bca3eb1117179b79643253b21 100644 (file)
@@ -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)
 </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>
index c74dc23621a9c469fba35855b3b4cea078997207..01f49f061fc7bff3c4847c8eafa44f633fd8b7a3 100755 (executable)
             <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> 
index f8336be0b5d6d0d48d6979dc0ecb7aeecaf736bd..cfbcafc2dbec70a2462691d3bf2be5d4b037a3a2 100644 (file)
         </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>
index 05475a52eeb2112816191f4e0ff8f24b0a820401..d4fc73acd3c044fcc3c2c425355824e7ce66eb1f 100644 (file)
            <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>
index be503d73e7b9a7c2a3f8511e3cbd643cad962bad..a1c52b4d48d2152a28dfb8adbcc4bbd01e22e820 100644 (file)
@@ -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 (file)
index 0000000..32562a6
--- /dev/null
@@ -0,0 +1,126 @@
+/* ====================================================================\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
index 397b979519d506fca19c6ea165620bab5f86fd0c..c301d52a8afa3549d9566c9bf0b25ccc66a0ce85 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    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;
+               }
+       }
 }
index 2f36ea644ff862539715e413faf5df551cba8a08..e1849a7d75c0514a1d6fb83e9768228601058873 100644 (file)
@@ -17,6 +17,7 @@
 
 package org.apache.poi.hssf.record;
 
+import org.apache.poi.hssf.util.CellRangeAddress8Bit;
 import org.apache.poi.util.LittleEndian;
 
 /**
@@ -35,66 +36,25 @@ public final class SelectionRecord extends Record {
     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);
     }
@@ -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;
     }
 
index 3f3a047e647da81a25ffa3ef6d8db7bbb1df37cf..2fc6730c5a7f0e53eb7b96f458ba8f2c8f567062 100755 (executable)
 
 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("    .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);
     }
 
     /**
@@ -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);
index 237b2cb40ef633e86d76cc8ad25784f32d46cd4e..e9a6ae5659fcb2e7d4e069210c0943476dd31d09 100644 (file)
 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();
@@ -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 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);
+       }
 }
index 43c5e86ebe290b4ac730710ecec5b3ac160b16a6..296b691cda291dabad0284a6531917c66ad140d7 100644 (file)
@@ -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 <tt>size</tt> bytes of the input stream, to create an array of <tt>Ptg</tt>s.
@@ -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();
 
index 2f0aa8a07db6d4df05e40587d3f96264cf480c17..8e091ec0032c644c7355654d22babd6a1e29cd78 100644 (file)
@@ -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<Field.ALL_FIELDS.size(); i++) {
                String seq = ((Field)Field.ALL_FIELDS.get(i)).sequence;
@@ -255,6 +260,7 @@ public abstract class HeaderFooter implements org.apache.poi.ss.usermodel.Header
        }
        
        // Now do the tricky, dynamic ones
+       // These are things like font sizes and font names
        text = text.replaceAll("\\&\\d+", "");
        text = text.replaceAll("\\&\".*?,.*?\"", "");
        
@@ -290,9 +296,9 @@ public abstract class HeaderFooter implements org.apache.poi.ss.usermodel.Header
     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");
index c7ff2653ca63214471252d5ecc2fba25684c7dd9..cc67b8eb05619af0528acce23008320dea358c67 100644 (file)
@@ -27,20 +27,23 @@ import org.apache.poi.util.LittleEndian;
  * @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
diff --git a/src/java/org/apache/poi/hssf/util/CellRangeAddress8Bit.java b/src/java/org/apache/poi/hssf/util/CellRangeAddress8Bit.java
new file mode 100644 (file)
index 0000000..3b0c83f
--- /dev/null
@@ -0,0 +1,65 @@
+/* ====================================================================\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
index e6534b34e0afbfbaa38116a1e4455f9a53601bb3..41c2f63ea2a6d091801a5252e92cd03da4502142 100644 (file)
@@ -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);
-       }
-
-       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;
-       }
 }
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 (file)
index 0000000..f804571
--- /dev/null
@@ -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'<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+"]";
+       }
+}
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 (file)
index 0000000..368755e
--- /dev/null
@@ -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 <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));
+       }
+}
index ae626fcae39a1534639a4ef72c3c5cd5479a2de4..b8d4ad298ad7541ad4b1fda003905e2145f96689 100644 (file)
@@ -70,6 +70,7 @@ public final class QuillContents extends HPBFPart {
                                bits[i].setOptA(optA);
                                bits[i].setOptB(optB);
                                bits[i].setOptC(optC);
+                               bits[i].setDataOffset(from);
                        } else {
                                // Doesn't have data
                        }
index 61c7955f66910a9288e9f3f8299c0b5fb682a82d..d6a5608bb4f41637b85f1b5173da0f47aa7e7e96 100644 (file)
@@ -28,6 +28,8 @@ public abstract class QCBit {
        protected int optB;
        protected int optC;
        
+       protected int dataOffset;
+       
        public QCBit(String thingType, String bitType, byte[] data) {
                this.thingType = thingType;
                this.bitType = bitType;
@@ -66,4 +68,15 @@ public abstract class QCBit {
        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;
+       }
 }
index 83003617201be1cd774efd773f5334bee5a0bb49..574c065ec3693ede2e9bbc72bee729dd72e92000 100644 (file)
@@ -167,6 +167,13 @@ public class HeaderStories {
                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;
        }
        
index 96396e10734df7b1cec12ec952fbeec48c0e928d..d5b4712227b2a8f1b6948b476ec69d3b818f830d 100644 (file)
@@ -59,11 +59,11 @@ public class TextPublisherTextExtractor extends TestCase {
                
                assertEquals(
 "This is some text on the first page\n" +
-"Its 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" +
-"Its Arial, 20 point font\n" +
-"Its 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" +
 "" +
@@ -102,4 +102,36 @@ public class TextPublisherTextExtractor extends TestCase {
                                , 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);
+       }
 }
index dbaf46c649666045c1fe25483ab19395989ee55b..631095007d6ac8f0f7ef23b3d66dc7a405781857 100644 (file)
@@ -47,4 +47,38 @@ public class TestEscherParts extends TestCase {
                
                // 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);
+       }
 }
index 704b4d4dd26793f91b11caa9b6030c0a244bb03e..a1b78752f818d4807b5049d5c616f4bc306afb84 100644 (file)
@@ -183,7 +183,7 @@ public class TestWordExtractor extends TestCase {
        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();
@@ -217,7 +217,7 @@ public class TestWordExtractor extends TestCase {
        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();
index 404f6e47a402b007b4db5344d2d3a6be2bbaac86..e68352b508676ef70c4ae5ea95b71c901d4719b4 100644 (file)
@@ -123,7 +123,7 @@ public class TestHeaderStories extends TestCase {
        
                assertEquals("", hs.getFirstHeader());
                assertEquals("", hs.getEvenHeader());
-               assertEquals("\r\r", hs.getOddHeader());
+               assertEquals("", hs.getOddHeader()); // Was \r\r but gets emptied
 
                
                assertEquals("", hs.getFirstFooter());
@@ -181,13 +181,13 @@ public class TestHeaderStories extends TestCase {
     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());
     }
     
index 92dc956189f31ff9e28a6c7aa6956b5b01a7a075..3742342360a357cc74707a8313f48b416e8aae92 100644 (file)
@@ -17,9 +17,6 @@
 
 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;
@@ -63,18 +60,18 @@ public final class TestSharedFormulaRecord extends 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) {
@@ -88,10 +85,4 @@ public final class TestSharedFormulaRecord extends TestCase {
                        }
                }
        }
-
-       private static Ptg[] toPtgArray(List list) {
-               Ptg[] result = new Ptg[list.size()];
-               list.toArray(result);
-               return result;
-       }
 }
index eb17a1ff33b863d7079cba5c6b19d6992d44c649..0f562d1456bddfbb2ecd83472c4fab02362c9681 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    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;
 
@@ -26,48 +26,40 @@ 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()
     {
@@ -87,21 +79,17 @@ public class TestTableRecord
 //         .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]);
+       }
 }
index 2906f5e020a6a004e7925c10ce3e73fc821ccc3b..9e5a34004505182aa806bad4f2cd0e7d704b917b 100644 (file)
@@ -20,6 +20,7 @@ package org.apache.poi.hssf.record.formula;
 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;
@@ -128,4 +129,28 @@ public final class TestArrayPtg extends TestCase {
                }
                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());
+       }
 }
index caa75633b0668ccb0e1c9c3233ecf4e89a980660..3ae1efcaaba00066b25632c40b1facaac13d6859 100644 (file)
@@ -87,8 +87,8 @@ public final class TestHSSFHeaderFooter extends TestCase {
        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());
        }
 
        /**