]> source.dussan.org Git - poi.git/commitdiff
Merged revisions 700479,700493,700916,701302,701569,701598,701747 via svnmerge from
authorJosh Micich <josh@apache.org>
Thu, 9 Oct 2008 06:38:50 +0000 (06:38 +0000)
committerJosh Micich <josh@apache.org>
Thu, 9 Oct 2008 06:38:50 +0000 (06:38 +0000)
https://svn.apache.org/repos/asf/poi/trunk

........
  r700479 | yegor | 2008-09-30 07:32:37 -0700 (Tue, 30 Sep 2008) | 1 line

  reverted the change made in r693085 , see bug #45859
........
  r700493 | yegor | 2008-09-30 08:11:26 -0700 (Tue, 30 Sep 2008) | 1 line

  initial support for creating hyperlinks in HSLF, units test are still to do
........
  r700916 | josh | 2008-10-01 13:56:21 -0700 (Wed, 01 Oct 2008) | 1 line

  Fixed bug in CellCacheEntry (support for caching blank evaluation results)
........
  r701302 | yegor | 2008-10-02 22:27:06 -0700 (Thu, 02 Oct 2008) | 1 line

  fixed bug #45889:rrayIndexOutOfBoundsException when constructing HSLF Table with a single row
........
  r701569 | josh | 2008-10-03 16:50:22 -0700 (Fri, 03 Oct 2008) | 1 line

  Fix for bug 45912 - ArrayIndexOutOfBoundsException in EmbeddedObjectRefSubRecord
........
  r701598 | josh | 2008-10-03 21:59:26 -0700 (Fri, 03 Oct 2008) | 1 line

  changed workbook reference to index in CellLocation
........
  r701747 | josh | 2008-10-04 21:43:48 -0700 (Sat, 04 Oct 2008) | 1 line

  Better bounds checking in RecordInputStream. Removed rarely used methods readShortArray and putShortArray
........

git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@703069 13f79535-47bb-0310-9956-ffa450edef68

23 files changed:
src/documentation/content/xdocs/changes.xml
src/documentation/content/xdocs/status.xml
src/java/org/apache/poi/hssf/record/EmbeddedObjectRefSubRecord.java
src/java/org/apache/poi/hssf/record/RecordInputStream.java
src/java/org/apache/poi/hssf/record/SeriesListRecord.java
src/java/org/apache/poi/hssf/usermodel/HSSFObjectData.java
src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
src/java/org/apache/poi/ss/formula/CellCacheEntry.java
src/java/org/apache/poi/ss/formula/CellLocation.java
src/java/org/apache/poi/ss/formula/CollaboratingWorkbooksEnvironment.java
src/java/org/apache/poi/ss/formula/EvaluationCache.java
src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java
src/java/org/apache/poi/util/LittleEndian.java
src/scratchpad/examples/src/org/apache/poi/hslf/examples/CreateHyperlink.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/model/Table.java
src/scratchpad/src/org/apache/poi/hslf/model/TextShape.java
src/scratchpad/src/org/apache/poi/hslf/record/TxInteractiveInfoAtom.java
src/scratchpad/testcases/org/apache/poi/hslf/model/TestTable.java
src/testcases/org/apache/poi/hssf/record/TestEmbeddedObjectRefSubRecord.java
src/testcases/org/apache/poi/hssf/record/TestSeriesListRecord.java
src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java
src/testcases/org/apache/poi/ss/formula/AllSSFormulaTests.java
src/testcases/org/apache/poi/ss/formula/TestCellCacheEntry.java [new file with mode: 0644]

index 36f01ceea58d5ff5595d8b28354e6e2f8d165cf2..c0f06eb8bd2424e4089a4f9da28e4a5bb4b2d31a 100644 (file)
@@ -67,6 +67,9 @@
            <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.2-alpha1" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="fix">45912 - fixed ArrayIndexOutOfBoundsException in EmbeddedObjectRefSubRecord</action>
+           <action dev="POI-DEVELOPERS" type="fix">45889 - fixed ArrayIndexOutOfBoundsException when constructing HSLF Table with a single row </action>
+           <action dev="POI-DEVELOPERS" type="add">Initial support for creating hyperlinks in HSLF</action>
            <action dev="POI-DEVELOPERS" type="fix">45876 - fixed BoundSheetRecord to allow sheet names longer than 31 chars</action>
            <action dev="POI-DEVELOPERS" type="add">45890 - fixed HSSFSheet.shiftRows to also update conditional formats</action>
            <action dev="POI-DEVELOPERS" type="add">45865 modified Formula Parser/Evaluator to handle cross-worksheet formulas</action>
index bcf430664c73c4fa437af45c7612abd000d9f7a8..9a4b02f57f1cbf016e63d183706a1578ea129ab6 100644 (file)
@@ -64,6 +64,9 @@
            <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.2-alpha1" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="fix">45912 - fixed ArrayIndexOutOfBoundsException in EmbeddedObjectRefSubRecord</action>
+           <action dev="POI-DEVELOPERS" type="fix">45889 - fixed ArrayIndexOutOfBoundsException when constructing HSLF Table with a single row </action>
+           <action dev="POI-DEVELOPERS" type="add">Initial support for creating hyperlinks in HSLF</action>
            <action dev="POI-DEVELOPERS" type="fix">45876 - fixed BoundSheetRecord to allow sheet names longer than 31 chars</action>
            <action dev="POI-DEVELOPERS" type="add">45890 - fixed HSSFSheet.shiftRows to also update conditional formats</action>
            <action dev="POI-DEVELOPERS" type="add">45865 modified Formula Parser/Evaluator to handle cross-worksheet formulas</action>
index fb6bce5d3a03310b7e0f9aa47a46313bea1640db..9e8d998b11ede515f299e62a3652a20c675d012e 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
    limitations under the License.
 ==================================================================== */
 
-
 package org.apache.poi.hssf.record;
 
+import java.io.ByteArrayInputStream;
 
-
-import org.apache.poi.util.*;
+import org.apache.poi.hssf.record.formula.Area3DPtg;
+import org.apache.poi.hssf.record.formula.AreaPtg;
+import org.apache.poi.hssf.record.formula.Ptg;
+import org.apache.poi.hssf.record.formula.Ref3DPtg;
+import org.apache.poi.hssf.record.formula.RefPtg;
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.StringUtil;
 
 /**
+ * ftPictFmla (0x0009)<br/>
  * A sub-record within the OBJ record which stores a reference to an object
  * stored in a separate entry within the OLE2 compound file.
  *
  * @author Daniel Noll
  */
-public class EmbeddedObjectRefSubRecord
-    extends SubRecord
-{
-    public static final short sid = 0x9;
-
-    public short   field_1_stream_id_offset;                    // Offset to stream ID from the point after this value.
-    public short[] field_2_unknown;                             // Unknown stuff at the front.  TODO: Confirm that it's a short[]
-    // TODO: Consider making a utility class for these.  I've discovered the same field ordering
-    //       in FormatRecord and StringRecord, it may be elsewhere too.
-    public short   field_3_unicode_len;                         // Length of Unicode string.
-    public boolean field_4_unicode_flag;                        // Flags whether the string is Unicode.
-    public String  field_5_ole_classname;                       // Classname of the embedded OLE document (e.g. Word.Document.8)
-    public int     field_6_stream_id;                           // ID of the OLE stream containing the actual data.
-
-    private int field_5_ole_classname_padding; // developer laziness...
-    public byte[] remainingBytes;
-
-    public EmbeddedObjectRefSubRecord()
-    {
-        field_2_unknown = new short[0];
-        remainingBytes = new byte[0];
-        field_1_stream_id_offset = 6;
-        field_5_ole_classname = "";
-    }
-
-    public short getSid()
-    {
-        return sid;
-    }
-
-    public EmbeddedObjectRefSubRecord(RecordInputStream in)
-    {
-        field_1_stream_id_offset       = in.readShort();
-        field_2_unknown                = in.readShortArray();
-        field_3_unicode_len            = in.readShort();
-        field_4_unicode_flag           = ( in.readByte() & 0x01 ) != 0;
-
-        if ( field_4_unicode_flag )
-        {
-            field_5_ole_classname      = in.readUnicodeLEString( field_3_unicode_len );
-        }
-        else
-        {
-            field_5_ole_classname      = in.readCompressedUnicode( field_3_unicode_len );
-        }
-
-        // Padded with NUL bytes.  The -2 is because field_1_stream_id_offset
-        // is relative to after the offset field, whereas in.getRecordOffset()
-        // is relative to the start of this record (minus the header.)
-        field_5_ole_classname_padding = 0;
-        while (in.getRecordOffset() - 2 < field_1_stream_id_offset)
-        {
-            field_5_ole_classname_padding++;
-            in.readByte(); // discard
-        }
-
-        // Fetch the stream ID
-        field_6_stream_id = in.readInt();
-        
-        // Store what's left
-        remainingBytes = in.readRemainder();
-    }
-
-    public int serialize(int offset, byte[] data)
-    {
-        int pos = offset;
-
-        LittleEndian.putShort(data, pos, sid); pos += 2;
-        LittleEndian.putShort(data, pos, (short)(getRecordSize() - 4)); pos += 2;
-
-        LittleEndian.putShort(data, pos, field_1_stream_id_offset); pos += 2;
-        LittleEndian.putShortArray(data, pos, field_2_unknown); pos += field_2_unknown.length * 2 + 2;
-        LittleEndian.putShort(data, pos, field_3_unicode_len); pos += 2;
-        data[pos] = field_4_unicode_flag ? (byte) 0x01 : (byte) 0x00; pos++;
-
-        if ( field_4_unicode_flag )
-        {
-            StringUtil.putUnicodeLE( field_5_ole_classname, data, pos ); pos += field_5_ole_classname.length() * 2;
-        }
-        else
-        {
-            StringUtil.putCompressedUnicode( field_5_ole_classname, data, pos ); pos += field_5_ole_classname.length();
-        }
-
-        // Padded with the same number of NUL bytes as were originally skipped.
-        // XXX: This is only accurate until we make the classname mutable.
-        pos += field_5_ole_classname_padding;
-        
-        LittleEndian.putInt(data, pos, field_6_stream_id); pos += 4;
-
-        System.arraycopy(remainingBytes, 0, data, pos, remainingBytes.length);
-
-        return getRecordSize();
-    }
-
-    /**
-     * Size of record (exluding 4 byte header)
-     */
-    public int getRecordSize()
-    {
-        // The stream id offset is relative to after the stream ID.
-        // Add 2 bytes for the stream id offset and 4 bytes for the stream id itself and 4 byts for the record header.
-        return remainingBytes.length + field_1_stream_id_offset + 2 + 4 + 4;
-    }
-
-    /**
-     * Gets the stream ID containing the actual data.  The data itself
-     * can be found under a top-level directory entry in the OLE2 filesystem
-     * under the name "MBD<var>xxxxxxxx</var>" where <var>xxxxxxxx</var> is
-     * this ID converted into hex (in big endian order, funnily enough.)
-     * 
-     * @return the data stream ID.
-     */
-    public int getStreamId()
-    {
-        return field_6_stream_id;
-    }
-
-    public String toString()
-    {
-        StringBuffer buffer = new StringBuffer();
-        buffer.append("[ftPictFmla]\n");
-        buffer.append("    .streamIdOffset       = ")
-            .append("0x").append(HexDump.toHex(  field_1_stream_id_offset ))
-            .append(" (").append( field_1_stream_id_offset ).append(" )")
-            .append(System.getProperty("line.separator"));
-        buffer.append("    .unknown              = ")
-            .append("0x").append(HexDump.toHex(  field_2_unknown ))
-            .append(" (").append( field_2_unknown.length ).append(" )")
-            .append(System.getProperty("line.separator"));
-        buffer.append("    .unicodeLen           = ")
-            .append("0x").append(HexDump.toHex(  field_3_unicode_len ))
-            .append(" (").append( field_3_unicode_len ).append(" )")
-            .append(System.getProperty("line.separator"));
-        buffer.append("    .unicodeFlag          = ")
-            .append("0x").append( field_4_unicode_flag ? 0x01 : 0x00 )
-            .append(" (").append( field_4_unicode_flag ).append(" )")
-            .append(System.getProperty("line.separator"));
-        buffer.append("    .oleClassname         = ")
-            .append(field_5_ole_classname)
-            .append(System.getProperty("line.separator"));
-        buffer.append("    .streamId             = ")
-            .append("0x").append(HexDump.toHex(  field_6_stream_id ))
-            .append(" (").append( field_6_stream_id ).append(" )")
-            .append(System.getProperty("line.separator"));
-        buffer.append("[/ftPictFmla]");
-        return buffer.toString();
-    }
+public final class EmbeddedObjectRefSubRecord extends SubRecord {
+       public static final short sid = 0x0009;
+
+       private static final byte[] EMPTY_BYTE_ARRAY = { };
+
+       private int field_1_unknown_int;
+       /** either an area or a cell ref */
+       private Ptg field_2_refPtg;
+       private byte[] field_2_unknownFormulaData;
+       // TODO: Consider making a utility class for these.  I've discovered the same field ordering
+       //         in FormatRecord and StringRecord, it may be elsewhere too.
+       private boolean field_3_unicode_flag;  // Flags whether the string is Unicode.
+       private String  field_4_ole_classname; // Classname of the embedded OLE document (e.g. Word.Document.8)
+       /** Formulas often have a single non-zero trailing byte.
+        * This is in a similar position to he pre-streamId padding
+        * It is unknown if the value is important (it seems to mirror a value a few bytes earlier) 
+        *  */
+       private Byte  field_4_unknownByte;
+       private Integer field_5_stream_id;     // ID of the OLE stream containing the actual data.
+       private byte[] field_6_unknown;
+
+
+       // currently for testing only - needs review
+       EmbeddedObjectRefSubRecord() {
+               field_2_unknownFormulaData = new byte[] { 0x02, 0x6C, 0x6A, 0x16, 0x01, }; // just some sample data.  These values vary a lot
+               field_6_unknown = EMPTY_BYTE_ARRAY;
+               field_4_ole_classname = null;
+       }
+
+       public short getSid() {
+               return sid;
+       }
+
+       public EmbeddedObjectRefSubRecord(RecordInputStream in) {
+               // Much guess-work going on here due to lack of any documentation.
+               // See similar source code in OOO:
+               // http://lxr.go-oo.org/source/sc/sc/source/filter/excel/xiescher.cxx
+               // 1223 void XclImpOleObj::ReadPictFmla( XclImpStream& rStrm, sal_uInt16 nRecSize )
+
+               int streamIdOffset = in.readShort(); // OOO calls this 'nFmlaLen'
+
+               int dataLenAfterFormula = in.remaining() - streamIdOffset;
+               int formulaSize = in.readUShort();
+               field_1_unknown_int = in.readInt();
+               byte[] formulaRawBytes = readRawData(in, formulaSize);
+               field_2_refPtg = readRefPtg(formulaRawBytes);
+               if (field_2_refPtg == null) {
+                       // common case
+                       // field_2_n16 seems to be 5 here
+                       // The formula almost looks like tTbl but the row/column values seem like garbage.
+                       field_2_unknownFormulaData = formulaRawBytes;
+               } else {
+                       field_2_unknownFormulaData = null;
+               }
+
+               int stringByteCount;
+               if (in.remaining() >= dataLenAfterFormula + 3) {
+                       int tag = in.readByte();
+                       if (tag != 0x03) {
+                               throw new RecordFormatException("Expected byte 0x03 here");
+                       }
+                       int nChars = in.readUShort();
+                       if (nChars > 0) {
+                                // OOO: the 4th way Xcl stores a unicode string: not even a Grbit byte present if length 0
+                               field_3_unicode_flag               = ( in.readByte() & 0x01 ) != 0;
+                               if (field_3_unicode_flag) {
+                                       field_4_ole_classname = in.readUnicodeLEString(nChars);
+                                       stringByteCount = nChars * 2;
+                               } else {
+                                       field_4_ole_classname = in.readCompressedUnicode(nChars);
+                                       stringByteCount = nChars;
+                               }
+                       } else {
+                               field_4_ole_classname = "";
+                               stringByteCount = 0;
+                       }
+               } else {
+                       field_4_ole_classname = null;
+                       stringByteCount = 0;
+               }
+               // Pad to next 2-byte boundary
+               if (((stringByteCount + formulaSize) % 2) != 0) {
+                       int b = in.readByte();
+                       if (field_2_refPtg != null && field_4_ole_classname == null) {
+                               field_4_unknownByte = new Byte((byte)b);
+                       }
+               }
+               int nUnexpectedPadding = in.remaining() - dataLenAfterFormula;
+
+               if (nUnexpectedPadding > 0) {
+                       System.err.println("Discarding " + nUnexpectedPadding + " unexpected padding bytes ");
+                       readRawData(in, nUnexpectedPadding);
+               }
+
+               // Fetch the stream ID
+               if (dataLenAfterFormula >= 4) {
+                       field_5_stream_id = new Integer(in.readInt());
+               } else {
+                       field_5_stream_id = null;
+               }
+
+               field_6_unknown = in.readRemainder();
+       }
+
+       private static Ptg readRefPtg(byte[] formulaRawBytes) {
+               byte[] data = new byte[formulaRawBytes.length + 4];
+               LittleEndian.putUShort(data, 0, -5555);
+               LittleEndian.putUShort(data, 2, formulaRawBytes.length);
+               System.arraycopy(formulaRawBytes, 0, data, 4, formulaRawBytes.length);
+               RecordInputStream in = new RecordInputStream(new ByteArrayInputStream(data));
+               in.nextRecord();
+               byte ptgSid = in.readByte();
+               switch(ptgSid) {
+                       case AreaPtg.sid:   return new AreaPtg(in);
+                       case Area3DPtg.sid: return new Area3DPtg(in);
+                       case RefPtg.sid:        return new RefPtg(in);
+                       case Ref3DPtg.sid:  return new Ref3DPtg(in);
+               }
+               return null;
+       }
+
+       private static byte[] readRawData(RecordInputStream in, int size) {
+               if (size < 0) {
+                       throw new IllegalArgumentException("Negative size (" + size + ")");
+               }
+               if (size == 0) {
+                       return EMPTY_BYTE_ARRAY;
+               }
+               byte[] result = new byte[size];
+               for(int i=0; i< size; i++) {
+                       result[i] = in.readByte();
+               }
+               return result;
+       }
+       
+       private int getStreamIDOffset(int formulaSize) {
+               int result = 2 + 4; // formulaSize + f2unknown_int
+               result += formulaSize;
+               
+               int stringLen;
+               if (field_4_ole_classname == null) {
+                       // don't write 0x03, stringLen, flag, text
+                       stringLen = 0;
+               } else {
+                       result += 1 + 2 + 1;  // 0x03, stringLen, flag
+                       stringLen = field_4_ole_classname.length();
+                       if (field_3_unicode_flag) {
+                               result += stringLen * 2;
+                       } else {
+                               result += stringLen;
+                       }
+               }
+               // pad to next 2 byte boundary
+               if ((result % 2) != 0) {
+                       result ++; 
+               }
+               return result;
+       }
+       
+       private int getDataSize(int idOffset) {
+
+               int result = 2 + idOffset; // 2 for idOffset short field itself
+               if (field_5_stream_id != null) {
+               result += 4;
+               }
+               return result +  field_6_unknown.length;
+       }
+       private int getDataSize() {
+               int formulaSize = field_2_refPtg == null ? field_2_unknownFormulaData.length : field_2_refPtg.getSize();
+               int idOffset = getStreamIDOffset(formulaSize);
+               return getDataSize(idOffset);
+       }
+
+       public int serialize(int base, byte[] data) {
+
+               int formulaSize = field_2_refPtg == null ? field_2_unknownFormulaData.length : field_2_refPtg.getSize();
+               int idOffset = getStreamIDOffset(formulaSize);
+               int dataSize = getDataSize(idOffset);
+               
+
+               LittleEndian.putUShort(data, base + 0, sid);
+               LittleEndian.putUShort(data, base + 2, dataSize);
+
+               LittleEndian.putUShort(data, base + 4, idOffset);
+               LittleEndian.putUShort(data, base + 6, formulaSize);
+               LittleEndian.putInt(data, base + 8, field_1_unknown_int);
+
+               int pos = base+12;
+
+               if (field_2_refPtg == null) {
+                       System.arraycopy(field_2_unknownFormulaData, 0, data, pos, field_2_unknownFormulaData.length);
+               } else {
+                       field_2_refPtg.writeBytes(data, pos);
+               }
+               pos += formulaSize;
+
+               int stringLen;
+               if (field_4_ole_classname == null) {
+                       // don't write 0x03, stringLen, flag, text
+                       stringLen = 0;
+               } else {
+                       LittleEndian.putByte(data, pos, 0x03);
+                       pos += 1;
+                       stringLen = field_4_ole_classname.length();
+                       LittleEndian.putUShort(data, pos, stringLen);
+                       pos += 2;
+                       LittleEndian.putByte(data, pos, field_3_unicode_flag ? 0x01 : 0x00);
+                       pos += 1;
+
+                       if (field_3_unicode_flag) {
+                               StringUtil.putUnicodeLE(field_4_ole_classname, data, pos);
+                               pos += stringLen * 2;
+                       } else {
+                               StringUtil.putCompressedUnicode(field_4_ole_classname, data, pos);
+                               pos += stringLen;
+                       }
+               }
+
+               // pad to next 2-byte boundary (requires 0 or 1 bytes)
+               switch(idOffset - (pos - 6 - base)) { // 6 for 3 shorts: sid, dataSize, idOffset
+                       case 1:
+                               LittleEndian.putByte(data, pos, field_4_unknownByte == null ? 0x00 : field_4_unknownByte.intValue());
+                               pos ++;
+                       case 0:
+                               break;
+                       default:
+                               throw new IllegalStateException("Bad padding calculation (" + idOffset + ", " + (pos-base) + ")");      
+               }
+
+               if (field_5_stream_id != null) {
+               LittleEndian.putInt(data, pos, field_5_stream_id.intValue());
+               pos += 4;
+               }
+               System.arraycopy(field_6_unknown, 0, data, pos, field_6_unknown.length);
+
+               return 4 + dataSize;
+       }
+
+       public int getRecordSize() {
+               return 4 + getDataSize();
+       }
+
+       /**
+        * Gets the stream ID containing the actual data.  The data itself
+        * can be found under a top-level directory entry in the OLE2 filesystem
+        * under the name "MBD<var>xxxxxxxx</var>" where <var>xxxxxxxx</var> is
+        * this ID converted into hex (in big endian order, funnily enough.)
+        *
+        * @return the data stream ID. Possibly <code>null</code>
+        */
+       public Integer getStreamId() {
+               return field_5_stream_id;
+       }
+
+       public String getOLEClassName() {
+               return field_4_ole_classname;
+       }
+
+       public byte[] getObjectData() {
+               return field_6_unknown;
+       }
+
 
+       public String toString() {
+               StringBuffer sb = new StringBuffer();
+               sb.append("[ftPictFmla]\n");
+               sb.append("    .f2unknown     = ").append(HexDump.intToHex(field_1_unknown_int)).append("\n");
+               if (field_2_refPtg == null) {
+                       sb.append("    .f3unknown     = ").append(HexDump.toHex(field_2_unknownFormulaData)).append("\n");
+               } else {
+                       sb.append("    .formula       = ").append(field_2_refPtg.toString()).append("\n");
+               }
+               if (field_4_ole_classname != null) {
+                       sb.append("    .unicodeFlag   = ").append(field_3_unicode_flag).append("\n");
+                       sb.append("    .oleClassname  = ").append(field_4_ole_classname).append("\n");
+               }
+               if (field_4_unknownByte != null) {
+                       sb.append("    .f4unknown   = ").append(HexDump.byteToHex(field_4_unknownByte.intValue())).append("\n");
+               }
+               if (field_5_stream_id != null) {
+                       sb.append("    .streamId      = ").append(HexDump.intToHex(field_5_stream_id.intValue())).append("\n");
+               }
+               if (field_6_unknown.length > 0) {
+                       sb.append("    .f7unknown     = ").append(HexDump.toHex(field_6_unknown)).append("\n");
+               }
+               sb.append("[/ftPictFmla]");
+               return sb.toString();
+       }
 }
index fe6a4b2ea3b70d01b02901bb3d43acae2276fa78..c23868c711d934bbb084edd7126a9216356d1f80 100755 (executable)
@@ -33,7 +33,7 @@ public class RecordInputStream extends InputStream {
   /** Maximum size of a single record (minus the 4 byte header) without a continue*/
   public final static short MAX_RECORD_DATA_SIZE = 8224;
   private static final int INVALID_SID_VALUE = -1;
-  
+
   private InputStream in;
   protected short currentSid;
   protected short currentLength = -1;
@@ -42,34 +42,34 @@ public class RecordInputStream extends InputStream {
   protected byte[] data = new byte[MAX_RECORD_DATA_SIZE];
   protected short recordOffset;
   protected long pos;
-  
+
   private boolean autoContinue = true;
 
-  public RecordInputStream(InputStream in) throws RecordFormatException  {
+  public RecordInputStream(InputStream in) throws RecordFormatException {
     this.in = in;
     try {
       nextSid = LittleEndian.readShort(in);
-      //Dont increment the pos just yet (technically we are at the start of
-      //the record stream until nextRecord is called).      
+      //Don't increment the pos just yet (technically we are at the start of
+      //the record stream until nextRecord is called).
     } catch (IOException ex) {
       throw new RecordFormatException("Error reading bytes", ex);
     }
   }
-  
-  /** This method will read a byte from the current record*/
-  public int read() {
-    checkRecordPosition();
-
-    byte result = data[recordOffset];
-    recordOffset += 1;
-    pos += 1;
-    return result;    
-  }
-  
+
+       /** This method will read a byte from the current record*/
+       public int read() {
+               checkRecordPosition(LittleEndian.BYTE_SIZE);
+
+               byte result = data[recordOffset];
+               recordOffset += LittleEndian.BYTE_SIZE;
+               pos += LittleEndian.BYTE_SIZE;
+               return result;
+       }
+
   public short getSid() {
     return currentSid;
   }
-  
+
   public short getLength() {
     return currentLength;
   }
@@ -85,12 +85,11 @@ public class RecordInputStream extends InputStream {
   public boolean hasNextRecord() {
     return nextSid != INVALID_SID_VALUE;
   }
-  
+
   /** Moves to the next record in the stream.
-   * 
+   *
    * <i>Note: The auto continue flag is reset to true</i>
    */
-  
   public void nextRecord() throws RecordFormatException {
     if ((currentLength != -1) && (currentLength != recordOffset)) {
       System.out.println("WARN. Unread "+remaining()+" bytes of record 0x"+Integer.toHexString(currentSid));
@@ -100,7 +99,7 @@ public class RecordInputStream extends InputStream {
     autoContinue = true;
     try {
       recordOffset = 0;
-      currentLength = LittleEndian.readShort(in);     
+      currentLength = LittleEndian.readShort(in);
       if (currentLength > MAX_RECORD_DATA_SIZE)
         throw new RecordFormatException("The content of an excel record cannot exceed "+MAX_RECORD_DATA_SIZE+" bytes");
       pos += LittleEndian.SHORT_SIZE;
@@ -113,138 +112,124 @@ public class RecordInputStream extends InputStream {
               // ex45582-22397.xls has one extra byte after the last record
               // Excel reads that file OK
           }
-          nextSid = INVALID_SID_VALUE;  
+          nextSid = INVALID_SID_VALUE;
       } else {
           nextSid = LittleEndian.readShort(in);
           if (nextSid == INVALID_SID_VALUE) {
               throw new RecordFormatException("Found sid " + nextSid + " after record with sid 0x"
                       + Integer.toHexString(currentSid).toUpperCase());
           }
-      }      
+      }
     } catch (IOException ex) {
       throw new RecordFormatException("Error reading bytes", ex);
     }
   }
-  
+
   public void setAutoContinue(boolean enable) {
-    this.autoContinue = enable;    
+    this.autoContinue = enable;
   }
-  
+
   public boolean getAutoContinue() {
     return autoContinue;
   }
-  
-  protected void checkRecordPosition() {
-    if (remaining() <= 0) {
-      if (isContinueNext() && autoContinue) {
-        nextRecord();
-      }
-      else throw new ArrayIndexOutOfBoundsException();
-    }    
-  }
-  
-  /**
-   * Reads an 8 bit, signed value
-   */
-  public byte readByte() {
-    checkRecordPosition();
-    
-    byte result = data[recordOffset];
-    recordOffset += 1;
-    pos += 1;
-    return result;
-  }
-  
-  /**
-   * Reads a 16 bit, signed value
-   */
-  public short readShort() {
-    checkRecordPosition();
-    
-    short result = LittleEndian.getShort(data, recordOffset);
-    recordOffset += LittleEndian.SHORT_SIZE;
-    pos += LittleEndian.SHORT_SIZE;
-    return result;
-  }
 
-  public int readInt() {
-    checkRecordPosition();
-    
-    int result = LittleEndian.getInt(data, recordOffset);
-    recordOffset += LittleEndian.INT_SIZE;
-    pos += LittleEndian.INT_SIZE;
-    return result;
-  }
+       private void checkRecordPosition(int requiredByteCount) {
 
-  public long readLong() {
-    checkRecordPosition();    
-    
-    long result = LittleEndian.getLong(data, recordOffset);
-    recordOffset += LittleEndian.LONG_SIZE;
-    pos += LittleEndian.LONG_SIZE;
-    return result;
-  }
+               if (remaining() < requiredByteCount) {
+                       if (isContinueNext() && autoContinue) {
+                               nextRecord();
+                       } else {
+                          throw new ArrayIndexOutOfBoundsException();
+                       }
+               }
+       }
 
-  /**
-   * Reads an 8 bit, unsigned value
-   */
-  public short readUByte() {
-      short s = readByte();
-      if(s < 0) {
-          s += 256;
-      }
-      return s;
-  }
+       /**
+        * Reads an 8 bit, signed value
+        */
+       public byte readByte() {
+               checkRecordPosition(LittleEndian.BYTE_SIZE);
 
-  /**
-   * Reads a 16 bit,un- signed value.
-   * @return
-   */
-  public int readUShort() {
-    checkRecordPosition();    
-    
-    int result = LittleEndian.getUShort(data, recordOffset);
-    recordOffset += LittleEndian.SHORT_SIZE;
-    pos += LittleEndian.SHORT_SIZE;
-    return result;
-  }
+               byte result = data[recordOffset];
+               recordOffset += LittleEndian.BYTE_SIZE;
+               pos += LittleEndian.BYTE_SIZE;
+               return result;
+       }
 
-  public double readDouble() {
-    checkRecordPosition();
-    long valueLongBits = LittleEndian.getLong(data, recordOffset);
-    double result = Double.longBitsToDouble(valueLongBits);
-    if (Double.isNaN(result)) {
-      throw new RuntimeException("Did not expect to read NaN");
-    }
-    recordOffset += LittleEndian.DOUBLE_SIZE;
-    pos += LittleEndian.DOUBLE_SIZE;
-    return result;
-  }
+       /**
+        * Reads a 16 bit, signed value
+        */
+       public short readShort() {
+               checkRecordPosition(LittleEndian.SHORT_SIZE);
 
-  
-  public short[] readShortArray() {
-    checkRecordPosition();
-    
-    short[] arr = LittleEndian.getShortArray(data, recordOffset);
-    final int size = (2 * (arr.length +1));
-    recordOffset += size;
-    pos += size;
-    
-    return arr;
-  }
-  
-  /**     
-   *  given a byte array of 16-bit unicode characters, compress to 8-bit and     
-   *  return a string     
-   *     
-   * { 0x16, 0x00 } -0x16     
-   *      
+               short result = LittleEndian.getShort(data, recordOffset);
+               recordOffset += LittleEndian.SHORT_SIZE;
+               pos += LittleEndian.SHORT_SIZE;
+               return result;
+       }
+
+       public int readInt() {
+               checkRecordPosition(LittleEndian.INT_SIZE);
+
+               int result = LittleEndian.getInt(data, recordOffset);
+               recordOffset += LittleEndian.INT_SIZE;
+               pos += LittleEndian.INT_SIZE;
+               return result;
+       }
+
+       public long readLong() {
+               checkRecordPosition(LittleEndian.LONG_SIZE);
+
+               long result = LittleEndian.getLong(data, recordOffset);
+               recordOffset += LittleEndian.LONG_SIZE;
+               pos += LittleEndian.LONG_SIZE;
+               return result;
+       }
+
+       /**
+        * Reads an 8 bit, unsigned value
+        */
+       public short readUByte() {
+               return (short) (readByte() & 0x00FF);
+       }
+
+       /**
+        * Reads a 16 bit, unsigned value.
+        * @return
+        */
+       public int readUShort() {
+               checkRecordPosition(LittleEndian.SHORT_SIZE);
+
+               int result = LittleEndian.getUShort(data, recordOffset);
+               recordOffset += LittleEndian.SHORT_SIZE;
+               pos += LittleEndian.SHORT_SIZE;
+               return result;
+       }
+
+       public double readDouble() {
+               checkRecordPosition(LittleEndian.DOUBLE_SIZE);
+               long valueLongBits = LittleEndian.getLong(data, recordOffset);
+               double result = Double.longBitsToDouble(valueLongBits);
+               if (Double.isNaN(result)) {
+                       throw new RuntimeException("Did not expect to read NaN"); // (Because Excel typically doesn't write NaN
+               }
+               recordOffset += LittleEndian.DOUBLE_SIZE;
+               pos += LittleEndian.DOUBLE_SIZE;
+               return result;
+       }
+
+  /**
+   *  given a byte array of 16-bit unicode characters, compress to 8-bit and
+   *  return a string
+   *
+   * { 0x16, 0x00 } -0x16
+   *
    * @param length the length of the final string
    * @return                                     the converted string
    * @exception  IllegalArgumentException        if len is too large (i.e.,
-   *      there is not enough data in string to create a String of that     
-   *      length)     
-   */  
+   *      there is not enough data in string to create a String of that
+   *      length)
+   */
   public String readUnicodeLEString(int length) {
     if ((length < 0) || (((remaining() / 2) < length) && !isContinueNext())) {
             throw new IllegalArgumentException("Illegal length - asked for " + length + " but only " + (remaining()/2) + " left!");
@@ -258,11 +243,11 @@ public class RecordInputStream extends InputStream {
         if(compressByte != 1) throw new IllegalArgumentException("compressByte in continue records must be 1 while reading unicode LE string");
       }
       char ch = (char)readShort();
-      buf.append(ch); 
+      buf.append(ch);
     }
     return buf.toString();
   }
-    
+
   public String readCompressedUnicode(int length) {
     if ((length < 0) || ((remaining() < length) && !isContinueNext())) {
             throw new IllegalArgumentException("Illegal length " + length);
@@ -277,23 +262,23 @@ public class RecordInputStream extends InputStream {
       }
       byte b = readByte();
       char ch = (char)(0x00FF & b); // avoid sex
-      buf.append(ch); 
+      buf.append(ch);
     }
-    return buf.toString();    
+    return buf.toString();
   }
-  
+
   /** Returns an excel style unicode string from the bytes reminaing in the record.
    * <i>Note:</i> Unicode strings differ from <b>normal</b> strings due to the addition of
    * formatting information.
-   * 
+   *
    * @return The unicode string representation of the remaining bytes.
    */
   public UnicodeString readUnicodeString() {
     return new UnicodeString(this);
   }
-  
+
   /** Returns the remaining bytes for the current record.
-   * 
+   *
    * @return The remaining bytes of the current record.
    */
   public byte[] readRemainder() {
@@ -304,39 +289,39 @@ public class RecordInputStream extends InputStream {
     pos += size;
     return result;
   }
-  
+
   /** Reads all byte data for the current record, including any
    *  that overlaps into any following continue records.
-   * 
+   *
    *  @deprecated Best to write a input stream that wraps this one where there is
    *  special sub record that may overlap continue records.
-   */  
+   */
   public byte[] readAllContinuedRemainder() {
     //Using a ByteArrayOutputStream is just an easy way to get a
     //growable array of the data.
     ByteArrayOutputStream out = new ByteArrayOutputStream(2*MAX_RECORD_DATA_SIZE);
 
     while (isContinueNext()) {
-      byte[] b = readRemainder();      
+      byte[] b = readRemainder();
       out.write(b, 0, b.length);
       nextRecord();
     }
-    byte[] b = readRemainder();      
-    out.write(b, 0, b.length);    
-    
+    byte[] b = readRemainder();
+    out.write(b, 0, b.length);
+
     return out.toByteArray();
   }
 
   /** The remaining number of bytes in the <i>current</i> record.
-   * 
+   *
    * @return The number of bytes remaining in the current record
    */
   public int remaining() {
     return (currentLength - recordOffset);
   }
 
-  /** Returns true iif a Continue record is next in the excel stream 
-   * 
+  /** Returns true iif a Continue record is next in the excel stream
+   *
    * @return True when a ContinueRecord is next.
    */
   public boolean isContinueNext() {
index 2894f158777a77d0108902aac251bcda82a9942f..4753eae05f3dbd4232c9820fa8c4fbe7cba44e36 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.util.*;
+import org.apache.poi.util.LittleEndian;
 
 /**
- * The series list record defines the series displayed as an overlay to the main chart record.
- * NOTE: This source is automatically generated please do not modify this file.  Either subclass or
- *       remove the record in src/records/definitions.
-
+ * 
+ * The series list record defines the series displayed as an overlay to the main chart record.<br/>
+ * TODO - does this record (0x1016) really exist.  It doesn't seem to be referenced in either the OOO or MS doc
+ * 
  * @author Glen Stampoultzis (glens at apache.org)
  */
-public class SeriesListRecord
-    extends Record
-{
-    public final static short      sid                             = 0x1016;
+public final class SeriesListRecord extends Record {
+    public final static short sid = 0x1016;
     private  short[]    field_1_seriesNumbers;
 
-
-    public SeriesListRecord()
-    {
-
+    public SeriesListRecord(short[] seriesNumbers) {
+       field_1_seriesNumbers = seriesNumbers;
     }
 
-    public SeriesListRecord(RecordInputStream in)
-    {
-        field_1_seriesNumbers          = in.readShortArray();
+    public SeriesListRecord(RecordInputStream in) {
+       int nItems = in.readUShort();
+       short[] ss = new short[nItems];
+       for (int i = 0; i < nItems; i++) {
+                       ss[i] = in.readShort();
+                       
+               }
+        field_1_seriesNumbers = ss;
     }
 
-    public String toString()
-    {
+    public String toString() {
         StringBuffer buffer = new StringBuffer();
 
         buffer.append("[SERIESLIST]\n");
-        buffer.append("    .seriesNumbers        = ")
-            .append(" (").append( getSeriesNumbers() ).append(" )");
-        buffer.append(System.getProperty("line.separator")); 
+        buffer.append("    .seriesNumbers= ").append(" (").append( getSeriesNumbers() ).append(" )");
+        buffer.append("\n"); 
 
         buffer.append("[/SERIESLIST]\n");
         return buffer.toString();
     }
 
-    public int serialize(int offset, byte[] data)
-    {
-        int pos = 0;
+    public int serialize(int offset, byte[] data) {
 
-        LittleEndian.putShort(data, 0 + offset, sid);
-        LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize() - 4));
+        int nItems = field_1_seriesNumbers.length;
+        int dataSize = 2 + 2 * nItems;
+       
+        LittleEndian.putUShort(data, 0 + offset, sid);
+        LittleEndian.putUShort(data, 2 + offset, dataSize);
 
-        LittleEndian.putShortArray(data, 4 + offset + pos, field_1_seriesNumbers);
+        LittleEndian.putUShort(data, 4 + offset, nItems);
+        
+        int pos = offset + 6;
+       for (int i = 0; i < nItems; i++) {
+               LittleEndian.putUShort(data, pos, field_1_seriesNumbers[i]);
+               pos += 2;
+       }
 
-        return getRecordSize();
+        return 4 + dataSize;
     }
 
-    /**
-     * Size of record (exluding 4 byte header)
-     */
     public int getRecordSize()
     {
         return 4  + field_1_seriesNumbers.length * 2 + 2;
@@ -86,34 +85,23 @@ public class SeriesListRecord
     }
 
     public Object clone() {
-        SeriesListRecord rec = new SeriesListRecord();
-    
-        rec.field_1_seriesNumbers = field_1_seriesNumbers;
-        return rec;
+        return new SeriesListRecord((short[]) field_1_seriesNumbers.clone());
     }
 
-
-
-
     /**
      * Get the series numbers field for the SeriesList record.
      */
-    public short[] getSeriesNumbers()
-    {
+    public short[] getSeriesNumbers() {
         return field_1_seriesNumbers;
     }
 
     /**
      * Set the series numbers field for the SeriesList record.
      */
-    public void setSeriesNumbers(short[] field_1_seriesNumbers)
-    {
+    public void setSeriesNumbers(short[] field_1_seriesNumbers) {
         this.field_1_seriesNumbers = field_1_seriesNumbers;
     }
-
-
-}  // END OF CLASS
-
+}
 
 
 
index 697c33b9e2b9d5731bcff562b6123992f49b36de..1db58833031d877bcf30f2049200458749d63eed 100644 (file)
@@ -32,7 +32,7 @@ import org.apache.poi.util.HexDump;
  *
  * @author Daniel Noll
  */
-public class HSSFObjectData
+public final class HSSFObjectData
 {
     /**
      * Underlying object record ultimately containing a reference to the object.
@@ -60,8 +60,7 @@ public class HSSFObjectData
      * Returns the OLE2 Class Name of the object
      */
     public String getOLE2ClassName() {
-       EmbeddedObjectRefSubRecord subRecord = findObjectRecord();
-       return subRecord.field_5_ole_classname;
+        return findObjectRecord().getOLEClassName();
     }
 
     /**
@@ -72,9 +71,9 @@ public class HSSFObjectData
      * @throws IOException if there was an error reading the data.
      */
     public DirectoryEntry getDirectory() throws IOException {
-       EmbeddedObjectRefSubRecord subRecord = findObjectRecord();
+        EmbeddedObjectRefSubRecord subRecord = findObjectRecord();
 
-       int streamId = ((EmbeddedObjectRefSubRecord) subRecord).getStreamId();
+        int streamId = subRecord.getStreamId().intValue();
         String streamName = "MBD" + HexDump.toHex(streamId);
 
         Entry entry = poifs.getRoot().getEntry(streamName);
@@ -91,8 +90,7 @@ public class HSSFObjectData
      *  Entry
      */
     public byte[] getObjectData() {
-       EmbeddedObjectRefSubRecord subRecord = findObjectRecord();
-       return subRecord.remainingBytes;
+        return findObjectRecord().getObjectData();
     }
     
     /**
@@ -101,10 +99,11 @@ public class HSSFObjectData
      * (Not all do, those that don't have a data portion)
      */
     public boolean hasDirectoryEntry() {
-       EmbeddedObjectRefSubRecord subRecord = findObjectRecord();
-       
-       // Field 6 tells you
-       return (subRecord.field_6_stream_id != 0);
+        EmbeddedObjectRefSubRecord subRecord = findObjectRecord();
+        
+        // 'stream id' field tells you
+        Integer streamId = subRecord.getStreamId();
+        return streamId != null && streamId.intValue() != 0;
     }
     
     /**
@@ -117,7 +116,7 @@ public class HSSFObjectData
         while (subRecordIter.hasNext()) {
             Object subRecord = subRecordIter.next();
             if (subRecord instanceof EmbeddedObjectRefSubRecord) {
-               return (EmbeddedObjectRefSubRecord)subRecord;
+                return (EmbeddedObjectRefSubRecord)subRecord;
             }
         }
         
index aacf6638632d482cfdee2449ceb543bdb9fac24e..24ef987eeea395fc72c44035c971e51c5fa0d1fa 100644 (file)
@@ -724,8 +724,8 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
             HSSFName newName = new HSSFName(this, newNameRecord);
             names.add(newName);
 
+            workbook.cloneDrawings(clonedSheet.getSheet());
         }
-        workbook.cloneDrawings(clonedSheet.getSheet());
         // TODO - maybe same logic required for other/all built-in name records
         
         return clonedSheet;
index 861874c25b54fd57a16a1bad716896c42f736503..bd7500023cfa246da24d66f9cdf3c2e1e6657951 100644 (file)
@@ -20,6 +20,7 @@ package org.apache.poi.ss.formula;
 import java.util.HashSet;
 import java.util.Set;
 
+import org.apache.poi.hssf.record.formula.eval.BlankEval;
 import org.apache.poi.hssf.record.formula.eval.BoolEval;
 import org.apache.poi.hssf.record.formula.eval.ErrorEval;
 import org.apache.poi.hssf.record.formula.eval.NumberEval;
@@ -60,6 +61,9 @@ final class CellCacheEntry {
                        // value type is changing
                        return false;
                }
+               if (a == BlankEval.INSTANCE) {
+                       return b == a;
+               }
                if (cls == NumberEval.class) {
                        return ((NumberEval)a).getNumberValue() == ((NumberEval)b).getNumberValue();
                }
index 6857c4bc1bbee17017745239aa5a619c5913c2f1..bb32e3dfa3fa83a6a5add28763df5c66daf56dcb 100644 (file)
@@ -25,24 +25,24 @@ import org.apache.poi.hssf.util.CellReference;
 final class CellLocation {
        public static final CellLocation[] EMPTY_ARRAY = { };
        
-       private final EvaluationWorkbook _book;
+       private final int _bookIx;
        private final int _sheetIndex;
        private final int _rowIndex;
        private final int _columnIndex;
        private final int _hashCode;
 
-       public CellLocation(EvaluationWorkbook book, int sheetIndex, int rowIndex, int columnIndex) {
+       public CellLocation(int bookIx, int sheetIndex, int rowIndex, int columnIndex) {
                if (sheetIndex < 0) {
                        throw new IllegalArgumentException("sheetIndex must not be negative");
                }
-               _book = book;
+               _bookIx = bookIx;
                _sheetIndex = sheetIndex;
                _rowIndex = rowIndex;
                _columnIndex = columnIndex;
-               _hashCode = System.identityHashCode(book) + sheetIndex + 17 * (rowIndex + 17 * columnIndex);
+               _hashCode = _bookIx + 17 * (sheetIndex + 17 * (rowIndex + 17 * columnIndex));
        }
-       public Object getBook() {
-               return _book;
+       public int getBookIndex() {
+               return _bookIx;
        }
        public int getSheetIndex() {
                return _sheetIndex;
@@ -65,7 +65,7 @@ final class CellLocation {
                if (getSheetIndex() != other.getSheetIndex()) {
                        return false;
                }
-               if (getBook() != other.getBook()) {
+               if (getBookIndex() != other.getBookIndex()) {
                        return false;
                }
                return true;
index c62d2f182d72a38d64ac2a3ecfd2c2da0adb6283..7939596d3bf7063bae4bbde9539a6bcc074bd8c8 100644 (file)
@@ -97,7 +97,7 @@ public final class CollaboratingWorkbooksEnvironment {
                EvaluationCache cache = new EvaluationCache(evalListener);
                
                for(int i=0; i<nItems; i++) {
-                       evaluators[i].attachToEnvironment(env, cache);
+                       evaluators[i].attachToEnvironment(env, cache, i);
                }
                
        }
index b0c34fd78a01ff87e3669fa1b6c795a4d5443ce9..a1b06ad17e5f0e71e0701d60e74c0a74536682dd 100644 (file)
@@ -203,7 +203,7 @@ final class EvaluationCache {
                        CellLocation clB = (CellLocation) b;
                        
                        int cmp;
-                       cmp = System.identityHashCode(clA.getBook()) - System.identityHashCode(clB.getBook());
+                       cmp = clA.getBookIndex() - clB.getBookIndex();
                        if (cmp != 0) {
                                return cmp;
                        }
index 9c02ca9f9e9db5ccb7e7443ece66418826c9fb98..7aa9bb2a8c891e0b9a9e09d4edbe6dced022f619 100644 (file)
@@ -80,6 +80,7 @@ public final class WorkbookEvaluator {
 
        private final EvaluationWorkbook _workbook;
        private EvaluationCache _cache;
+       private int _workbookIx;
 
        private final IEvaluationListener _evaluationListener;
        private final Map _sheetIndexesBySheet;
@@ -94,6 +95,7 @@ public final class WorkbookEvaluator {
                _cache = new EvaluationCache(evaluationListener);
                _sheetIndexesBySheet = new IdentityHashMap();
                _collaboratingWorkbookEnvironment = CollaboratingWorkbooksEnvironment.EMPTY;
+               _workbookIx = 0;
        }
 
        /**
@@ -111,9 +113,10 @@ public final class WorkbookEvaluator {
                        System.out.println(s);
                }
        }
-       /* package */ void attachToEnvironment(CollaboratingWorkbooksEnvironment collaboratingWorkbooksEnvironment, EvaluationCache cache) {
+       /* package */ void attachToEnvironment(CollaboratingWorkbooksEnvironment collaboratingWorkbooksEnvironment, EvaluationCache cache, int workbookIx) {
                _collaboratingWorkbookEnvironment = collaboratingWorkbooksEnvironment;
                _cache = cache;
+               _workbookIx = workbookIx;
        }
        /* package */ CollaboratingWorkbooksEnvironment getEnvironment() {
                return _collaboratingWorkbookEnvironment;
@@ -122,6 +125,7 @@ public final class WorkbookEvaluator {
        /* package */ void detachFromEnvironment() {
                _collaboratingWorkbookEnvironment = CollaboratingWorkbooksEnvironment.EMPTY;
                _cache = new EvaluationCache(_evaluationListener);
+               _workbookIx = 0;
        }
        /* package */ IEvaluationListener getEvaluationListener() {
                return _evaluationListener;
@@ -148,7 +152,7 @@ public final class WorkbookEvaluator {
                        throw new IllegalArgumentException("value must not be null");
                }
                int sheetIndex = getSheetIndex(sheet);
-               _cache.setValue(new CellLocation(_workbook, sheetIndex, rowIndex, columnIndex), true, CellLocation.EMPTY_ARRAY, value);
+               _cache.setValue(getCellLoc(sheetIndex, rowIndex, columnIndex), true, CellLocation.EMPTY_ARRAY, value);
 
        }
        /**
@@ -157,7 +161,7 @@ public final class WorkbookEvaluator {
         */
        public void notifySetFormula(Sheet sheet, int rowIndex, int columnIndex) {
                int sheetIndex = getSheetIndex(sheet);
-               _cache.setValue(new CellLocation(_workbook, sheetIndex, rowIndex, columnIndex), false, CellLocation.EMPTY_ARRAY, null);
+               _cache.setValue(getCellLoc(sheetIndex, rowIndex, columnIndex), false, CellLocation.EMPTY_ARRAY, null);
 
        }
        private int getSheetIndex(Sheet sheet) {
@@ -175,7 +179,7 @@ public final class WorkbookEvaluator {
 
        public ValueEval evaluate(Cell srcCell) {
                int sheetIndex = getSheetIndex(srcCell.getSheet());
-               CellLocation cellLoc = new CellLocation(_workbook, sheetIndex, srcCell.getRowIndex(), srcCell.getCellNum());
+               CellLocation cellLoc = getCellLoc(sheetIndex, srcCell.getRowIndex(), srcCell.getCellNum());
                return internalEvaluate(srcCell, cellLoc, new EvaluationTracker(_cache));
        }
 
@@ -471,8 +475,11 @@ public final class WorkbookEvaluator {
                } else {
                        cell = row.getCell(columnIndex);
                }
-               CellLocation cellLoc = new CellLocation(_workbook, sheetIndex, rowIndex, columnIndex);
+               CellLocation cellLoc = getCellLoc(sheetIndex, rowIndex, columnIndex);
                tracker.acceptDependency(cellLoc);
                return internalEvaluate(cell, cellLoc, tracker);
        }
+       private CellLocation getCellLoc(int sheetIndex, int rowIndex, int columnIndex) {
+               return new CellLocation(_workbookIx, sheetIndex, rowIndex, columnIndex);
+       }
 }
index 6838eb2aa45ccbd689c173414dde920cadd975fd..373710b47a6609ed49243186fe789ee66225b108 100644 (file)
@@ -250,20 +250,6 @@ public final class LittleEndian implements LittleEndianConsts {
         putNumber(data, offset, value, LittleEndianConsts.BYTE_SIZE);
     }
 
-    /**
-     *  put a array of shorts into a byte array
-     *
-     *@param  data    the byte array
-     *@param  offset  a starting offset into the byte array
-     *@param  value   the short array
-     */
-    public static void putShortArray(final byte[] data, final int offset, final short[] value) {
-        putNumber(data, offset, value.length, SHORT_SIZE);
-        for (int i = 0; i < value.length; i++) {
-            putNumber(data, offset + 2 + (i * 2), value[i], SHORT_SIZE);
-        }
-    }
-
     /**
      * put an unsigned short value into a byte array
      *
diff --git a/src/scratchpad/examples/src/org/apache/poi/hslf/examples/CreateHyperlink.java b/src/scratchpad/examples/src/org/apache/poi/hslf/examples/CreateHyperlink.java
new file mode 100755 (executable)
index 0000000..9254c37
--- /dev/null
@@ -0,0 +1,59 @@
+/* ====================================================================\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
+package org.apache.poi.hslf.examples;\r
+\r
+import org.apache.poi.hslf.usermodel.SlideShow;\r
+import org.apache.poi.hslf.usermodel.RichTextRun;\r
+import org.apache.poi.hslf.model.*;\r
+\r
+import java.io.FileInputStream;\r
+import java.io.FileOutputStream;\r
+import java.awt.*;\r
+\r
+/**\r
+ * Demonstrates how to create hyperlinks in PowerPoint presentations\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class CreateHyperlink {\r
+\r
+    public static void main(String[] args) throws Exception {\r
+        SlideShow ppt = new SlideShow();\r
+\r
+        Slide slide = ppt.createSlide();\r
+\r
+        TextBox shape = new TextBox();\r
+        shape.setText("Apache POI");\r
+        Rectangle anchor = new Rectangle(100, 100, 200, 50);\r
+        shape.setAnchor(anchor);\r
+\r
+        String text = shape.getText();\r
+        Hyperlink link = new Hyperlink();\r
+        link.setAddress("http://www.apache.org");\r
+        link.setTitle(shape.getText());\r
+        int linkId = ppt.addHyperlink(link);\r
+\r
+        shape.setHyperlink(linkId, 0, text.length());\r
+\r
+        slide.addShape(shape);\r
+\r
+        FileOutputStream out = new FileOutputStream("hyperlink.ppt");\r
+        ppt.write(out);\r
+        out.close();\r
+\r
+   }\r
+}\r
index 113a2d8f2b05b460bbfb2e0eaf8c149bb066fd46..bd27969fafe3dbfc9ad4b68803f51f1a1b42885e 100755 (executable)
@@ -53,6 +53,9 @@ public class Table extends ShapeGroup {
     public Table(int numrows, int numcols) {\r
         super();\r
 \r
+        if(numrows < 1) throw new IllegalArgumentException("The number of rows must be greater than 1");\r
+        if(numcols < 1) throw new IllegalArgumentException("The number of columns must be greater than 1");\r
+\r
         int x=0, y=0, tblWidth=0, tblHeight=0;\r
         cells = new TableCell[numrows][numcols];\r
         for (int i = 0; i < cells.length; i++) {\r
@@ -165,11 +168,11 @@ public class Table extends ShapeGroup {
                 Rectangle anchor = sh[i].getAnchor();\r
                 if(anchor.y != y0){\r
                     y0 = anchor.y;\r
-                    if(row != null) maxrowlen = Math.max(maxrowlen, row.size());\r
                     row = new ArrayList();\r
                     lst.add(row);\r
                 }\r
                 row.add(sh[i]);\r
+                maxrowlen = Math.max(maxrowlen, row.size());\r
             }\r
         }\r
         cells = new TableCell[lst.size()][maxrowlen];\r
index 751753b22e538e426dd25d9c42fc43459dd2c480..a4d823be55029b03f1e378307304684d1f62ba18 100755 (executable)
@@ -547,4 +547,31 @@ public abstract class TextShape extends SimpleShape {
         return (OEPlaceholderAtom)getClientDataRecord(RecordTypes.OEPlaceholderAtom.typeID);
     }
 
+    /**
+     *
+     * Assigns a hyperlink to this text shape
+     *
+     * @param linkId    id of the hyperlink, @see org.apache.poi.hslf.usermodel.SlideShow#addHyperlink(Hyperlink)
+     * @param      beginIndex   the beginning index, inclusive.
+     * @param      endIndex     the ending index, exclusive.
+     * @see org.apache.poi.hslf.usermodel.SlideShow#addHyperlink(Hyperlink)
+     */
+    public void setHyperlink(int linkId, int beginIndex, int endIndex){
+        //TODO validate beginIndex and endIndex and throw IllegalArgumentException
+
+        InteractiveInfo info = new InteractiveInfo();
+        InteractiveInfoAtom infoAtom = info.getInteractiveInfoAtom();
+        infoAtom.setAction(InteractiveInfoAtom.ACTION_HYPERLINK);
+        infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_Url);
+        infoAtom.setHyperlinkID(linkId);
+
+        _txtbox.appendChildRecord(info);
+
+        TxInteractiveInfoAtom txiatom = new TxInteractiveInfoAtom();
+        txiatom.setStartIndex(beginIndex);
+        txiatom.setEndIndex(endIndex);
+        _txtbox.appendChildRecord(txiatom);
+
+    }
+
 }
index b3db6bafaa8c38a775742ea714a08743e2998187..e6a827977343284bcae5f00a9240f73412146f86 100644 (file)
@@ -40,7 +40,7 @@ public class TxInteractiveInfoAtom extends RecordAtom {
     /**\r
      * Constructs a brand new link related atom record.\r
      */\r
-    protected TxInteractiveInfoAtom() {\r
+    public TxInteractiveInfoAtom() {\r
         _header = new byte[8];\r
         _data = new byte[8];\r
 \r
index fff6482eccf4b74fdbbd25d71060fb4b628ddb1c..7202ff345fe76a67afdbce372769cf7bff22f82a 100755 (executable)
@@ -60,4 +60,40 @@ public class TestTable extends TestCase {
         assertEquals(tbl.getNumberOfRows(), tbl3.getNumberOfRows());\r
     }\r
 \r
+    /**\r
+     * Error constructing Table when rownum=1\r
+     */\r
+    public void test45889(){\r
+        SlideShow ppt = new SlideShow();\r
+        Slide slide = ppt.createSlide();\r
+        Shape[] shapes;\r
+        Table tbl1 = new Table(1, 5);\r
+        assertEquals(5, tbl1.getNumberOfColumns());\r
+        assertEquals(1, tbl1.getNumberOfRows());\r
+        slide.addShape(tbl1);\r
+\r
+        shapes = slide.getShapes();\r
+        assertEquals(1, shapes.length);\r
+\r
+        Table tbl2 = (Table)shapes[0];\r
+        assertSame(tbl1.getSpContainer(), tbl2.getSpContainer());\r
+\r
+        assertEquals(tbl1.getNumberOfColumns(), tbl2.getNumberOfColumns());\r
+        assertEquals(tbl1.getNumberOfRows(), tbl2.getNumberOfRows());\r
+    }\r
+\r
+    public void testIllegalCOnstruction(){\r
+        try {\r
+            Table tbl = new Table(0, 5);\r
+            fail("Table(rownum, colnum) must throw IllegalArgumentException if any of tghe arguments is less than 1");\r
+        } catch (IllegalArgumentException e){\r
+\r
+        }\r
+        try {\r
+            Table tbl = new Table(5, 0);\r
+            fail("Table(rownum, colnum) must throw IllegalArgumentException if any of tghe arguments is less than 1");\r
+        } catch (IllegalArgumentException e){\r
+\r
+        }\r
+    }\r
 }\r
index feb13757fc4ac2b3c14e3406d81f26f870242286..e09f9e34ab1ccc3e4501b7a216623c7cfeff2916 100644 (file)
 ==================================================================== */\r
 package org.apache.poi.hssf.record;\r
 \r
-import junit.framework.TestCase;\r
-import org.apache.poi.util.HexRead;\r
-\r
-import java.io.IOException;\r
 import java.io.ByteArrayInputStream;\r
 import java.util.Arrays;\r
 \r
+import junit.framework.TestCase;\r
+\r
+import org.apache.poi.util.HexRead;\r
+\r
 /**\r
  * Tests the serialization and deserialization of the TestEmbeddedObjectRefSubRecord\r
  * class works correctly.  Test data taken directly from a real\r
@@ -30,53 +30,111 @@ import java.util.Arrays;
  *\r
  * @author Yegor Kozlov\r
  */\r
-public class TestEmbeddedObjectRefSubRecord extends TestCase {\r
-\r
-    String data1 = "[20, 00, 05, 00, FC, 10, 76, 01, 02, 24, 14, DF, 00, 03, 10, 00, 00, 46, 6F, 72, 6D, 73, 2E, 43, 68, 65, 63, 6B, 42, 6F, 78, 2E, 31, 00, 00, 00, 00, 00, 70, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, ]";\r
-\r
-    public void testStore() throws IOException {\r
-\r
-        byte[] src = HexRead.readFromString(data1);\r
-        src = TestcaseRecordInputStream.mergeDataAndSid(EmbeddedObjectRefSubRecord.sid, (short)src.length, src);\r
-\r
-        RecordInputStream in = new RecordInputStream(new ByteArrayInputStream(src));\r
-        in.nextRecord();\r
-\r
-        EmbeddedObjectRefSubRecord record1 = new EmbeddedObjectRefSubRecord(in);\r
-\r
-        byte[] ser = record1.serialize();\r
-\r
-        RecordInputStream in2 = new RecordInputStream(new ByteArrayInputStream(ser));\r
-        in2.nextRecord();\r
-        EmbeddedObjectRefSubRecord record2 = new EmbeddedObjectRefSubRecord(in2);\r
-\r
-        assertTrue(Arrays.equals(src, ser));\r
-        assertEquals(record1.field_1_stream_id_offset, record2.field_1_stream_id_offset);\r
-        assertTrue(Arrays.equals(record1.field_2_unknown, record2.field_2_unknown));\r
-        assertEquals(record1.field_3_unicode_len, record2.field_3_unicode_len);\r
-        assertEquals(record1.field_4_unicode_flag, record2.field_4_unicode_flag);\r
-        assertEquals(record1.field_5_ole_classname, record2.field_5_ole_classname);\r
-        assertEquals(record1.field_6_stream_id, record2.field_6_stream_id);\r
-        assertTrue(Arrays.equals(record1.remainingBytes, record2.remainingBytes));\r
-    }\r
-\r
-    public void testCreate() throws IOException {\r
-\r
-\r
-        EmbeddedObjectRefSubRecord record1 = new EmbeddedObjectRefSubRecord();\r
-\r
-        byte[] ser = record1.serialize();\r
-        RecordInputStream in2 = new RecordInputStream(new ByteArrayInputStream(ser));\r
-        in2.nextRecord();\r
-        EmbeddedObjectRefSubRecord record2 = new EmbeddedObjectRefSubRecord(in2);\r
-\r
-        assertEquals(record1.field_1_stream_id_offset, record2.field_1_stream_id_offset);\r
-        assertTrue(Arrays.equals(record1.field_2_unknown, record2.field_2_unknown));\r
-        assertEquals(record1.field_3_unicode_len, record2.field_3_unicode_len);\r
-        assertEquals(record1.field_4_unicode_flag, record2.field_4_unicode_flag);\r
-        assertEquals(record1.field_5_ole_classname, record2.field_5_ole_classname);\r
-        assertEquals(record1.field_6_stream_id, record2.field_6_stream_id);\r
-        assertTrue(Arrays.equals(record1.remainingBytes, record2.remainingBytes));\r
+public final class TestEmbeddedObjectRefSubRecord extends TestCase {\r
 \r
-    }\r
+       String data1 = "[20, 00, 05, 00, FC, 10, 76, 01, 02, 24, 14, DF, 00, 03, 10, 00, 00, 46, 6F, 72, 6D, 73, 2E, 43, 68, 65, 63, 6B, 42, 6F, 78, 2E, 31, 00, 00, 00, 00, 00, 70, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, ]";\r
+\r
+       public void testStore() {\r
+\r
+               byte[] src = hr(data1);\r
+               src = TestcaseRecordInputStream.mergeDataAndSid(EmbeddedObjectRefSubRecord.sid, (short)src.length, src);\r
+\r
+               RecordInputStream in = new RecordInputStream(new ByteArrayInputStream(src));\r
+               in.nextRecord();\r
+\r
+               EmbeddedObjectRefSubRecord record1 = new EmbeddedObjectRefSubRecord(in);\r
+\r
+               byte[] ser = record1.serialize();\r
+\r
+               RecordInputStream in2 = new RecordInputStream(new ByteArrayInputStream(ser));\r
+               in2.nextRecord();\r
+               EmbeddedObjectRefSubRecord record2 = new EmbeddedObjectRefSubRecord(in2);\r
+\r
+               assertTrue(Arrays.equals(src, ser));\r
+               assertEquals(record1.getOLEClassName(), record2.getOLEClassName());\r
+\r
+               byte[] ser2 = record1.serialize();\r
+               assertTrue(Arrays.equals(ser, ser2));\r
+       }\r
+\r
+       public void testCreate() {\r
+\r
+               EmbeddedObjectRefSubRecord record1 = new EmbeddedObjectRefSubRecord();\r
+\r
+               byte[] ser = record1.serialize();\r
+               RecordInputStream in2 = new RecordInputStream(new ByteArrayInputStream(ser));\r
+               in2.nextRecord();\r
+               EmbeddedObjectRefSubRecord record2 = new EmbeddedObjectRefSubRecord(in2);\r
+\r
+               assertEquals(record1.getOLEClassName(), record2.getOLEClassName());\r
+               assertEquals(record1.getStreamId(), record2.getStreamId());\r
+\r
+               byte[] ser2 = record1.serialize();\r
+               assertTrue(Arrays.equals(ser, ser2));\r
+       }\r
+\r
+\r
+       /**\r
+        * taken from ftPictFmla sub-record in attachment 22645 (offset 0x40AB).\r
+        */\r
+       private static final byte[] data45912 = hr(\r
+               "09 00 14 00 " +\r
+               "12 00 0B 00 F8 02 88 04 3B 00 " +\r
+               "00 00 00 01 00 00 00 01 " +\r
+               "00 00");\r
+\r
+       public void testCameraTool_bug45912() {\r
+               byte[] data = data45912;\r
+               RecordInputStream in = new RecordInputStream(new ByteArrayInputStream(data));\r
+               in.nextRecord();\r
+\r
+               EmbeddedObjectRefSubRecord rec = new EmbeddedObjectRefSubRecord(in);\r
+               byte[] ser2 = rec.serialize();\r
+               assertTrue(Arrays.equals(data, ser2));\r
+\r
+\r
+       }\r
+\r
+       private static byte[] hr(String string) {\r
+               return HexRead.readFromString(string);\r
+       }\r
+\r
+       /**\r
+        * tests various examples of OLE controls\r
+        */\r
+       public void testVarious() {\r
+               String[] rawData = {\r
+                       "12 00 0B 00 70 95 0B 05 3B 01 00 36 00 40 00 18 00 19 00 18",\r
+                       "12 00 0B 00 B0 4D 3E 03 3B 00 00 00 00 01 00 00 80 01 C0 00",\r
+                       "0C 00 05 00 60 AF 3B 03 24 FD FF FE C0 FE",\r
+                       "24 00 05 00 40 42 3E 03 02 80 CD B4 04 03 15 00 00 46 6F 72 6D 73 2E 43 6F 6D 6D 61 6E 64 42 75 74 74 6F 6E 2E 31 00 00 00 00 54 00 00 00 00 00 00 00 00 00 00 00",\r
+                       "22 00 05 00 10 4E 3E 03 02 00 4C CC 04 03 12 00 00 46 6F 72 6D 73 2E 53 70 69 6E 42 75 74 74 6F 6E 2E 31 00 54 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00",\r
+                       "20 00 05 00 E0 41 3E 03 02 00 FC 0B 05 03 10 00 00 46 6F 72 6D 73 2E 43 6F 6D 62 6F 42 6F 78 2E 31 00 74 00 00 00 4C 00 00 00 00 00 00 00 00 00 00 00",\r
+                       "24 00 05 00 00 4C AF 03 02 80 E1 93 05 03 14 00 00 46 6F 72 6D 73 2E 4F 70 74 69 6F 6E 42 75 74 74 6F 6E 2E 31 00 C0 00 00 00 70 00 00 00 00 00 00 00 00 00 00 00",\r
+                       "20 00 05 00 E0 A4 28 04 02 80 EA 93 05 03 10 00 00 46 6F 72 6D 73 2E 43 68 65 63 6B 42 6F 78 2E 31 00 30 01 00 00 6C 00 00 00 00 00 00 00 00 00 00 00",\r
+                       "1C 00 05 00 30 40 3E 03 02 00 CC B4 04 03 0D 00 00 46 6F 72 6D 73 2E 4C 61 62 65 6C 2E 31 9C 01 00 00 54 00 00 00 00 00 00 00 00 00 00 00",\r
+                       "1E 00 05 00 B0 A4 28 04 02 00 D0 0A 05 03 0F 00 00 46 6F 72 6D 73 2E 4C 69 73 74 42 6F 78 2E 31 F0 01 00 00 48 00 00 00 00 00 00 00 00 00 00 00",\r
+                       "24 00 05 00 C0 AF 3B 03 02 80 D1 0A 05 03 14 00 00 46 6F 72 6D 73 2E 54 6F 67 67 6C 65 42 75 74 74 6F 6E 2E 31 00 38 02 00 00 6C 00 00 00 00 00 00 00 00 00 00 00",\r
+                       "1E 00 05 00 90 AF 3B 03 02 80 D4 0A 05 03 0F 00 00 46 6F 72 6D 73 2E 54 65 78 74 42 6F 78 2E 31 A4 02 00 00 48 00 00 00 00 00 00 00 00 00 00 00",\r
+                       "24 00 05 00 60 40 3E 03 02 00 D6 0A 05 03 14 00 00 46 6F 72 6D 73 2E 54 6F 67 67 6C 65 42 75 74 74 6F 6E 2E 31 00 EC 02 00 00 6C 00 00 00 00 00 00 00 00 00 00 00",\r
+                       "20 00 05 00 20 4D 3E 03 02 00 D9 0A 05 03 11 00 00 46 6F 72 6D 73 2E 53 63 72 6F 6C 6C 42 61 72 2E 31 58 03 00 00 20 00 00 00 00 00 00 00 00 00 00 00",\r
+                       "20 00 05 00 00 AF 28 04 02 80 31 AC 04 03 10 00 00 53 68 65 6C 6C 2E 45 78 70 6C 6F 72 65 72 2E 32 00 78 03 00 00 AC 00 00 00 00 00 00 00 00 00 00 00",\r
+               };\r
+\r
+               for (int i = 0; i < rawData.length; i++) {\r
+                       confirmRead(hr(rawData[i]), i);\r
+               }\r
+       }\r
+\r
+       private static void confirmRead(byte[] data, int i) {\r
+               RecordInputStream in = new TestcaseRecordInputStream(EmbeddedObjectRefSubRecord.sid, (short)data.length, data);\r
+\r
+               EmbeddedObjectRefSubRecord rec = new EmbeddedObjectRefSubRecord(in);\r
+               byte[] ser2 = rec.serialize();\r
+               byte[] d2 = (byte[]) data.clone(); // remove sid+len for compare\r
+               System.arraycopy(ser2, 4, d2, 0, d2.length);\r
+               if (!Arrays.equals(data, d2)) {\r
+                       fail("re-read NQR for case " + i);\r
+               }\r
+       }\r
 }\r
index 7e81f2928061e3e854406ebf4c109e3295aa331f..027b6c3867d49c37835def05d6e705f5b93d9a34 100644 (file)
@@ -29,7 +29,7 @@ import junit.framework.TestCase;
  * @author Glen Stampoultzis (glens at apache.org)
  */
 public final class TestSeriesListRecord extends TestCase {
-    byte[] data = new byte[] {
+    private static final byte[] data = {
         (byte)0x02,(byte)0x00,(byte)0x01,(byte)0x20,(byte)0xff,(byte)0xf0
     };
 
@@ -43,10 +43,8 @@ public final class TestSeriesListRecord extends TestCase {
         assertEquals( 4 + 6, record.getRecordSize() );
     }
 
-    public void testStore()
-    {
-        SeriesListRecord record = new SeriesListRecord();
-        record.setSeriesNumbers( new short[] { (short)0x2001, (short)0xf0ff } );
+    public void testStore() {
+        SeriesListRecord record = new SeriesListRecord(new short[] { (short)0x2001, (short)0xf0ff } );
 
         byte [] recordBytes = record.serialize();
         assertEquals(recordBytes.length - 4, data.length);
index eb706f1095fe14cd782ebd727b5323a48c2c06d6..0178d336fe424fd4785454dad72f1a7b058a9c1c 100644 (file)
@@ -971,7 +971,7 @@ public final class TestBugs extends TestCase {
     public void test44840() {
         HSSFWorkbook wb = openSample("WithCheckBoxes.xls");
 
-        // Take a look at the embeded objects
+        // Take a look at the embedded objects
         List objects = wb.getAllEmbeddedObjects();
         assertEquals(1, objects.size());
 
@@ -982,10 +982,10 @@ public final class TestBugs extends TestCase {
         EmbeddedObjectRefSubRecord rec = obj.findObjectRecord();
         assertNotNull(rec);
 
-        assertEquals(32, rec.field_1_stream_id_offset);
-        assertEquals(0, rec.field_6_stream_id); // WRONG!
-        assertEquals("Forms.CheckBox.1", rec.field_5_ole_classname);
-        assertEquals(12, rec.remainingBytes.length);
+//        assertEquals(32, rec.field_1_stream_id_offset);
+        assertEquals(0, rec.getStreamId().intValue()); // WRONG!
+        assertEquals("Forms.CheckBox.1", rec.getOLEClassName());
+        assertEquals(12, rec.getObjectData().length);
 
         // Doesn't have a directory
         assertFalse(obj.hasDirectoryEntry());
@@ -997,7 +997,7 @@ public final class TestBugs extends TestCase {
             obj.getDirectory();
             fail();
         } catch(FileNotFoundException e) {
-            // expectd during successful test
+            // expected during successful test
         } catch (IOException e) {
             throw new RuntimeException(e);
         }
index dd1360f7695bd40d6d1905a3a4b0559f0c74e53b..13e2cfecdf48ce8662f4de89501ea5634d22235d 100644 (file)
@@ -26,9 +26,10 @@ import junit.framework.TestSuite;
  */
 public final class AllSSFormulaTests {
     public static Test suite() {
-        TestSuite result = new TestSuite(AllSSFormulaTests.class.getName());
-        result.addTestSuite(TestEvaluationCache.class);
-        result.addTestSuite(TestWorkbookEvaluator.class);
-        return result;
-    }
+               TestSuite result = new TestSuite(AllSSFormulaTests.class.getName());
+               result.addTestSuite(TestCellCacheEntry.class);
+               result.addTestSuite(TestEvaluationCache.class);
+               result.addTestSuite(TestWorkbookEvaluator.class);
+               return result;
+       }
 }
diff --git a/src/testcases/org/apache/poi/ss/formula/TestCellCacheEntry.java b/src/testcases/org/apache/poi/ss/formula/TestCellCacheEntry.java
new file mode 100644 (file)
index 0000000..e8ff03f
--- /dev/null
@@ -0,0 +1,53 @@
+/* ====================================================================
+   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.ss.formula;
+
+import org.apache.poi.hssf.record.formula.eval.BlankEval;
+import org.apache.poi.hssf.record.formula.eval.NumberEval;
+import org.apache.poi.hssf.record.formula.eval.ValueEval;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
+/**
+ * Tests {@link CellCacheEntry}.
+ *
+ * @author Josh Micich
+ */
+public class TestCellCacheEntry extends TestCase {
+
+       public void testBasic() {
+               CellCacheEntry cce = new CellCacheEntry();
+               cce.updatePlainValue(new NumberEval(42.0));
+               ValueEval ve = cce.getValue();
+               assertEquals(42, ((NumberEval)ve).getNumberValue(), 0.0);
+               
+               cce.setFormulaResult(new NumberEval(10.0), new CellLocation[] { });
+       }
+
+       public void testBlank() {
+               CellCacheEntry cce = new CellCacheEntry();
+               cce.updatePlainValue(BlankEval.INSTANCE);
+               try {
+                       cce.updatePlainValue(BlankEval.INSTANCE);
+               } catch (IllegalStateException e) {
+                       // bug was visible around svn r700356
+                       throw new AssertionFailedError("cache entry does not handle blank values properly");
+               }
+       }
+}