]> source.dussan.org Git - poi.git/commitdiff
Merged revisions 638786-638802,638805-638811,638813-638814,638816-639230,639233-63924...
authorNick Burch <nick@apache.org>
Tue, 6 May 2008 16:00:36 +0000 (16:00 +0000)
committerNick Burch <nick@apache.org>
Tue, 6 May 2008 16:00:36 +0000 (16:00 +0000)
https://svn.apache.org:443/repos/asf/poi/trunk

........
  r651992 | nick | 2008-04-27 19:02:13 +0100 (Sun, 27 Apr 2008) | 1 line

  Fix from Trejkaz from bug #44857 - Avoid OOM on unknown escher records when EscherMetafileBlip is incorrect
........
  r652285 | yegor | 2008-04-30 07:18:05 +0100 (Wed, 30 Apr 2008) | 1 line

  start a new section for 3.1-beta2
........
  r652288 | yegor | 2008-04-30 07:19:38 +0100 (Wed, 30 Apr 2008) | 1 line

  correctly process PICT blips (see bug #44886)
........
  r652290 | yegor | 2008-04-30 07:21:04 +0100 (Wed, 30 Apr 2008) | 1 line

  more flexible creation of a cluster
........
  r652292 | yegor | 2008-04-30 07:22:02 +0100 (Wed, 30 Apr 2008) | 1 line

  a few more words in the release guide
........
  r652298 | yegor | 2008-04-30 07:29:11 +0100 (Wed, 30 Apr 2008) | 1 line

  more work on rendering ppt slides
........
  r652329 | nick | 2008-04-30 12:10:49 +0100 (Wed, 30 Apr 2008) | 1 line

  Tests to show that bugs 44891 and 44861 were both already fixed
........
  r652426 | josh | 2008-05-01 04:25:37 +0100 (Thu, 01 May 2008) | 1 line

  fixed bug 44892 - made HSSFWorkbook.getSheet(String) case insensitive
........
  r652446 | josh | 2008-05-01 08:42:18 +0100 (Thu, 01 May 2008) | 1 line

  44914 - Fix/suppress warning message - WARN. Unread n bytes of record 0xNN
........
  r652561 | josh | 2008-05-01 16:46:21 +0100 (Thu, 01 May 2008) | 1 line

  added disabled junit for bug 44916
........
  r652934 | josh | 2008-05-02 23:36:49 +0100 (Fri, 02 May 2008) | 1 line

  44921 - allow Ptg.writeBytes() to be called on relative ref Ptgs (RefN* and AreaN*)
........
  r652936 | josh | 2008-05-02 23:49:38 +0100 (Fri, 02 May 2008) | 1 line

  should have been submitted with r652934
........
  r652994 | josh | 2008-05-03 04:59:32 +0100 (Sat, 03 May 2008) | 1 line

  Fixed 44675 - Parameter operand classes (function metadata) required to encode SUM() etc properly. Added parse validation for number of parameters
........
  r653117 | josh | 2008-05-03 20:53:38 +0100 (Sat, 03 May 2008) | 1 line

  44929 - Improved error handling in HSSFWorkbook when attempting to read a BIFF5 file
........
  r653125 | josh | 2008-05-03 21:13:56 +0100 (Sat, 03 May 2008) | 1 line

  Swapped ArrayIndexOutOfBoundsException for plain array length check in AbstractFunctionPtg.getParameterClass(). (To help debugging when trying to find a real AIOOB)
........
  r653484 | yegor | 2008-05-05 14:59:11 +0100 (Mon, 05 May 2008) | 1 line

  take into account indentation in HSSFSheet.autosizeColumn
........
  r653485 | yegor | 2008-05-05 14:59:38 +0100 (Mon, 05 May 2008) | 1 line

  take into account indentation in HSSFSheet.autosizeColumn
........
  r653486 | yegor | 2008-05-05 15:00:30 +0100 (Mon, 05 May 2008) | 1 line

  getting ready to 3.1-beta2
........
  r653520 | yegor | 2008-05-05 17:12:21 +0100 (Mon, 05 May 2008) | 1 line

  bug #44235 is not reproducible in 3.1-beta1
........
  r653521 | yegor | 2008-05-05 17:13:24 +0100 (Mon, 05 May 2008) | 1 line

  restored mistakenly commented line
........
  r653551 | josh | 2008-05-05 19:30:49 +0100 (Mon, 05 May 2008) | 1 line

  Added test case method javadoc for old bug 44675
........
  r653608 | josh | 2008-05-05 22:38:07 +0100 (Mon, 05 May 2008) | 1 line

  Follow-on from 28754 - StringPtg.toFormulaString() should escape double quotes
........
  r653668 | josh | 2008-05-06 03:02:41 +0100 (Tue, 06 May 2008) | 1 line

  42564 - fixed ArrayPtg to use ConstantValueParser.  Fixed a few other ArrayPtg encoding issues.
........
  r653675 | josh | 2008-05-06 04:57:15 +0100 (Tue, 06 May 2008) | 1 line

  42570 - fixed LabelRecord to use empty string instead of null when the length is zero.
........

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

87 files changed:
build.xml
src/documentation/content/xdocs/changes.xml
src/documentation/content/xdocs/index.xml
src/documentation/content/xdocs/status.xml
src/documentation/release-guide.txt
src/java/org/apache/poi/ddf/EscherDggRecord.java
src/java/org/apache/poi/ddf/EscherMetafileBlip.java
src/java/org/apache/poi/hssf/dev/BiffViewer.java
src/java/org/apache/poi/hssf/model/FormulaParser.java
src/java/org/apache/poi/hssf/model/Workbook.java
src/java/org/apache/poi/hssf/record/ColumnInfoRecord.java
src/java/org/apache/poi/hssf/record/FileSharingRecord.java
src/java/org/apache/poi/hssf/record/FormulaRecord.java
src/java/org/apache/poi/hssf/record/LabelRecord.java
src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java
src/java/org/apache/poi/hssf/record/constant/ConstantValueParser.java
src/java/org/apache/poi/hssf/record/constant/ErrorConstant.java
src/java/org/apache/poi/hssf/record/formula/AbstractFunctionPtg.java
src/java/org/apache/poi/hssf/record/formula/AreaNAPtg.java
src/java/org/apache/poi/hssf/record/formula/AreaNPtg.java
src/java/org/apache/poi/hssf/record/formula/AreaNVPtg.java
src/java/org/apache/poi/hssf/record/formula/AreaPtg.java
src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java
src/java/org/apache/poi/hssf/record/formula/ArrayPtgA.java
src/java/org/apache/poi/hssf/record/formula/ArrayPtgV.java
src/java/org/apache/poi/hssf/record/formula/FuncPtg.java
src/java/org/apache/poi/hssf/record/formula/FuncVarPtg.java
src/java/org/apache/poi/hssf/record/formula/Ptg.java
src/java/org/apache/poi/hssf/record/formula/RefNAPtg.java
src/java/org/apache/poi/hssf/record/formula/RefNPtg.java
src/java/org/apache/poi/hssf/record/formula/RefNVPtg.java
src/java/org/apache/poi/hssf/record/formula/ReferencePtg.java
src/java/org/apache/poi/hssf/record/formula/StringPtg.java
src/java/org/apache/poi/hssf/record/formula/function/FunctionDataBuilder.java
src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadata.java
src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataReader.java
src/java/org/apache/poi/hssf/usermodel/HSSFPictureData.java
src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
src/ooxml/interfaces-jdk14/org/apache/poi/ss/usermodel/Workbook.java
src/scratchpad/src/org/apache/poi/hslf/model/AutoShapes.java
src/scratchpad/src/org/apache/poi/hslf/model/Freeform.java
src/scratchpad/src/org/apache/poi/hslf/model/MasterSheet.java
src/scratchpad/src/org/apache/poi/hslf/model/Picture.java
src/scratchpad/src/org/apache/poi/hslf/model/Shape.java
src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java
src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java
src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java
src/scratchpad/src/org/apache/poi/hslf/model/Slide.java
src/scratchpad/src/org/apache/poi/hslf/model/SlideMaster.java
src/scratchpad/src/org/apache/poi/hslf/model/TextPainter.java
src/scratchpad/src/org/apache/poi/hslf/model/TextRun.java
src/scratchpad/src/org/apache/poi/hslf/model/TextShape.java
src/scratchpad/src/org/apache/poi/hslf/record/PPDrawing.java
src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java
src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/TextRulerAtom.java [new file with mode: 0755]
src/scratchpad/src/org/apache/poi/hslf/usermodel/RichTextRun.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java
src/scratchpad/testcases/org/apache/poi/hslf/model/TestFreeform.java
src/scratchpad/testcases/org/apache/poi/hslf/model/TestShapes.java
src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextRulerAtom.java [new file with mode: 0755]
src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestRichTextRun.java
src/testcases/org/apache/poi/ddf/AllPOIDDFTests.java
src/testcases/org/apache/poi/ddf/TestEscherBlipRecord.java [new file with mode: 0755]
src/testcases/org/apache/poi/ddf/TestEscherContainerRecord.java
src/testcases/org/apache/poi/ddf/data/Container.dat [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/HSSFTestDataSamples.java
src/testcases/org/apache/poi/hssf/data/44235.xls [new file with mode: 0755]
src/testcases/org/apache/poi/hssf/data/44840.xls [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/data/44861.xls [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/data/44891.xls [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/data/ex42570-20305.xls [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/data/ex44921-21902.xls [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java
src/testcases/org/apache/poi/hssf/record/AllRecordTests.java
src/testcases/org/apache/poi/hssf/record/TestLabelRecord.java [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/record/formula/AllFormulaTests.java
src/testcases/org/apache/poi/hssf/record/formula/TestArrayPtg.java [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/record/formula/TestFuncVarPtg.java [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/record/formula/TestReferencePtg.java
src/testcases/org/apache/poi/hssf/usermodel/AllUserModelTests.java
src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java
src/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorBugs.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFPatriarch.java [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFPicture.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFWorkbook.java

index 2145611b7145c8706ea08b3de8356693b183504c..c58f06ae2eed2854910bea1ac2d94229332fbe0f 100644 (file)
--- a/build.xml
+++ b/build.xml
@@ -579,6 +579,8 @@ under the License.
         file="${main.src.test}/org/apache/poi/hpsf/data"/> 
       <sysproperty key="POIFS.testdata.path"
         file="${main.src.test}/org/apache/poi/poifs/data"/> 
+      <sysproperty key="DDF.testdata.path"
+        file="${main.src.test}/org/apache/poi/ddf/data"/> 
       <sysproperty key="java.awt.headless" value="true"/>
       <formatter type="plain"/>
       <formatter type="xml"/>
index d0d73c8d8ab06dd39b15797c829274417662ec49..3eb198142acaee8a1865fd28d108ec692d4917b2 100644 (file)
            <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling PowerPoint files, irrespective of if they are .ppt or .pptx</action>
            <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-beta1" date="2008-04-??">
+        <release version="3.1-beta2" date="2008-05-??">
+           <action dev="POI-DEVELOPERS" type="fix">42570 - fixed LabelRecord to use empty string instead of null when the length is zero.</action>
+           <action dev="POI-DEVELOPERS" type="fix">42564 - fixed ArrayPtg to use ConstantValueParser.  Fixed a few other ArrayPtg encoding issues.</action>
+           <action dev="POI-DEVELOPERS" type="fix">Follow-on from 28754 - StringPtg.toFormulaString() should escape double quotes</action>
+           <action dev="POI-DEVELOPERS" type="fix">44929 - Improved error handling in HSSFWorkbook when attempting to read a BIFF5 file</action>
+           <action dev="POI-DEVELOPERS" type="fix">44675 - Parameter operand classes (function metadata) required to encode SUM() etc properly. Added parse validation for number of parameters</action>
+           <action dev="POI-DEVELOPERS" type="fix">44921 - allow Ptg.writeBytes() to be called on relative ref Ptgs (RefN* and AreaN*)</action>
+           <action dev="POI-DEVELOPERS" type="fix">44914 - Fix/suppress warning message "WARN. Unread n bytes of record 0xNN"</action>
+           <action dev="POI-DEVELOPERS" type="fix">44892 - made HSSFWorkbook.getSheet(String) case insensitive</action>
+           <action dev="POI-DEVELOPERS" type="fix">44886] - Correctly process PICT metafile in EscherMetafileBlip</action>
+           <action dev="POI-DEVELOPERS" type="fix">44893 - Take into account indentation in HSSFSheet.autoSizeColumn</action>
+        </release>
+        <release version="3.1-beta1" date="2008-04-28">
+           <action dev="POI-DEVELOPERS" type="fix">44857 - Avoid OOM on unknown escher records when EscherMetafileBlip is incorrect</action>
            <action dev="POI-DEVELOPERS" type="add">HSLF: Support for getting embedded sounds from slide show </action>
            <action dev="POI-DEVELOPERS" type="add">HSLF: Initial support for rendering slides into images</action>
            <action dev="POI-DEVELOPERS" type="add">HSLF: Support for getting OLE object data from slide show </action>
index e6f136652b3cd9107476681358649a2c5b264418..09fb0709f8aa6500324b18919f00146d3b1bb066 100644 (file)
        People interested should follow the
        <link href="mailinglists.html">dev list</link> to track progress.</p>
     </section>
-    <section><title>February 06 2008 - POI 3.0.2 Released</title>
+    <section><title>POI 3.1-BETA1 Released (2008-04028)</title>
+      <p>
+        The POI team is pleased to announce the release of 3.1 BETA1 which is one of the final steps before 3.1 FINAL.
+        The status of this release is a beta, meaning that we encourage users to try it out.
+        If you find any bugs, please report them to the POI <link href="https://issues.apache.org/bugzilla/buglist.cgi?product=POI">bug database</link> or to
+        the <link href="./mailinglists.html">POI Developer List</link>.
+        </p><p>  A full list of changes        is available in
+      <link href="./changes.html">the changelog</link>, and 
+               <link href="http://www.apache.org/dyn/closer.cgi/poi/dev/">download</link>
+               the source and binaries from your
+    <link href="http://www.apache.org/dyn/closer.cgi/poi/dev/">local mirror</link>.
+      </p>
+      <p>
+        The release is also available from the central Maven repository 
+        under Group ID "org.apache.poi" and Version "3.1-beta1".
+      </p>  
+    </section>
+    <section><title>POI 3.0.2 Released</title>
       <p>The POI team is pleased to announce POI 3.0.2, the latest release of Apache POI.
         There have been many important bug fixes since the 3.0.1 release and a lot of new features.  A full list of changes    is available in 
       <link href="./changes.html">the changelog</link>, and 
index 8a9b8e1208e0766fdefc42fb4ea3ff470855b63a..5732cd9339d470a8e59600dccdd62ce4668a8cb9 100644 (file)
            <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling PowerPoint files, irrespective of if they are .ppt or .pptx</action>
            <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-beta1" date="2008-04-??">
+        <release version="3.1-beta2" date="2008-05-??">
+           <action dev="POI-DEVELOPERS" type="fix">42570 - fixed LabelRecord to use empty string instead of null when the length is zero.</action>
+           <action dev="POI-DEVELOPERS" type="fix">42564 - fixed ArrayPtg to use ConstantValueParser.  Fixed a few other ArrayPtg encoding issues.</action>
+           <action dev="POI-DEVELOPERS" type="fix">Follow-on from 28754 - StringPtg.toFormulaString() should escape double quotes</action>
+           <action dev="POI-DEVELOPERS" type="fix">44929 - Improved error handling in HSSFWorkbook when attempting to read a BIFF5 file</action>
+           <action dev="POI-DEVELOPERS" type="fix">44675 - Parameter operand classes (function metadata) required to encode SUM() etc properly. Added parse validation for number of parameters</action>
+           <action dev="POI-DEVELOPERS" type="fix">44921 - allow Ptg.writeBytes() to be called on relative ref Ptgs (RefN* and AreaN*)</action>
+           <action dev="POI-DEVELOPERS" type="fix">44914 - Fix/suppress warning message "WARN. Unread n bytes of record 0xNN"</action>
+           <action dev="POI-DEVELOPERS" type="fix">44892 - made HSSFWorkbook.getSheet(String) case insensitive</action>
+           <action dev="POI-DEVELOPERS" type="fix">44886] - Correctly process PICT metafile in EscherMetafileBlip</action>
+           <action dev="POI-DEVELOPERS" type="fix">44893 - Take into account indentation in HSSFSheet.autoSizeColumn</action>
+        </release>
+        <release version="3.1-beta1" date="2008-04-28">
+           <action dev="POI-DEVELOPERS" type="fix">44857 - Avoid OOM on unknown escher records when EscherMetafileBlip is incorrect</action>
+>>>>>>> .merge-right.r653675
            <action dev="POI-DEVELOPERS" type="add">HSLF: Support for getting embedded sounds from slide show </action>
            <action dev="POI-DEVELOPERS" type="add">HSLF: Initial support for rendering slides into images</action>
            <action dev="POI-DEVELOPERS" type="add">HSLF: Support for getting OLE object data from slide show </action>
index 8a38150ac10e53384545d15e40a237940c621386..56ebf3fa1d3c6e6851a97c9a15ce39e614f66c9a 100755 (executable)
@@ -19,7 +19,11 @@ POI Release Guide
   POI 3.0.2 and 3.1 were built using Ant 1.6.2 and Forrest 0.5\r
 \r
 (II) Making release artefacts\r
-  1. Update version id in build.xml. \r
+  1. Update version id in build.xml\r
+{code:xml}\r
+  <property name="version.id" value="3.1-beta1"/>\r
+{code}\r
+\r
   2. Tag current version. Include the current revision number in the comment\r
 \r
 {code}\r
@@ -46,7 +50,7 @@ https://svn.apache.org/repos/asf/poi/trunk
 \r
   5. Start a new section in sites.xml and status.xml. \r
 \r
-  6. Build as if the vote had passed. The buid date must be +7 days from current.\r
+  6. Build as if the vote had passed. The build date must be +7 days from current.\r
 {code}\r
 ant build\r
 {code}\r
@@ -109,27 +113,68 @@ Log-in on people.apache.org
 \r
 1. Go to ~/POI-3.1-BETA1\r
 \r
+zap previous version first.\r
+\r
+{code}\r
 cd ~/POI-3.1-BETA1/main\r
+{code}\r
 \r
 BETA and ALPHA releases:\r
+\r
+{code}\r
 cp *-src-* /www/www.apache.org/dist/poi/dev/src\r
 cp *-bin-* /www/www.apache.org/dist/poi/dev/bin\r
+{code}\r
 \r
 FINAL release:\r
+{code}\r
 cp *-src-* /www/www.apache.org/dist/poi/release/src\r
 cp *-bin-* /www/www.apache.org/dist/poi/release/bin\r
+{code}\r
 \r
+{code}\r
 cd ~/POI-3.1-BETA1/maven\r
-\r
 cp -r org.apache.poi /www/people.apache.org/repo/m1-ibiblio-rsync-repository/\r
 cp -r poi/poms      /www/people.apache.org/repo/m1-ibiblio-rsync-repository/poi\r
-\r
+{code}\r
 \r
 2. Make sure that the files are owned by the unix group apcvs and that they are writable by this group. \r
 \r
 3. Wait for the distributions to appear on your favourite mirror\r
 \r
-4. Send announcements:\r
+4. test maven\r
+create a simple project and make sure the release artifacts are accessible by maven:\r
+\r
+{code}\r
+$ mvn archetype:create -DgroupId=org.apache.poi.scratchpad -DartifactId=maven-test\r
+cd maven-test\r
+{code}\r
+edit pom.xml and add the release artefacts to the project dependencies: \r
+\r
+{code:xml}\r
+    <dependency>\r
+      <groupId>org.apache.poi</groupId>\r
+      <artifactId>poi</artifactId>\r
+      <version>3.1-beta1</version>\r
+    </dependency>\r
+    <dependency>\r
+      <groupId>org.apache.poi</groupId>\r
+      <artifactId>poi-scratchpad</artifactId>\r
+      <version>3.1-beta1</version>\r
+    </dependency>\r
+{code}\r
+\r
+{code}\r
+mvn compile \r
+{code}\r
+\r
+You should see [INFO] BUILD SUCCESSFUL in the end.\r
+\r
+5. Don't forget to upload the latest version of the site and javadocs\r
+\r
+6. Send announcements:\r
  - to poi-user and poi-dev lists\r
- - send announcements to announcement@apache.org, announcements@jakarta.apache.org\r
+ - to announcement@apache.org, announcements@jakarta.apache.org\r
+\r
+Note, announcements should be sent from your @apache.org e-mail address.\r
 \r
index 0eff3feeefa383e3934e35eac07c0a626aea5b7c..0ff00016d506a00d5b03284f84b00fc6ce517ed0 100644 (file)
@@ -38,6 +38,7 @@ public class EscherDggRecord
     private int field_3_numShapesSaved;
     private int field_4_drawingsSaved;
     private FileIdCluster[] field_5_fileIdClusters;
+    private int maxDgId;
 
     public static class FileIdCluster
     {
@@ -87,6 +88,7 @@ public class EscherDggRecord
         for (int i = 0; i < field_5_fileIdClusters.length; i++)
         {
             field_5_fileIdClusters[i] = new FileIdCluster(LittleEndian.getInt( data, pos + size ), LittleEndian.getInt( data, pos + size + 4 ));
+            maxDgId = Math.max(maxDgId, field_5_fileIdClusters[i].getDrawingGroupId());
             size += 8;
         }
         bytesRemaining         -= size;
@@ -229,7 +231,14 @@ public class EscherDggRecord
         this.field_4_drawingsSaved = field_4_drawingsSaved;
     }
 
-    public FileIdCluster[] getFileIdClusters()
+    /**
+     * @return The maximum drawing group ID
+     */
+    public int getMaxDrawingGroupId(){
+        return maxDgId;
+    }
+
+     public FileIdCluster[] getFileIdClusters()
     {
         return field_5_fileIdClusters;
     }
@@ -240,10 +249,23 @@ public class EscherDggRecord
     }
 
     public void addCluster( int dgId, int numShapedUsed )
+    {
+        addCluster(dgId, numShapedUsed, true);
+    }
+
+    /**
+     * Add a new cluster
+     *
+     * @param dgId  id of the drawing group (stored in the record options)
+     * @param numShapedUsed initial value of the numShapedUsed field
+     * @param sort if true then sort clusters by drawing group id.(
+     *  In Excel the clusters are sorted but in PPT they are not)
+     */
+    public void addCluster( int dgId, int numShapedUsed, boolean sort )
     {
         List clusters = new ArrayList(Arrays.asList(field_5_fileIdClusters));
         clusters.add(new FileIdCluster(dgId, numShapedUsed));
-        Collections.sort(clusters, new Comparator()
+        if(sort) Collections.sort(clusters, new Comparator()
         {
             public int compare( Object o1, Object o2 )
             {
@@ -257,6 +279,7 @@ public class EscherDggRecord
                     return +1;
             }
         } );
+        maxDgId = Math.min(maxDgId, dgId);
         field_5_fileIdClusters = (FileIdCluster[]) clusters.toArray( new FileIdCluster[clusters.size()] );
     }
 }
index f2dc1bb01427740e098a29dffcaba231a2f319e7..75c282ea59854e200fa57e21ea318fa8b4e3073e 100644 (file)
@@ -41,9 +41,20 @@ public class EscherMetafileBlip
     public static final short RECORD_ID_WMF = (short) 0xF018 + 3;
     public static final short RECORD_ID_PICT = (short) 0xF018 + 4;
 
+    /**
+     * BLIP signatures as defined in the escher spec
+     */
+    public static final short SIGNATURE_EMF  = 0x3D40;
+    public static final short SIGNATURE_WMF  = 0x2160;
+    public static final short SIGNATURE_PICT = 0x5420;
+
     private static final int HEADER_SIZE = 8;
 
     private byte[] field_1_UID;
+    /**
+     * The primary UID is only saved to disk if (blip_instance ^ blip_signature == 1)
+     */
+    private byte[] field_2_UID;
     private int field_2_cb;
     private int field_3_rcBounds_x1;
     private int field_3_rcBounds_y1;
@@ -72,6 +83,12 @@ public class EscherMetafileBlip
 
         field_1_UID = new byte[16];
         System.arraycopy( data, pos, field_1_UID, 0, 16 ); pos += 16;
+
+        if((getOptions() ^ getSignature()) == 0x10){
+            field_2_UID = new byte[16];
+            System.arraycopy( data, pos, field_2_UID, 0, 16 ); pos += 16;
+        }
+
         field_2_cb = LittleEndian.getInt( data, pos ); pos += 4;
         field_3_rcBounds_x1 = LittleEndian.getInt( data, pos ); pos += 4;
         field_3_rcBounds_y1 = LittleEndian.getInt( data, pos ); pos += 4;
@@ -118,9 +135,12 @@ public class EscherMetafileBlip
         int pos = offset;
         LittleEndian.putShort( data, pos, getOptions() ); pos += 2;
         LittleEndian.putShort( data, pos, getRecordId() ); pos += 2;
-        LittleEndian.putInt( data, getRecordSize() - HEADER_SIZE ); pos += 4;
+        LittleEndian.putInt( data, pos, getRecordSize() - HEADER_SIZE ); pos += 4;
 
-        System.arraycopy( field_1_UID, 0, data, pos, 16 ); pos += 16;
+        System.arraycopy( field_1_UID, 0, data, pos, field_1_UID.length ); pos += field_1_UID.length;
+        if((getOptions() ^ getSignature()) == 0x10){
+            System.arraycopy( field_2_UID, 0, data, pos, field_2_UID.length ); pos += field_2_UID.length;
+        }
         LittleEndian.putInt( data, pos, field_2_cb ); pos += 4;
         LittleEndian.putInt( data, pos, field_3_rcBounds_x1 ); pos += 4;
         LittleEndian.putInt( data, pos, field_3_rcBounds_y1 ); pos += 4;
@@ -135,7 +155,7 @@ public class EscherMetafileBlip
         System.arraycopy( raw_pictureData, 0, data, pos, raw_pictureData.length );
 
         listener.afterRecordSerialize(offset + getRecordSize(), getRecordId(), getRecordSize(), this);
-        return HEADER_SIZE + 16 + 1 + raw_pictureData.length;
+        return getRecordSize();
     }
 
     /**
@@ -161,7 +181,7 @@ public class EscherMetafileBlip
         }
         catch ( IOException e )
         {
-            log.log(POILogger.INFO, "Possibly corrupt compression or non-compressed data", e);
+            log.log(POILogger.WARN, "Possibly corrupt compression or non-compressed data", e);
             return data;
         }
     }
@@ -173,7 +193,11 @@ public class EscherMetafileBlip
      */
     public int getRecordSize()
     {
-        return 8 + 50 + raw_pictureData.length;
+        int size = 8 + 50 + raw_pictureData.length;
+        if((getOptions() ^ getSignature()) == 0x10){
+            size += field_2_UID.length;
+        }
+        return size;
     }
 
     public byte[] getUID()
@@ -186,6 +210,16 @@ public class EscherMetafileBlip
         this.field_1_UID = field_1_UID;
     }
 
+    public byte[] getPrimaryUID()
+    {
+        return field_2_UID;
+    }
+
+    public void setPrimaryUID( byte[] field_2_UID )
+    {
+        this.field_2_UID = field_2_UID;
+    }
+
     public int getUncompressedSize()
     {
         return field_2_cb;
@@ -264,6 +298,7 @@ public class EscherMetafileBlip
                 "  RecordId: 0x" + HexDump.toHex( getRecordId() ) + nl +
                 "  Options: 0x" + HexDump.toHex( getOptions() ) + nl +
                 "  UID: 0x" + HexDump.toHex( field_1_UID ) + nl +
+                (field_2_UID == null ? "" : ("  UID2: 0x" + HexDump.toHex( field_2_UID ) + nl)) +
                 "  Uncompressed Size: " + HexDump.toHex( field_2_cb ) + nl +
                 "  Bounds: " + getBounds() + nl +
                 "  Size in EMU: " + getSizeEMU() + nl +
@@ -273,4 +308,19 @@ public class EscherMetafileBlip
                 "  Extra Data:" + nl + extraData;
     }
 
+    /**
+     * Return the blip signature
+     *
+     * @return the blip signature
+     */
+    public short getSignature(){
+        short sig = 0;
+        switch(getRecordId()){
+            case RECORD_ID_EMF: sig = SIGNATURE_EMF; break;
+            case RECORD_ID_WMF: sig = SIGNATURE_WMF; break;
+            case RECORD_ID_PICT: sig = SIGNATURE_PICT; break;
+            default: log.log(POILogger.WARN, "Unknown metafile: " + getRecordId()); break;
+        }
+        return sig;
+    }
 }
index 8263d1b9b9c64cc530e6ec82bf0e720d6cde2d24..242a85f45f8e4bf1275d8ceb8a444be51ce41e60 100644 (file)
@@ -122,7 +122,7 @@ public class BiffViewer {
 
     private static void dumpNormal(Record record, int startloc, short rectype, short recsize)
     {
-        //System.out.println("Offset 0x" + Integer.toHexString(startloc) + " (" + startloc + ")");
+        System.out.println("Offset 0x" + Integer.toHexString(startloc) + " (" + startloc + ")");
         System.out.println( "recordid = 0x" + Integer.toHexString( rectype ) + ", size = " + recsize );
         System.out.println( record.toString() );
 
index 33966e932ecfe3e2a21ab28816ac39816e16310c..88a39ffe6fc5e45fd93aa64417240f57d697fcf4 100644 (file)
@@ -380,12 +380,13 @@ public final class FormulaParser {
         } else {
             isVarArgs = !fm.hasFixedArgsLength();
             funcIx = fm.getIndex();
+            validateNumArgs(numArgs, fm);
         }
         AbstractFunctionPtg retval;
         if(isVarArgs) {
             retval = new FuncVarPtg(name, (byte)numArgs);
         } else {
-            retval = new FuncPtg(funcIx, (byte)numArgs);
+            retval = new FuncPtg(funcIx);
         }
         if (!name.equals(AbstractFunctionPtg.FUNCTION_NAME_IF)) {
             // early return for everything else besides IF()
@@ -447,6 +448,29 @@ public final class FormulaParser {
         return retval;
     }
 
+    private void validateNumArgs(int numArgs, FunctionMetadata fm) {
+        if(numArgs < fm.getMinParams()) {
+            String msg = "Too few arguments to function '" + fm.getName() + "'. ";
+            if(fm.hasFixedArgsLength()) {
+                msg += "Expected " + fm.getMinParams();
+            } else {
+                msg += "At least " + fm.getMinParams() + " were expected";
+            }
+            msg += " but got " + numArgs + ".";
+            throw new FormulaParseException(msg);
+         }
+        if(numArgs > fm.getMaxParams()) {
+            String msg = "Too many arguments to function '" + fm.getName() + "'. ";
+            if(fm.hasFixedArgsLength()) {
+                msg += "Expected " + fm.getMaxParams();
+            } else {
+                msg += "At most " + fm.getMaxParams() + " were expected";
+            }
+            msg += " but got " + numArgs + ".";
+            throw new FormulaParseException(msg);
+       }
+    }
+
     private static boolean isArgumentDelimiter(char ch) {
         return ch ==  ',' || ch == ')';
     }
index 8fa3010a4b2ec6cbe64276d32d5e0db19ff1909b..08f2263182c873c14ea7a876524d8ad02186b7a9 100644 (file)
@@ -476,9 +476,9 @@ public class Workbook implements Model
     }
 
     /**
-     * Determines whether a workbook contains the privided sheet name.
+     * Determines whether a workbook contains the provided sheet name.
      *
-     * @param name the name to test
+     * @param name the name to test (case insensitive match)
      * @param excludeSheetIdx the sheet to exclude from the check or -1 to include all sheets in the check.
      * @return true if the sheet contains the name, false otherwise.
      */
@@ -487,7 +487,7 @@ public class Workbook implements Model
         for ( int i = 0; i < boundsheets.size(); i++ )
         {
             BoundSheetRecord boundSheetRecord = (BoundSheetRecord) boundsheets.get( i );
-            if (excludeSheetIdx != i && name.equals(boundSheetRecord.getSheetname()))
+            if (excludeSheetIdx != i && name.equalsIgnoreCase(boundSheetRecord.getSheetname()))
                 return true;
         }
         return false;
index 932c4547a1007737a9a5723ad056daa307c34ea4..b77dca3e174663e1ecc363ec9ce3a773c84863b8 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.
 ==================================================================== */
-        
 
-/*
- * ColumnInfoRecord.java
- *
- * Created on December 8, 2001, 8:44 AM
- */
 package org.apache.poi.hssf.record;
 
 import org.apache.poi.util.LittleEndian;
@@ -29,29 +22,28 @@ import org.apache.poi.util.BitField;
 import org.apache.poi.util.BitFieldFactory;
 
 /**
- * Title: ColumnInfo Record<P>
- * Description:  Defines with width and formatting for a range of columns<P>
- * REFERENCE:  PG 293 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)<P>
+ * Title: COLINFO Record<p/>
+ * Description:  Defines with width and formatting for a range of columns<p/>
+ * REFERENCE:  PG 293 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 ColumnInfoRecord
-    extends Record
-{
+public final class ColumnInfoRecord extends Record {
     public static final short     sid = 0x7d;
     private short                 field_1_first_col;
     private short                 field_2_last_col;
     private short                 field_3_col_width;
     private short                 field_4_xf_index;
     private short                 field_5_options;
-    static final private BitField hidden    = BitFieldFactory.getInstance(0x01);
-    static final private BitField outlevel  = BitFieldFactory.getInstance(0x0700);
-    static final private BitField collapsed = BitFieldFactory.getInstance(0x1000);
+    private static final BitField hidden    = BitFieldFactory.getInstance(0x01);
+    private static final BitField outlevel  = BitFieldFactory.getInstance(0x0700);
+    private static final BitField collapsed = BitFieldFactory.getInstance(0x1000);
+    // Excel seems write values 2, 10, and 260, even though spec says "must be zero"
     private short                 field_6_reserved;
 
     public ColumnInfoRecord()
     {
+        field_6_reserved = 2; // seems to be the most common value
     }
 
     /**
@@ -71,7 +63,18 @@ public class ColumnInfoRecord
         field_3_col_width = in.readShort();
         field_4_xf_index  = in.readShort();
         field_5_options   = in.readShort();
-        field_6_reserved  = in.readShort();
+        switch(in.remaining()) {
+            case 2: // usual case
+                field_6_reserved  = in.readShort();
+                break;
+            case 1:
+                // often COLINFO gets encoded 1 byte short
+                // shouldn't matter because this field is unused
+                field_6_reserved  = in.readByte(); 
+                break;
+            default:
+                throw new RuntimeException("Unusual record size remaining=(" + in.remaining() + ")");
+        }
     }
 
     protected void validateSid(short id)
index 2f56eb092a0b481ac460ca2a8cbaed5311d2d051..e375af365720a87b64a015d8195638ae98cf688c 100644 (file)
    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.LittleEndian;
-import org.apache.poi.util.POILogFactory;
-import org.apache.poi.util.POILogger;
 import org.apache.poi.util.StringUtil;
 
 /**
- * Title:        FileSharing<P>
+ * Title:        FILESHARING<P>
  * Description:  stores the encrypted readonly for a workbook (write protect) 
- * REFERENCE:  PG 314 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)<P>
+ * This functionality is accessed from the options dialog box available when performing 'Save As'.<p/>
+ * REFERENCE:  PG 314 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)<p/>
  * @author Andrew C. Oliver (acoliver at apache dot org)
  */
+public final class FileSharingRecord extends Record {
 
-public class FileSharingRecord extends Record {
-    private static POILogger logger = POILogFactory.getLogger(FileSharingRecord.class);
-       
     public final static short sid = 0x5b;
     private short             field_1_readonly;
     private short             field_2_password;
-    private byte              field_3_username_length;
-    private short             field_4_unknown; // not documented
-    private String            field_5_username;
+    private byte              field_3_username_unicode_options;
+    private String            field_3_username_value;
 
     public FileSharingRecord() {}
     
@@ -61,23 +56,15 @@ public class FileSharingRecord extends Record {
     protected void fillFields(RecordInputStream in) {
         field_1_readonly = in.readShort();
         field_2_password = in.readShort();
-        field_3_username_length = in.readByte();
         
-        // Is this really correct? The latest docs
-        //  seem to hint there's nothing between the
-        //  username length and the username string
-        field_4_unknown = in.readShort();
+        int nameLen = in.readShort();
         
-        // Ensure we don't try to read more data than
-        //  there actually is
-        if(field_3_username_length > in.remaining()) {
-               logger.log(POILogger.WARN, "FileSharingRecord defined a username of length " + field_3_username_length + ", but only " + in.remaining() + " bytes were left, truncating");
-               field_3_username_length = (byte)in.remaining();
-        }
-        if(field_3_username_length > 0) {
-               field_5_username = in.readCompressedUnicode(field_3_username_length);
+        if(nameLen > 0) {
+            // TODO - Current examples(3) from junits only have zero length username. 
+            field_3_username_unicode_options = in.readByte();
+            field_3_username_value = in.readCompressedUnicode(nameLen);
         } else {
-               field_5_username = "";
+            field_3_username_value = "";
         }
     }
 
@@ -135,45 +122,24 @@ public class FileSharingRecord extends Record {
     /**
      * @returns byte representing the length of the username field
      */
-    public byte getUsernameLength() {
-        return field_3_username_length ;
-    }
-
-    /**
-     * @param byte representing the length of the username field
-     */
-    public void setUsernameLength(byte length) {
-        this.field_3_username_length = length;
+    public short getUsernameLength() {
+        return (short) field_3_username_value.length();
     }
 
     /**
      * @returns username of the user that created the file
      */
     public String getUsername() {
-        return this.field_5_username;
+        return field_3_username_value;
     }
 
     /**
      * @param username of the user that created the file
      */
     public void setUsername(String username) {
-        this.field_5_username = username;
-        this.field_3_username_length = (byte)username.length();
-    }
-
-    /**
-     * @return short value of a "bonus field" in Excel that was not doc'd
-     */
-    public short getUnknown() {
-        return field_4_unknown;
+        field_3_username_value = username;
     }
 
-    /**
-     * @param unknown field value to set (bonus field that is not doc'd)
-     */
-    public void setUnknown(short unk) {
-        field_4_unknown = unk;
-    }
 
     public String toString() {
         StringBuffer buffer = new StringBuffer();
@@ -183,10 +149,6 @@ public class FileSharingRecord extends Record {
             .append(getReadOnly() == 1 ? "true" : "false").append("\n");
         buffer.append("    .password       = ")
             .append(Integer.toHexString(getPassword())).append("\n");
-        buffer.append("    .userlen        = ")
-            .append(Integer.toHexString(getUsernameLength())).append("\n");
-        buffer.append("    .unknown        = ")
-            .append(Integer.toHexString(getUnknown())).append("\n");
         buffer.append("    .username       = ")
             .append(getUsername()).append("\n");
         buffer.append("[/FILESHARING]\n");
@@ -194,18 +156,25 @@ public class FileSharingRecord extends Record {
     }
 
     public int serialize(int offset, byte [] data) {
+        // TODO - junit
         LittleEndian.putShort(data, 0 + offset, sid);
         LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize()-4));
         LittleEndian.putShort(data, 4 + offset, getReadOnly());
         LittleEndian.putShort(data, 6 + offset, getPassword());
-        data[ 8 + offset ] =  getUsernameLength();
-        LittleEndian.putShort(data, 9 + offset, getUnknown());
-        StringUtil.putCompressedUnicode( getUsername(), data, 11 + offset );
+        LittleEndian.putShort(data, 8 + offset, getUsernameLength());
+        if(getUsernameLength() > 0) {
+            LittleEndian.putByte(data, 10 + offset, field_3_username_unicode_options);
+            StringUtil.putCompressedUnicode( getUsername(), data, 11 + offset );
+        }
         return getRecordSize();
     }
 
     public int getRecordSize() {
-        return 11+getUsernameLength();
+        short nameLen = getUsernameLength();
+        if (nameLen < 1) {
+            return 10;
+        }
+        return 11+nameLen;
     }
 
     public short getSid() {
@@ -219,10 +188,7 @@ public class FileSharingRecord extends Record {
       FileSharingRecord clone = new FileSharingRecord();
       clone.setReadOnly(field_1_readonly);
       clone.setPassword(field_2_password);
-      clone.setUsernameLength(field_3_username_length);
-      clone.setUnknown(field_4_unknown);
-      clone.setUsername(field_5_username);
+      clone.setUsername(field_3_username_value);
       return clone;
     }
-
 }
index c405f901bd1078545a0c4cda01736063be1cd3a3..c20f5e6d32f3070a9c9616b00eeb43cb20b41776 100644 (file)
@@ -40,7 +40,7 @@ import org.apache.poi.util.LittleEndian;
  * @version 2.0-pre
  */
 
-public class FormulaRecord
+public final class FormulaRecord
     extends Record
     implements CellValueRecordInterface, Comparable
 {
@@ -108,6 +108,11 @@ public class FormulaRecord
         } catch (java.lang.UnsupportedOperationException uoe)  {
           throw new RecordFormatException(uoe);
         }
+        if (in.remaining() == 10) {
+            // TODO - this seems to occur when IntersectionPtg is present
+            // 10 extra bytes are just 0x01 and 0x00
+            // This causes POI stderr: "WARN. Unread 10 bytes of record 0x6"
+        }
     }
 
     //public void setRow(short row)
index 2dc2d940f4de1cf90247655c82d32450ac344361..c3fd5fb3dd8e51defc95ddbd457a9b90a643f89b 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.
 ==================================================================== */
-        
 
-/*
- * LabelRecord.java
- *
- * Created on November 11, 2001, 12:51 PM
- */
 package org.apache.poi.hssf.record;
 
 /**
@@ -33,14 +26,10 @@ package org.apache.poi.hssf.record;
  * @version 2.0-pre
  * @see org.apache.poi.hssf.record.LabelSSTRecord
  */
-
-public class LabelRecord
-    extends Record
-    implements CellValueRecordInterface
-{
+public final class LabelRecord extends Record implements CellValueRecordInterface {
     public final static short sid = 0x204;
-    //private short             field_1_row;
-    private int             field_1_row;
+
+    private int               field_1_row;
     private short             field_2_column;
     private short             field_3_xf_index;
     private short             field_4_string_len;
@@ -85,35 +74,30 @@ public class LabelRecord
 
     protected void fillFields(RecordInputStream in)
     {
-        //field_1_row          = LittleEndian.getShort(data, 0 + offset);
         field_1_row          = in.readUShort();
         field_2_column       = in.readShort();
         field_3_xf_index     = in.readShort();
         field_4_string_len   = in.readShort();
         field_5_unicode_flag = in.readByte();
         if (field_4_string_len > 0) {
-          if (isUnCompressedUnicode()) {
-            field_6_value = in.readUnicodeLEString(field_4_string_len);
-          } else {
-            field_6_value = in.readCompressedUnicode(field_4_string_len);
+            if (isUnCompressedUnicode()) {
+                field_6_value = in.readUnicodeLEString(field_4_string_len);
+            } else {
+                field_6_value = in.readCompressedUnicode(field_4_string_len);
+            }
+        } else {
+            field_6_value = "";
         }
-        } else field_6_value = null;
     }
 
-/* READ ONLY ACCESS... THIS IS FOR COMPATIBILITY ONLY...USE LABELSST!
-      public void setRow(short row) {
-        field_1_row = row;
-      }
-
-      public void setColumn(short col) {
-        field_2_column = col;
-      }
-
-      public void setXFIndex(short index) {
-        field_3_xf_index = index;
-      }
-  */
-    //public short getRow()
+/*
+ * READ ONLY ACCESS... THIS IS FOR COMPATIBILITY ONLY...USE LABELSST! public
+ * void setRow(short row) { field_1_row = row; }
+ * 
+ * public void setColumn(short col) { field_2_column = col; }
+ * 
+ * public void setXFIndex(short index) { field_3_xf_index = index; }
+ */
     public int getRow()
     {
         return field_1_row;
index a7715474ac4a26de2604d85c2e96cd6fd69522d0..2b0c50d122675828157fe158fd89486acbaa55bc 100755 (executable)
@@ -33,7 +33,7 @@ import org.apache.poi.hssf.record.formula.*;
  * @author Danny Mui at apache dot org
  */
 public final class SharedFormulaRecord extends Record {
-        public final static short   sid = 0x4BC;
+    public final static short   sid = 0x4BC;
     
     private int               field_1_first_row;
     private int               field_2_last_row;
@@ -186,6 +186,16 @@ public final class SharedFormulaRecord extends Record {
      * counter part
      */
     protected static Stack convertSharedFormulas(Stack ptgs, int formulaRow, int formulaColumn) {
+        if(false) {
+            /*
+             * TODO - (May-2008) Stop converting relative ref Ptgs in shared formula records. 
+             * If/when POI writes out the workbook, this conversion makes an unnecessary diff in the BIFF records.
+             * Disabling this code breaks one existing junit.
+             * Some fix-up will be required to make Ptg.toFormulaString(HSSFWorkbook) work properly.
+             * That method will need 2 extra params: rowIx and colIx.
+             */
+            return ptgs;
+        }
         Stack newPtgStack = new Stack();
 
         if (ptgs != null)
@@ -265,7 +275,7 @@ public final class SharedFormulaRecord extends Record {
         throw new RuntimeException("Shared Formula Conversion: Coding Error");
       }
     }
-    
+
     private static int fixupRelativeColumn(int currentcolumn, int column, boolean relative) {
         if(relative) {
             // mask out upper bits to produce 'wrapping' at column 256 ("IV")
index 7d44b008f8878981007787eadc5818974d1ce633..12f26bfdee5fdbd44df17eaca84923114eef2729 100755 (executable)
@@ -24,9 +24,8 @@ import org.apache.poi.util.LittleEndian;
 
 /**
  * To support Constant Values (2.5.7) as required by the CRN record.
- * This class should probably also be used for two dimensional arrays which are encoded by 
+ * This class is also used for two dimensional arrays which are encoded by 
  * EXTERNALNAME (5.39) records and Array tokens.<p/>
- * TODO - code in ArrayPtg should be merged with this code.  It currently supports only 2 of the constant types
  * 
  * @author Josh Micich
  */
index 2fc79a948f4d4d89ada4a46ccb5df6cb85fdafca..3421dd4a8e55fbafd8add2db92a763045c081902 100644 (file)
@@ -47,18 +47,31 @@ public class ErrorConstant {
        public int getErrorCode() {
                return _errorCode;
        }
+       public String getText() {
+               if(HSSFErrorConstants.isValidCode(_errorCode)) {
+                       return HSSFErrorConstants.getText(_errorCode);
+               }
+               return "unknown error code (" + _errorCode + ")";
+       }
 
        public static ErrorConstant valueOf(int errorCode) {
                switch (errorCode) {
-               case HSSFErrorConstants.ERROR_NULL:  return NULL;
-               case HSSFErrorConstants.ERROR_DIV_0: return DIV_0;
-               case HSSFErrorConstants.ERROR_VALUE: return VALUE;
-               case HSSFErrorConstants.ERROR_REF:   return REF;
-               case HSSFErrorConstants.ERROR_NAME:  return NAME;
-               case HSSFErrorConstants.ERROR_NUM:   return NUM;
-               case HSSFErrorConstants.ERROR_NA:    return NA;
+                       case HSSFErrorConstants.ERROR_NULL:  return NULL;
+                       case HSSFErrorConstants.ERROR_DIV_0: return DIV_0;
+                       case HSSFErrorConstants.ERROR_VALUE: return VALUE;
+                       case HSSFErrorConstants.ERROR_REF:   return REF;
+                       case HSSFErrorConstants.ERROR_NAME:  return NAME;
+                       case HSSFErrorConstants.ERROR_NUM:   return NUM;
+                       case HSSFErrorConstants.ERROR_NA:       return NA;
                }
                System.err.println("Warning - unexpected error code (" + errorCode + ")");
                return new ErrorConstant(errorCode);
        }
+       public String toString() {
+               StringBuffer sb = new StringBuffer(64);
+               sb.append(getClass().getName()).append(" [");
+               sb.append(getText());
+               sb.append("]");
+               return sb.toString();
+       }
 }
index 5133eca2064cb601838ca35d3b5c86f401af1b11..7cd704deba9783c136277265a6d2b901481bccf0 100644 (file)
@@ -147,10 +147,12 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
     }
 
     public byte getParameterClass(int index) {
-        try {
-            return paramClass[index];
-        } catch (ArrayIndexOutOfBoundsException aioobe) {
+        if (index >= paramClass.length) {
+            // For var-arg (and other?) functions, the metadata does not list all the parameter
+            // operand classes.  In these cases, all extra parameters are assumed to have the 
+            // same operand class as the last one specified.
             return paramClass[paramClass.length - 1];
         }
+        return paramClass[index];
     }
 }
index 3d406d3e39e0562590c39d2edba07b543bab9c2b..7667f65f5a09be34a64763501bfc2f84a15749cd 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.
 ==================================================================== */
 
-/*
- * AreaPtg.java
- *
- * Created on November 17, 2001, 9:30 PM
- */
 package org.apache.poi.hssf.record.formula;
 
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.BitField;
-
 import org.apache.poi.hssf.record.RecordInputStream;
-import org.apache.poi.hssf.util.AreaReference;
-import org.apache.poi.hssf.util.CellReference;
 import org.apache.poi.ss.usermodel.Workbook;
 
 /**
@@ -36,8 +25,7 @@ import org.apache.poi.ss.usermodel.Workbook;
  * @author Jason Height (jheight at chariot dot net dot au)
  */
 
-public class AreaNAPtg
-    extends AreaPtg
+public final class AreaNAPtg extends AreaPtg
 {
     public final static short sid  = 0x6D;
 
@@ -50,20 +38,16 @@ public class AreaNAPtg
       super(in);
     }
 
-    public void writeBytes(byte [] array, int offset) {
-      throw new RuntimeException("Coding Error: This method should never be called. This ptg should be converted");
-    }
-
     public String getAreaPtgName() {
       return "AreaNAPtg";
     }
 
     public String toFormulaString(Workbook book)
     {
-      throw new RuntimeException("Coding Error: This method should never be called. This ptg should be converted");
+      throw notImplemented();
     }
 
     public Object clone() {
-      throw new RuntimeException("Coding Error: This method should never be called. This ptg should be converted");
+      throw notImplemented();
     }
 }
index 4ff10d55f910a1bbd821917d15c75a62e5888674..ee939a7fca1a055e0eb9def56062406027f84f64 100644 (file)
@@ -36,8 +36,7 @@ import org.apache.poi.ss.usermodel.Workbook;
  * @author Jason Height (jheight at chariot dot net dot au)
  */
 
-public class AreaNPtg
-    extends AreaPtg
+public final class AreaNPtg extends AreaPtg
 {
   public final static short sid  = 0x2D;
 
@@ -50,23 +49,16 @@ public class AreaNPtg
     super(in);
   }
 
-  public void writeBytes(byte [] array, int offset) {
-    super.writeBytes(array,offset);
-    //this should be a warning...there doesn't seem to be any rationale to throwing an exception here...
-    //this excpeiton appears to break user defined named ranges...
-    //throw new RuntimeException("Coding Error: This method should never be called. This ptg should be converted");
-  }
-
   public String getAreaPtgName() {
     return "AreaNPtg";
   }
 
   public String toFormulaString(Workbook book)
   {
-    throw new RuntimeException("Coding Error: This method should never be called. This ptg should be converted");
+    throw notImplemented();
   }
 
   public Object clone() {
-    throw new RuntimeException("Coding Error: This method should never be called. This ptg should be converted");
+    throw notImplemented();
   }
 }
index 53ef8c8ee25872c6562b9bbcc1e55eb074ab58bb..f24745f4a7dba1e7118dd05ac7fdcd0b03e82348 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.
 ==================================================================== */
 
-/*
- * AreaPtg.java
- *
- * Created on November 17, 2001, 9:30 PM
- */
 package org.apache.poi.hssf.record.formula;
 
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.BitField;
-
 import org.apache.poi.hssf.record.RecordInputStream;
-import org.apache.poi.hssf.util.AreaReference;
-import org.apache.poi.hssf.util.CellReference;
 import org.apache.poi.ss.usermodel.Workbook;
 
 /**
@@ -36,10 +25,7 @@ import org.apache.poi.ss.usermodel.Workbook;
  * @author  andy
  * @author Jason Height (jheight at chariot dot net dot au)
  */
-
-public class AreaNVPtg
-    extends AreaPtg
-{
+public final class AreaNVPtg extends AreaPtg {
   public final static short sid  = 0x4D;
 
   protected AreaNVPtg() {
@@ -51,20 +37,16 @@ public class AreaNVPtg
     super(in);
   }
 
-  public void writeBytes(byte [] array, int offset) {
-    throw new RuntimeException("Coding Error: This method should never be called. This ptg should be converted");
-  }
-
   public String getAreaPtgName() {
     return "AreaNVPtg";
   }
 
   public String toFormulaString(Workbook book)
   {
-    throw new RuntimeException("Coding Error: This method should never be called. This ptg should be converted");
+    throw notImplemented();
   }
 
   public Object clone() {
-    throw new RuntimeException("Coding Error: This method should never be called. This ptg should be converted");
+    throw notImplemented();
   }
 }
index 3e3e6c8faeb9bc3184dd1e1b20b035606e0dafe8..c4e7534c7d8c10d2d283ca3639b32d99ac8be861 100644 (file)
@@ -15,7 +15,6 @@
    limitations under the License.
 ==================================================================== */
 
-
 package org.apache.poi.hssf.record.formula;
 
 import org.apache.poi.util.LittleEndian;
@@ -32,10 +31,15 @@ import org.apache.poi.hssf.record.RecordInputStream;
  * @author  andy
  * @author Jason Height (jheight at chariot dot net dot au)
  */
+public class AreaPtg extends Ptg implements AreaI {
+    /**
+     * TODO - (May-2008) fix subclasses of AreaPtg 'AreaN~' which are used in shared formulas.
+     * see similar comment in ReferencePtg
+     */
+    protected final RuntimeException notImplemented() {
+          return new RuntimeException("Coding Error: This method should never be called. This ptg should be converted");
+    }
 
-public class AreaPtg
-    extends Ptg implements AreaI
-{
     public final static short sid  = 0x25;
     private final static int  SIZE = 9;
     /** zero based, unsigned 16 bit */
index 2a0117862505dc014ac39a92ce46d5bb5db41137..e3ce03f9f59d107161234695a432e8129781005d 100644 (file)
 
 package org.apache.poi.hssf.record.formula;
 
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.BitField;
-import org.apache.poi.util.BitFieldFactory;
-import org.apache.poi.util.StringUtil;
-
-import org.apache.poi.hssf.util.CellReference;
-import org.apache.poi.ss.usermodel.Workbook;
-import org.apache.poi.hssf.record.RecordFormatException;
 import org.apache.poi.hssf.record.RecordInputStream;
-import org.apache.poi.hssf.record.SSTRecord;
 import org.apache.poi.hssf.record.UnicodeString;
+import org.apache.poi.hssf.record.constant.ConstantValueParser;
+import org.apache.poi.hssf.record.constant.ErrorConstant;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.util.LittleEndian;
 
 /**
  * ArrayPtg - handles arrays
  * 
- * The ArrayPtg is a little wierd, the size of the Ptg when parsing initially only
+ * The ArrayPtg is a little weird, the size of the Ptg when parsing initially only
  * includes the Ptg sid and the reserved bytes. The next Ptg in the expression then follows.
  * It is only after the "size" of all the Ptgs is met, that the ArrayPtg data is actually
  * held after this. So Ptg.createParsedExpression keeps track of the number of 
@@ -40,209 +35,160 @@ import org.apache.poi.hssf.record.UnicodeString;
  *  
  * @author Jason Height (jheight at chariot dot net dot au)
  */
-
-public class ArrayPtg extends Ptg
-{
-    public final static byte sid  = 0x20;
-    protected byte field_1_reserved;
-    protected byte field_2_reserved;
-    protected byte field_3_reserved;
-    protected byte field_4_reserved;
-    protected byte field_5_reserved;
-    protected byte field_6_reserved;
-    protected byte field_7_reserved;
-    
-    
-    protected short  token_1_columns;
-    protected short token_2_rows;
-    protected Object[][] token_3_arrayValues;
-
-    protected ArrayPtg() {
-      //Required for clone methods
-    }
-
-    public ArrayPtg(RecordInputStream in)
-    {
-       field_1_reserved = in.readByte();
-       field_2_reserved = in.readByte();
-       field_3_reserved = in.readByte();
-       field_4_reserved = in.readByte();
-       field_5_reserved = in.readByte();
-       field_6_reserved = in.readByte();
-       field_7_reserved = in.readByte();
-    }
-    
-    /** 
-     * Read in the actual token (array) values. This occurs 
-     * AFTER the last Ptg in the expression.
-     * See page 304-305 of Excel97-2007BinaryFileFormat(xls)Specification.pdf
-     */
-    public void readTokenValues(RecordInputStream in) {        
-        token_1_columns = (short)(0x00ff & in.readByte());
-        token_2_rows = in.readShort();
-        
-        //The token_1_columns and token_2_rows do not follow the documentation.
-        //The number of physical rows and columns is actually +1 of these values.
-        //Which is not explicitly documented.
-        token_1_columns++;
-        token_2_rows++;        
-        
-        token_3_arrayValues = new Object[token_1_columns][token_2_rows];
-        
-        for (int x=0;x<token_1_columns;x++) {
-                       for (int y=0;y<token_2_rows;y++) {
-                               byte grbit = in.readByte();
-                               if (grbit == 0x01) {
-                                       token_3_arrayValues[x][y] = new Double(in.readDouble());
-                               } else if (grbit == 0x02) {
-                                       //Ignore the doco, it is actually a unicode string with all the
-                                       //trimmings ie 16 bit size, option byte etc
-                                       token_3_arrayValues[x][y] = in.readUnicodeString();
-                               } else throw new RecordFormatException("Unknown grbit '"+grbit+"' at " + x + "," + y + " with " + in.remaining() + " bytes left");
+public class ArrayPtg extends Ptg {
+       public static final byte sid  = 0x20;
+
+       private static final int RESERVED_FIELD_LEN = 7;
+       // TODO - fix up field visibility and subclasses
+       protected byte[] field_1_reserved;
+       // data from these fields comes after the Ptg data of all tokens in current formula
+       protected short  token_1_columns;
+       protected short token_2_rows;
+       protected Object[] token_3_arrayValues;
+
+       protected ArrayPtg() {
+         //Required for clone methods
+       }
+
+       public ArrayPtg(RecordInputStream in)
+       {
+               field_1_reserved = new byte[RESERVED_FIELD_LEN];
+               // TODO - add readFully method to RecordInputStream
+               for(int i=0; i< RESERVED_FIELD_LEN; i++) {
+                       field_1_reserved[i] = in.readByte();
+               }
+       }
+       
+       /** 
+        * Read in the actual token (array) values. This occurs 
+        * AFTER the last Ptg in the expression.
+        * See page 304-305 of Excel97-2007BinaryFileFormat(xls)Specification.pdf
+        */
+       public void readTokenValues(RecordInputStream in) {
+               short nColumns = in.readUByte();
+               short nRows = in.readShort();
+               //The token_1_columns and token_2_rows do not follow the documentation.
+               //The number of physical rows and columns is actually +1 of these values.
+               //Which is not explicitly documented.
+               nColumns++;
+               nRows++;
+               
+               token_1_columns = nColumns;
+               token_2_rows = nRows;
+               
+               int totalCount = nRows * nColumns;
+               token_3_arrayValues = ConstantValueParser.parse(in, totalCount);
+       }
+
+       public String toString()
+       {
+               StringBuffer buffer = new StringBuffer("[ArrayPtg]\n");
+
+               buffer.append("columns = ").append(getColumnCount()).append("\n");
+               buffer.append("rows = ").append(getRowCount()).append("\n");
+               for (int x=0;x<getColumnCount();x++) {
+                       for (int y=0;y<getRowCount();y++) {
+                               Object o = token_3_arrayValues[getValueIndex(x, y)];
+                               buffer.append("[").append(x).append("][").append(y).append("] = ").append(o).append("\n"); 
+                       }
+               }
+               return buffer.toString();
+       }
+
+       /* package */ int getValueIndex(int colIx, int rowIx) {
+               if(colIx < 0 || colIx >= token_1_columns) {
+                       throw new IllegalArgumentException("Specified colIx (" + colIx 
+                                       + ") is outside the allowed range (0.." + (token_1_columns-1) + ")");
+               }
+               if(rowIx < 0 || rowIx >= token_2_rows) {
+                       throw new IllegalArgumentException("Specified rowIx (" + rowIx 
+                                       + ") is outside the allowed range (0.." + (token_2_rows-1) + ")");
+               }
+               return rowIx * token_1_columns + colIx;
+       }
+
+       public void writeBytes(byte[] data, int offset) {
+               
+               LittleEndian.putByte(data, offset + 0, sid + ptgClass);
+               System.arraycopy(field_1_reserved, 0, data, offset+1, RESERVED_FIELD_LEN);
+       }
+
+       public int writeTokenValueBytes(byte[] data, int offset) {
+
+               LittleEndian.putByte(data,  offset + 0, token_1_columns-1);
+               LittleEndian.putShort(data, offset + 1, (short)(token_2_rows-1));
+               ConstantValueParser.encode(data, offset + 3, token_3_arrayValues);
+               return 3 + ConstantValueParser.getEncodedSize(token_3_arrayValues);
+       }
+
+       public short getRowCount() {
+               return token_2_rows;
+       }
+
+       public short getColumnCount() {
+               return token_1_columns;
+       }
+
+       /** This size includes the size of the array Ptg plus the Array Ptg Token value size*/
+       public int getSize()
+       {
+               int size = 1+7+1+2;
+               size += ConstantValueParser.getEncodedSize(token_3_arrayValues);
+               return size;
+       }
+
+       public String toFormulaString(Workbook book)
+       {
+               StringBuffer b = new StringBuffer();
+               b.append("{");
+               for (int x=0;x<getColumnCount();x++) {
+                       if (x > 0) {
+                               b.append(";");
                        }
-        }
-    }
-
-    public String toString()
-    {
-        StringBuffer buffer = new StringBuffer("[ArrayPtg]\n");
-
-        buffer.append("columns = ").append(getColumnCount()).append("\n");
-        buffer.append("rows = ").append(getRowCount()).append("\n");
-        for (int x=0;x<getColumnCount();x++) {
-               for (int y=0;y<getRowCount();y++) {
-                       Object o = token_3_arrayValues[x][y];
-                               buffer.append("[").append(x).append("][").append(y).append("] = ").append(o).append("\n"); 
-               }
-        }
-        return buffer.toString();
-    }
-
-    public void writeBytes(byte [] array, int offset)
-    {
-        array[offset++] = (byte) (sid + ptgClass);
-        array[offset++] = field_1_reserved;
-        array[offset++] = field_2_reserved;
-        array[offset++] = field_3_reserved;
-        array[offset++] = field_4_reserved;
-        array[offset++] = field_5_reserved;
-        array[offset++] = field_6_reserved;
-        array[offset++] = field_7_reserved;
-        
-    }
-    public int writeTokenValueBytes(byte [] array, int offset) {
-       int pos = 0;
-       array[pos + offset] = (byte)(token_1_columns-1);
-        pos++;
-        LittleEndian.putShort(array, pos+offset, (short)(token_2_rows-1));
-        pos += 2;
-        for (int x=0;x<getColumnCount();x++) {
-               for (int y=0;y<getRowCount();y++) {
-                       Object o = token_3_arrayValues[x][y];
-                       if (o instanceof Double) {
-                               array[pos+offset] = 0x01;
-                               pos++;
-                               LittleEndian.putDouble(array, pos+offset, ((Double)o).doubleValue());
-                               pos+=8;
-                       } else if (o instanceof UnicodeString) {
-                               array[pos+offset] = 0x02;
-                               pos++;                          
-                               UnicodeString s = (UnicodeString)o;
-                               //JMH TBD Handle string continuation. Id do it now but its 4am.
-                       UnicodeString.UnicodeRecordStats stats = new UnicodeString.UnicodeRecordStats();
-                       s.serialize(stats, pos + offset, array);
-                       pos += stats.recordSize; 
-                       } else throw new RuntimeException("Coding error");
-               }
-        }
-        return pos;
-    }
-
-    public void setRowCount(short row)
-    {
-        token_2_rows = row;
-    }
-
-    public short getRowCount()
-    {
-        return token_2_rows;
-    }
-
-    public void setColumnCount(short col)
-    {
-        token_1_columns = (byte)col;
-    }
-
-    public short getColumnCount()
-    {
-        return token_1_columns;
-    }
-
-    /** This size includes the size of the array Ptg plus the Array Ptg Token value size*/
-    public int getSize()
-    {
-       int size = 1+7+1+2;
-        for (int x=0;x<getColumnCount();x++) {
-               for (int y=0;y<getRowCount();y++) {
-                       Object o = token_3_arrayValues[x][y];
-                       if (o instanceof UnicodeString) {
-                               size++;
-                       UnicodeString.UnicodeRecordStats rs = new UnicodeString.UnicodeRecordStats();
-                    ((UnicodeString)o).getRecordSize(rs);                              
-                               size += rs.recordSize;
-                       } else if (o instanceof Double) {
-                               size += 9;
-                       }
-               }
-        }
-        return size;
-    }
-
-    public String toFormulaString(Workbook book)
-    {
-       StringBuffer b = new StringBuffer();
-       b.append("{");
-        for (int x=0;x<getColumnCount();x++) {
-               for (int y=0;y<getRowCount();y++) {
-                       Object o = token_3_arrayValues[x][y];
-                       if (o instanceof String) {
-                               b.append((String)o);
-                       } else if (o instanceof Double) {
-                               b.append(((Double)o).doubleValue());
-                       }
-                       if (y != getRowCount())
-                               b.append(",");
-               }
-               if (x != getColumnCount())
-                       b.append(";");
-          }
-        b.append("}");
-        return b.toString();
-    }
-    
-    public byte getDefaultOperandClass() {
-        return Ptg.CLASS_ARRAY;
-    }
-    
-    public Object clone() {
-      ArrayPtg ptg = new ArrayPtg();
-      ptg.field_1_reserved = field_1_reserved;
-      ptg.field_2_reserved = field_2_reserved;
-      ptg.field_3_reserved = field_3_reserved;
-      ptg.field_4_reserved = field_4_reserved;
-      ptg.field_5_reserved = field_5_reserved;
-      ptg.field_6_reserved = field_6_reserved;
-      ptg.field_7_reserved = field_7_reserved;
-      
-      ptg.token_1_columns = token_1_columns;
-      ptg.token_2_rows = token_2_rows;
-      ptg.token_3_arrayValues = new Object[getColumnCount()][getRowCount()];
-      for (int x=0;x<getColumnCount();x++) {
-       for (int y=0;y<getRowCount();y++) {
-               ptg.token_3_arrayValues[x][y] = token_3_arrayValues[x][y];
-       }
-      }      
-      ptg.setClass(ptgClass);
-      return ptg;
-    }
+                       for (int y=0;y<getRowCount();y++) {
+                               if (y > 0) {
+                                       b.append(",");
+                               }
+                               Object o = token_3_arrayValues[getValueIndex(x, y)];
+                               b.append(getConstantText(o));
+                       }
+                 }
+               b.append("}");
+               return b.toString();
+       }
+       
+       private static String getConstantText(Object o) {
+
+               if (o == null) {
+                       return ""; // TODO - how is 'empty value' represented in formulas?
+               }
+               if (o instanceof UnicodeString) {
+                       return "\"" + ((UnicodeString)o).getString() + "\"";
+               }
+               if (o instanceof Double) {
+                       return ((Double)o).toString();
+               }
+               if (o instanceof Boolean) {
+                       ((Boolean)o).toString();
+               }
+               if (o instanceof ErrorConstant) {
+                       return ((ErrorConstant)o).getText();
+               }
+               throw new IllegalArgumentException("Unexpected constant class (" + o.getClass().getName() + ")");
+       }
+       
+       public byte getDefaultOperandClass() {
+               return Ptg.CLASS_ARRAY;
+       }
+       
+       public Object clone() {
+         ArrayPtg ptg = new ArrayPtg();
+         ptg.field_1_reserved = (byte[]) field_1_reserved.clone();
+         
+         ptg.token_1_columns = token_1_columns;
+         ptg.token_2_rows = token_2_rows;
+         ptg.token_3_arrayValues = (Object[]) token_3_arrayValues.clone();
+         ptg.setClass(ptgClass);
+         return ptg;
+       }
 }
index 8f567c967c4db58f115e7c21fa259f56d90a925f..5be866e963f048a3cf69893d3b96db5e548612fb 100644 (file)
 
 package org.apache.poi.hssf.record.formula;
 
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.BitField;
-import org.apache.poi.util.BitFieldFactory;
-import org.apache.poi.util.StringUtil;
-
-import org.apache.poi.hssf.util.CellReference;
-import org.apache.poi.ss.usermodel.Workbook;
-import org.apache.poi.hssf.record.RecordFormatException;
 import org.apache.poi.hssf.record.RecordInputStream;
-import org.apache.poi.hssf.record.SSTRecord;
-import org.apache.poi.hssf.record.UnicodeString;
 
 /**
  * ArrayPtgA - handles arrays
  *  
  * @author Jason Height (jheight at chariot dot net dot au)
  */
-
-public class ArrayPtgA extends ArrayPtg
-{
+public final class ArrayPtgA extends ArrayPtg {
     public final static byte sid  = 0x60;
 
-    protected ArrayPtgA() {
-       super();
+    private ArrayPtgA() {
       //Required for clone methods
     }
 
-    public ArrayPtgA(RecordInputStream in)
-    {
+    public ArrayPtgA(RecordInputStream in) {
        super(in);
     }
         
     public Object clone() {
       ArrayPtgA ptg = new ArrayPtgA();
-      ptg.field_1_reserved = field_1_reserved;
-      ptg.field_2_reserved = field_2_reserved;
-      ptg.field_3_reserved = field_3_reserved;
-      ptg.field_4_reserved = field_4_reserved;
-      ptg.field_5_reserved = field_5_reserved;
-      ptg.field_6_reserved = field_6_reserved;
-      ptg.field_7_reserved = field_7_reserved;
+      ptg.field_1_reserved = (byte[]) field_1_reserved.clone();
       
       ptg.token_1_columns = token_1_columns;
       ptg.token_2_rows = token_2_rows;
-      ptg.token_3_arrayValues = new Object[getColumnCount()][getRowCount()];
-      for (int x=0;x<getColumnCount();x++) {
-       for (int y=0;y<getRowCount();y++) {
-               ptg.token_3_arrayValues[x][y] = token_3_arrayValues[x][y];
-       }
-      }      
+      ptg.token_3_arrayValues = (Object[]) token_3_arrayValues.clone();
       ptg.setClass(ptgClass);
       return ptg;
     }
index 4c3e17c5ef6a71b1b0e86d03a49d27b52ab19974..ba955654aae9e11f061474ae80815cfaceaffb54 100644 (file)
 
 package org.apache.poi.hssf.record.formula;
 
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.BitField;
-import org.apache.poi.util.BitFieldFactory;
-import org.apache.poi.util.StringUtil;
-
-import org.apache.poi.hssf.util.CellReference;
-import org.apache.poi.ss.usermodel.Workbook;
-import org.apache.poi.hssf.record.RecordFormatException;
 import org.apache.poi.hssf.record.RecordInputStream;
-import org.apache.poi.hssf.record.SSTRecord;
-import org.apache.poi.hssf.record.UnicodeString;
 
 /**
  * ArrayPtg - handles arrays
  * 
- * The ArrayPtg is a little wierd, the size of the Ptg when parsing initially only
+ * The ArrayPtg is a little weird, the size of the Ptg when parsing initially only
  * includes the Ptg sid and the reserved bytes. The next Ptg in the expression then follows.
  * It is only after the "size" of all the Ptgs is met, that the ArrayPtg data is actually
  * held after this. So Ptg.createParsedExpression keeps track of the number of 
@@ -40,38 +30,24 @@ import org.apache.poi.hssf.record.UnicodeString;
  *  
  * @author Jason Height (jheight at chariot dot net dot au)
  */
-
-public class ArrayPtgV extends ArrayPtg
-{
+public final class ArrayPtgV extends ArrayPtg {
     public final static byte sid  = 0x40;
 
-    protected ArrayPtgV() {
+    private ArrayPtgV() {
       //Required for clone methods
     }
 
-    public ArrayPtgV(RecordInputStream in)
-    {
+    public ArrayPtgV(RecordInputStream in) {
        super(in);
     }
     
     public Object clone() {
       ArrayPtgV ptg = new ArrayPtgV();
-      ptg.field_1_reserved = field_1_reserved;
-      ptg.field_2_reserved = field_2_reserved;
-      ptg.field_3_reserved = field_3_reserved;
-      ptg.field_4_reserved = field_4_reserved;
-      ptg.field_5_reserved = field_5_reserved;
-      ptg.field_6_reserved = field_6_reserved;
-      ptg.field_7_reserved = field_7_reserved;
+      ptg.field_1_reserved = (byte[]) field_1_reserved.clone();
       
       ptg.token_1_columns = token_1_columns;
       ptg.token_2_rows = token_2_rows;
-      ptg.token_3_arrayValues = new Object[getColumnCount()][getRowCount()];
-      for (int x=0;x<getColumnCount();x++) {
-       for (int y=0;y<getRowCount();y++) {
-               ptg.token_3_arrayValues[x][y] = token_3_arrayValues[x][y];
-       }
-      }      
+      ptg.token_3_arrayValues = (Object[]) token_3_arrayValues.clone();
       ptg.setClass(ptgClass);
       return ptg;
     }
index 69edf88aae0ca05c2e9d0e7ccea3c7172467ef40..7901fb675d30aaf91eaf3c349b576882e9f3bcb4 100644 (file)
@@ -57,10 +57,12 @@ public final class FuncPtg extends AbstractFunctionPtg {
         }
         numParams = fm.getMinParams();
     }
-    public FuncPtg(int functionIndex, int numberOfParameters) {
+    public FuncPtg(int functionIndex) {
         field_2_fnc_index = (short) functionIndex;
-        numParams = numberOfParameters;
-        paramClass = new byte[] { Ptg.CLASS_VALUE, }; // TODO
+        FunctionMetadata fm = FunctionMetadataRegistry.getFunctionByIndex(functionIndex);
+        numParams = fm.getMinParams(); // same as max since these are not var-arg funcs
+        returnClass = fm.getReturnClassCode();
+        paramClass = fm.getParameterClassCodes();
     }
 
     public void writeBytes(byte[] array, int offset) {
index fb652713933d09b40fe64b0be60859cdf385c288..431dc5717b820969f231bf436ee9a5aab4139b97 100644 (file)
@@ -54,8 +54,8 @@ public final class FuncVarPtg extends AbstractFunctionPtg{
             returnClass = Ptg.CLASS_VALUE;
             paramClass = new byte[] {Ptg.CLASS_VALUE};
         } else {
-            returnClass = Ptg.CLASS_VALUE;
-            paramClass = new byte[] {Ptg.CLASS_VALUE};
+            returnClass = fm.getReturnClassCode();
+            paramClass = fm.getParameterClassCodes();
         }
     }
 
index 24c80d8f7bac7ebd307b6c0785ad3d2134166f81..8816a2d683c31d2f0a1b7b348a4f0e4b61b855c9 100644 (file)
@@ -30,24 +30,23 @@ import org.apache.poi.hssf.record.RecordInputStream;
  * @author avik
  * @author Jason Height (jheight at chariot dot net dot au)
  */
-
 public abstract class Ptg
 {
 
-        
+
     /* 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 ;
@@ -61,13 +60,13 @@ public abstract class Ptg
                         weHaveABracket = true;
                     } else {
                         o = (OperationPtg) operands.pop();
-                        while (!(o instanceof ParenthesisPtg)) { 
+                        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());
                     }
@@ -82,12 +81,16 @@ public abstract class Ptg
                 //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.
+     * Extra data (beyond <tt>size</tt>) may be read if and <tt>ArrayPtg</tt>s are present.
+     */
     public static Stack createParsedExpressionTokens(short size,  RecordInputStream in )
     {
         Stack stack = new Stack();
@@ -97,22 +100,25 @@ public abstract class Ptg
         {
             Ptg ptg = Ptg.createPtg( in );
             if (ptg instanceof ArrayPtg) {
-               if (arrayPtgs == null)
-                       arrayPtgs = new ArrayList(5);
-               arrayPtgs.add(ptg);
-               pos += 8;
+                if (arrayPtgs == null)
+                    arrayPtgs = new ArrayList(5);
+                arrayPtgs.add(ptg);
+                pos += 8;
             } else pos += ptg.getSize();
             stack.push( ptg );
         }
+        if(pos != size) {
+            throw new RuntimeException("Ptg array size mismatch");
+        }
         if (arrayPtgs != null) {
-               for (int i=0;i<arrayPtgs.size();i++) {
-                       ArrayPtg p = (ArrayPtg)arrayPtgs.get(i);
-                       p.readTokenValues(in);
-               }
+            for (int i=0;i<arrayPtgs.size();i++) {
+                ArrayPtg p = (ArrayPtg)arrayPtgs.get(i);
+                p.readTokenValues(in);
+            }
         }
         return stack;
     }
-    
+
     public static Ptg createPtg(RecordInputStream in)
     {
         byte id     = in.readByte();
@@ -123,142 +129,142 @@ public abstract class Ptg
              case ExpPtg.sid :                  // 0x01
                  retval = new ExpPtg(in);
                  break;
+
              case AddPtg.sid :                  // 0x03
                  retval = new AddPtg(in);
                  break;
-                 
+
              case SubtractPtg.sid :             // 0x04
                  retval = new SubtractPtg(in);
                  break;
-         
+
              case MultiplyPtg.sid :             // 0x05
                  retval = new MultiplyPtg(in);
                  break;
-                 
+
              case DividePtg.sid :               // 0x06
-                     retval = new DividePtg(in);
-                     break;
-                 
+                  retval = new DividePtg(in);
+                  break;
+
              case PowerPtg.sid :                // 0x07
                  retval = new PowerPtg(in);
                  break;
-                 
+
              case ConcatPtg.sid :               // 0x08
                  retval = new ConcatPtg(in);
-                                 break;
+                              break;
+
              case LessThanPtg.sid:              // 0x09
                  retval = new LessThanPtg(in);
-                                 break;
+                              break;
+
               case LessEqualPtg.sid :            // 0x0a
                  retval = new LessEqualPtg(in);
-                                 break;
+                              break;
+
              case EqualPtg.sid :                // 0x0b
                  retval = new EqualPtg(in);
-                                 break;
-                 
+                              break;
+
              case GreaterEqualPtg.sid :         // 0x0c
                  retval = new GreaterEqualPtg(in);
-                                 break;
-                 
+                              break;
+
              case GreaterThanPtg.sid :          // 0x0d
                  retval = new GreaterThanPtg(in);
-                                 break;
+                              break;
+
              case NotEqualPtg.sid :             // 0x0e
                  retval = new NotEqualPtg(in);
-                                 break;
+                              break;
+
              case IntersectionPtg.sid :         // 0x0f
                  retval = new IntersectionPtg(in);
-                                 break;
+                              break;
               case UnionPtg.sid :                // 0x10
                  retval = new UnionPtg(in);
-                                 break;
-                 
+                              break;
+
              case RangePtg.sid :                // 0x11
                  retval = new RangePtg(in);
-                                 break;
-                 
+                              break;
+
              case UnaryPlusPtg.sid :            // 0x12
                  retval = new UnaryPlusPtg(in);
-                                 break;
-                 
+                              break;
+
              case UnaryMinusPtg.sid :           // 0x13
                  retval = new UnaryMinusPtg(in);
-                                 break;
-                 
+                              break;
+
              case PercentPtg.sid :              // 0x14
                  retval = new PercentPtg(in);
-                                 break;
-                 
+                              break;
+
              case ParenthesisPtg.sid :          // 0x15
                  retval = new ParenthesisPtg(in);
-                                 break;
+                              break;
+
              case MissingArgPtg.sid :           // 0x16
                  retval = new MissingArgPtg(in);
-                                 break;
+                              break;
+
              case StringPtg.sid :               // 0x17
                 retval = new StringPtg(in);
                 break;
+
              case AttrPtg.sid :                 // 0x19
              case 0x1a :
                  retval = new AttrPtg(in);
-                                 break;
-                 
+                              break;
+
              case ErrPtg.sid :                  // 0x1c
                  retval = new ErrPtg(in);
-                                 break;
+                              break;
+
              case BoolPtg.sid :                 // 0x1d
                 retval = new BoolPtg(in);
                 break;
+
              case IntPtg.sid :                  // 0x1e
                  retval = new IntPtg(in);
-                                 break;
+                              break;
+
              case NumberPtg.sid :               // 0x1f
-                     retval = new NumberPtg(in);
-                     break;
-                 
+                  retval = new NumberPtg(in);
+                  break;
+
              case ArrayPtg.sid :                // 0x20
-               retval = new ArrayPtg(in);
-               break;
+                 retval = new ArrayPtg(in);
+                 break;
              case ArrayPtgV.sid :               // 0x40
-               retval = new ArrayPtgV(in);
-               break;
+                 retval = new ArrayPtgV(in);
+                 break;
              case ArrayPtgA.sid :               // 0x60
-               retval = new ArrayPtgA(in);
-               break;
-                 
+                 retval = new ArrayPtgA(in);
+                 break;
+
              case FuncPtg.sid :                 // 0x21
              case FuncPtg.sid + 0x20 :          // 0x41
              case FuncPtg.sid + 0x40 :          // 0x61
                  retval = new FuncPtg(in);
                  break;
-                 
+
              case FuncVarPtg.sid :              // 0x22
              case FuncVarPtg.sid + 0x20 :       // 0x42
              case FuncVarPtg.sid + 0x40 :       // 0x62
                  retval = new FuncVarPtg(in);
-                                 break;
-                 
-             case ReferencePtg.sid :            // 0x24  
+                              break;
+
+             case ReferencePtg.sid :            // 0x24
                  retval = new ReferencePtg(in);
-                                 break;
+                              break;
              case RefAPtg.sid :                 // 0x64
                  retval = new RefAPtg(in);
-                 break;   
+                 break;
              case RefVPtg.sid :                 // 0x44
                  retval = new RefVPtg(in);
-                 break;   
+                 break;
              case RefNAPtg.sid :                // 0x6C
                  retval = new RefNAPtg(in);
                  break;
@@ -267,11 +273,11 @@ public abstract class Ptg
                  break;
              case RefNVPtg.sid :                // 0x4C
                  retval = new RefNVPtg(in);
-                 break;                                  
-                 
-             case AreaPtg.sid :                 // 0x25          
+                 break;
+
+             case AreaPtg.sid :                 // 0x25
                  retval = new AreaPtg(in);
-                                 break;
+                              break;
              case AreaVPtg.sid:                 // 0x45
                  retval = new AreaVPtg(in);
                  break;
@@ -287,81 +293,81 @@ public abstract class Ptg
              case AreaNVPtg.sid :               // 0x4D
                 retval = new AreaNVPtg(in);
                 break;
-                 
+
              case MemAreaPtg.sid :              // 0x26
              case MemAreaPtg.sid + 0x40 :       // 0x46
              case MemAreaPtg.sid + 0x20 :       // 0x66
                  retval = new MemAreaPtg(in);
                  break;
-                 
+
              case MemErrPtg.sid :               // 0x27
              case MemErrPtg.sid + 0x20 :        // 0x47
              case MemErrPtg.sid + 0x40 :        // 0x67
                  retval = new MemErrPtg(in);
-                                 break;
-                 
+                              break;
+
              case MemFuncPtg.sid :              // 0x29
                  retval = new MemFuncPtg(in);
                  break;
-                 
+
              case RefErrorPtg.sid :             // 0x2a
              case RefErrorPtg.sid + 0x20 :      // 0x4a
              case RefErrorPtg.sid + 0x40 :      // 0x6a
                  retval = new RefErrorPtg(in);
-                                 break;
-                 
+                              break;
+
              case AreaErrPtg.sid :              // 0x2b
              case AreaErrPtg.sid + 0x20 :       // 0x4b
              case AreaErrPtg.sid + 0x40 :       // 0x6b
                  retval = new AreaErrPtg(in);
-                                 break;
-                 
+                              break;
+
              case NamePtg.sid :                 // 0x23
              case NamePtg.sid + 0x20 :          // 0x43
              case NamePtg.sid + 0x40 :          // 0x63
                  retval = new NamePtg(in);
                  break;
-                 
+
              case NameXPtg.sid :                // 0x39
              case NameXPtg.sid + 0x20 :         // 0x45
              case NameXPtg.sid + 0x40 :         // 0x79
                  retval = new NameXPtg(in);
-                                 break;
+                              break;
+
              case Area3DPtg.sid :               // 0x3b
              case Area3DPtg.sid + 0x20 :        // 0x5b
              case Area3DPtg.sid + 0x40 :        // 0x7b
                  retval = new Area3DPtg(in);
-                                 break;
+                              break;
+
              case Ref3DPtg.sid :                // 0x3a
              case Ref3DPtg.sid + 0x20:          // 0x5a
              case Ref3DPtg.sid + 0x40:          // 0x7a
                  retval = new Ref3DPtg(in);
-                                 break;
+                              break;
+
              case DeletedRef3DPtg.sid:          // 0x3c
              case DeletedRef3DPtg.sid + 0x20:   // 0x5c
              case DeletedRef3DPtg.sid + 0x40:   // 0x7c
                  retval = new DeletedRef3DPtg(in);
-                                 break;
-                 
+                              break;
+
              case DeletedArea3DPtg.sid :        // 0x3d
              case DeletedArea3DPtg.sid + 0x20 : // 0x5d
              case DeletedArea3DPtg.sid + 0x40 : // 0x7d
                  retval = new DeletedArea3DPtg(in);
                  break;
-                 
+
              case 0x00:
-                retval = new UnknownPtg();
-                break;
-                 
+                 retval = new UnknownPtg();
+                 break;
+
             default :
                  //retval = new UnknownPtg();
                  throw new java.lang.UnsupportedOperationException(" Unknown Ptg in Formula: 0x"+
                         Integer.toHexString(( int ) id) + " (" + ( int ) id + ")");
         }
-        
+
         if (id > 0x60) {
             retval.setClass(CLASS_ARRAY);
         } else if (id > 0x40) {
@@ -371,35 +377,35 @@ public abstract class Ptg
         }
 
        return retval;
-        
+
     }
-    
+
     public static int serializePtgStack(Stack expression, byte[] array, int offset) {
-       int pos = 0;
-       int size = 0;
-       if (expression != null)
-               size = expression.size();
-
-       List arrayPtgs = null;
-       
-       for (int k = 0; k < size; k++) {
-               Ptg ptg = ( Ptg ) expression.get(k);
-               
-               ptg.writeBytes(array, pos + offset);
-               if (ptg instanceof ArrayPtg) {
-                 if (arrayPtgs == null)
-                         arrayPtgs = new ArrayList(5);
-                 arrayPtgs.add(ptg);
-                 pos += 8;
-               } else pos += ptg.getSize();
-       }
-       if (arrayPtgs != null) {
-               for (int i=0;i<arrayPtgs.size();i++) {
-                       ArrayPtg p = (ArrayPtg)arrayPtgs.get(i);
-                       pos += p.writeTokenValueBytes(array, pos + offset);
-               }
-       }
-       return pos;
+        int pos = 0;
+        int size = 0;
+        if (expression != null)
+            size = expression.size();
+
+        List arrayPtgs = null;
+
+        for (int k = 0; k < size; k++) {
+            Ptg ptg = ( Ptg ) expression.get(k);
+
+            ptg.writeBytes(array, pos + offset);
+            if (ptg instanceof ArrayPtg) {
+              if (arrayPtgs == null)
+                  arrayPtgs = new ArrayList(5);
+              arrayPtgs.add(ptg);
+              pos += 8;
+            } else pos += ptg.getSize();
+        }
+        if (arrayPtgs != null) {
+            for (int i=0;i<arrayPtgs.size();i++) {
+                ArrayPtg p = (ArrayPtg)arrayPtgs.get(i);
+                pos += p.writeTokenValueBytes(array, pos + offset);
+            }
+        }
+        return pos;
     }
 
     public abstract int getSize();
@@ -414,7 +420,7 @@ public abstract class Ptg
     }
     /** write this Ptg to a byte array*/
     public abstract void writeBytes(byte [] array, int offset);
-    
+
     /**
      * return a string representation of this token alone
      */
@@ -425,7 +431,7 @@ public abstract class Ptg
     public String toDebugString() {
         byte[] ba = new byte[getSize()];
         String retval=null;
-        writeBytes(ba,0);        
+        writeBytes(ba,0);
         try {
             retval = org.apache.poi.util.HexDump.dump(ba,0,0);
         } catch (Exception e) {
@@ -433,7 +439,7 @@ public abstract class Ptg
         }
         return retval;
     }
-    
+
     /** Overridden toString method to ensure object hash is not printed.
      * This helps get rid of gratuitous diffs when comparing two dumps
      * Subclasses may output more relevant information by overriding this method
@@ -441,26 +447,26 @@ public abstract class Ptg
     public String toString(){
         return this.getClass().toString();
     }
-    
+
     public static final byte CLASS_REF = 0x00;
     public static final byte CLASS_VALUE = 0x20;
     public static final byte CLASS_ARRAY = 0x40;
-    
+
     protected byte ptgClass = CLASS_REF; //base ptg
-    
+
     public void setClass(byte thePtgClass) {
         ptgClass = thePtgClass;
     }
-    
+
     /** returns the class (REF/VALUE/ARRAY) for this Ptg */
     public byte getPtgClass() {
         return ptgClass;
     }
-    
+
     public abstract byte getDefaultOperandClass();
 
     public abstract Object clone();
 
-    
-    
+
+
 }
index f813ea23ce1315fd6c4c6a04e9b42047eab11761..4a9b85bc72a47c48be6c590ccba4271d0bdf08e1 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.
 ==================================================================== */
 
-/*
- * ValueReferencePtg.java
- *
- * Created on November 21, 2001, 5:27 PM
- */
 package org.apache.poi.hssf.record.formula;
 
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.BitField;
-
 import org.apache.poi.hssf.record.RecordInputStream;
-import org.apache.poi.hssf.util.CellReference;
 import org.apache.poi.ss.usermodel.Workbook;
 
 /**
  * RefNAPtg
  * @author Jason Height (jheight at chariot dot net dot au)
  */
-
-public class RefNAPtg extends ReferencePtg
+public final class RefNAPtg extends ReferencePtg
 {
     public final static byte sid  = 0x6C;
 
@@ -48,21 +37,16 @@ public class RefNAPtg extends ReferencePtg
       super(in);
     }
 
-    public void writeBytes(byte [] array, int offset)
-    {
-      throw new RuntimeException("Coding Error: This method should never be called. This ptg should be converted");
-    }
-
     public String getRefPtgName() {
       return "RefNAPtg";
     }
 
     public String toFormulaString(Workbook book)
     {
-      throw new RuntimeException("Coding Error: This method should never be called. This ptg should be converted");
+      throw notImplemented();
     }
 
     public Object clone() {
-      throw new RuntimeException("Coding Error: This method should never be called. This ptg should be converted");
+        throw notImplemented();
     }
 }
index fc19859b50458355e7ca976850ead1a808d99dfc..5c8d93c79e019f70fb41110411a44c004406c004 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.
 ==================================================================== */
 
-/*
- * RefNPtg.java
- *
- */
 package org.apache.poi.hssf.record.formula;
 
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.BitField;
-
 import org.apache.poi.hssf.record.RecordInputStream;
-import org.apache.poi.hssf.util.CellReference;
 import org.apache.poi.ss.usermodel.Workbook;
 
 /**
  * RefNPtg
  * @author Jason Height (jheight at apache dot com)
  */
-
-public class RefNPtg extends ReferencePtg
+public final class RefNPtg extends ReferencePtg
 {
     public final static byte sid  = 0x2C;
 
@@ -49,21 +39,16 @@ public class RefNPtg extends ReferencePtg
       super(in);
     }
 
-    public void writeBytes(byte [] array, int offset)
-    {
-      throw new RuntimeException("Coding Error: This method should never be called. This ptg should be converted");
-    }
-
     public String getRefPtgName() {
       return "RefNPtg";
     }
 
     public String toFormulaString(Workbook book)
     {
-      throw new RuntimeException("Coding Error: This method should never be called. This ptg should be converted");
+        throw notImplemented();
     }
 
     public Object clone() {
-      throw new RuntimeException("Coding Error: This method should never be called. This ptg should be converted");
+        throw notImplemented();
     }
 }
index 52b2e51fb2db20bdfd0ac3dac86f46a0d3fb638e..cb2f663d2698472d11525759165c6458bdb9f837 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
 
 package org.apache.poi.hssf.record.formula;
 
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.BitField;
-
 import org.apache.poi.hssf.record.RecordInputStream;
-import org.apache.poi.hssf.util.CellReference;
 import org.apache.poi.ss.usermodel.Workbook;
 
 /**
@@ -30,8 +25,7 @@ import org.apache.poi.ss.usermodel.Workbook;
  * @author Jason Height (jheight at chariot dot net dot au)
  */
 
-public class RefNVPtg extends ReferencePtg
-{
+public final class RefNVPtg extends ReferencePtg {
   public final static byte sid  = 0x4C;
 
   protected RefNVPtg() {
@@ -45,21 +39,16 @@ public class RefNVPtg extends ReferencePtg
     super(in);
   }
 
-  public void writeBytes(byte [] array, int offset)
-  {
-    throw new RuntimeException("Coding Error: This method should never be called. This ptg should be converted");
-  }
-
   public String getRefPtgName() {
     return "RefNVPtg";
   }
 
   public String toFormulaString(Workbook book)
   {
-    throw new RuntimeException("Coding Error: This method should never be called. This ptg should be converted");
+      throw notImplemented();
   }
 
   public Object clone() {
-    throw new RuntimeException("Coding Error: This method should never be called. This ptg should be converted");
+      throw notImplemented();
   }
 }
index ff6f4a20eea3df16a9e4d0a9a2e01ffba672d763..4486ec087ca7f948bd5de72e67fc2d15f4623375 100644 (file)
@@ -30,8 +30,20 @@ import org.apache.poi.hssf.record.RecordInputStream;
  * @author  Andrew C. Oliver (acoliver@apache.org)
  * @author Jason Height (jheight at chariot dot net dot au)
  */
-
 public class ReferencePtg extends Ptg {
+    /**
+     * TODO - (May-2008) fix subclasses of ReferencePtg 'RefN~' which are used in shared formulas.
+     * (See bugzilla 44921)
+     * The 'RefN~' instances do not work properly, and are expected to be converted by 
+     * SharedFormulaRecord.convertSharedFormulas().  
+     * This conversion currently does not take place for formulas of named ranges, conditional 
+     * format rules and data validation rules.  
+     * Furthermore, conversion is probably not appropriate in those instances.
+     */
+    protected final RuntimeException notImplemented() {
+        return new RuntimeException("Coding Error: This method should never be called. This ptg should be converted");
+    }
+
     private final static int SIZE = 5;
     public final static byte sid  = 0x24;
     private final static int MAX_ROW_NUMBER = 65536;             
index b6615182d3838e84fc1a6fcb69b8acf35f654c88..c90590d1b81415b9963fb47cacc4954fa3491731 100644 (file)
@@ -24,106 +24,123 @@ import org.apache.poi.util.StringUtil;
 import org.apache.poi.hssf.record.RecordInputStream;
 
 /**
- * Number
- * Stores a String value in a formula value stored in the format &lt;length 2 bytes&gt;char[]
- * @author  Werner Froidevaux
+ * String Stores a String value in a formula value stored in the format
+ * &lt;length 2 bytes&gt;char[]
+ * 
+ * @author Werner Froidevaux
  * @author Jason Height (jheight at chariot dot net dot au)
  * @author Bernard Chesnoy
  */
-
-public class StringPtg
-    extends Ptg
-{
-    public final static int  SIZE = 9;
-    public final static byte sid  = 0x17;
-    //NOTE: OO doc says 16bit lenght, but BiffViewer says 8
-    // Book says something totally different, so dont look there!
-    int field_1_length;
-    byte field_2_options;
-    BitField fHighByte = BitFieldFactory.getInstance(0x01);
-    private String            field_3_string;
+public final class StringPtg extends Ptg {
+    public final static int SIZE = 9;
+    public final static byte sid = 0x17;
+    private static final BitField fHighByte = BitFieldFactory.getInstance(0x01);
+    /** the character (")used in formulas to delimit string literals */
+    private static final char FORMULA_DELIMITER = '"';
+
+    /**
+     * NOTE: OO doc says 16bit length, but BiffViewer says 8 Book says something
+     * totally different, so don't look there!
+     */
+    private int field_1_length;
+    private byte field_2_options;
+    private String field_3_string;
 
     private StringPtg() {
-      //Required for clone methods
+        // Required for clone methods
     }
 
     /** Create a StringPtg from a byte array read from disk */
-    public StringPtg(RecordInputStream in)
-    {
-        field_1_length = in.readByte() & 0xFF;
+    public StringPtg(RecordInputStream in) {
+        field_1_length = in.readUByte();
         field_2_options = in.readByte();
         if (fHighByte.isSet(field_2_options)) {
-            field_3_string= in.readUnicodeLEString(field_1_length);
-        }else {
-            field_3_string=in.readCompressedUnicode(field_1_length);
+            field_3_string = in.readUnicodeLEString(field_1_length);
+        } else {
+            field_3_string = in.readCompressedUnicode(field_1_length);
         }
 
-        //setValue(new String(data, offset+3, data[offset+1] + 256*data[offset+2]));
+        // setValue(new String(data, offset+3, data[offset+1] + 256*data[offset+2]));
     }
 
-    /** Create a StringPtg from a string representation of  the number
-     *  Number format is not checked, it is expected to be validated in the parser
-     *   that calls this method.
-     *  @param value : String representation of a floating point number
+    /**
+     * Create a StringPtg from a string representation of the number Number
+     * format is not checked, it is expected to be validated in the parser that
+     * calls this method.
+     * 
+     * @param value :
+     *            String representation of a floating point number
      */
     public StringPtg(String value) {
-        if (value.length() >255) {
-            throw new IllegalArgumentException("String literals in formulas cant be bigger than 255 characters ASCII");
+        if (value.length() > 255) {
+            throw new IllegalArgumentException(
+                    "String literals in formulas can't be bigger than 255 characters ASCII");
         }
-        this.field_2_options=0;        
-        field_2_options = (byte)this.fHighByte.setBoolean(field_2_options, StringUtil.hasMultibyte(value));
-        this.field_3_string=value;
-        this.field_1_length=value.length(); //for the moment, we support only ASCII strings in formulas we create
+        field_2_options = 0;
+        field_2_options = (byte) fHighByte.setBoolean(field_2_options, StringUtil
+                .hasMultibyte(value));
+        field_3_string = value;
+        field_1_length = value.length(); // for the moment, we support only ASCII strings in formulas we create
     }
 
-    /*
-    public void setValue(String value)
-    {
-        field_1_value = value;
-    }*/
-
-
-    public String getValue()
-    {
+    public String getValue() {
         return field_3_string;
     }
 
-    public void writeBytes(byte [] array, int offset)
-    {
-        array[ offset + 0 ] = sid;
-        array[ offset + 1 ] = (byte)field_1_length;
-        array[ offset + 2 ] = field_2_options;
+    public void writeBytes(byte[] array, int offset) {
+        array[offset + 0] = sid;
+        array[offset + 1] = (byte) field_1_length;
+        array[offset + 2] = field_2_options;
         if (fHighByte.isSet(field_2_options)) {
-            StringUtil.putUnicodeLE(getValue(),array,offset+3);
-        }else {
-            StringUtil.putCompressedUnicode(getValue(),array,offset+3);
+            StringUtil.putUnicodeLE(getValue(), array, offset + 3);
+        } else {
+            StringUtil.putCompressedUnicode(getValue(), array, offset + 3);
         }
     }
 
-    public int getSize()
-    {
+    public int getSize() {
         if (fHighByte.isSet(field_2_options)) {
-            return 2*field_1_length+3;
+            return 2 * field_1_length + 3;
         } else {
-            return field_1_length+3;
+            return field_1_length + 3;
         }
     }
 
-    public String toFormulaString(Workbook book)
-    {
-        return "\""+getValue()+"\"";
+    public String toFormulaString(Workbook book) {
+        String value = field_3_string;
+        int len = value.length();
+        StringBuffer sb = new StringBuffer(len + 4);
+        sb.append(FORMULA_DELIMITER);
+
+        for (int i = 0; i < len; i++) {
+            char c = value.charAt(i);
+            if (c == FORMULA_DELIMITER) {
+                sb.append(FORMULA_DELIMITER);
+            }
+            sb.append(c);
+        }
+
+        sb.append(FORMULA_DELIMITER);
+        return sb.toString();
     }
+
     public byte getDefaultOperandClass() {
-       return Ptg.CLASS_VALUE;
-   }
+        return Ptg.CLASS_VALUE;
+    }
 
-   public Object clone() {
-     StringPtg ptg = new StringPtg();
-     ptg.field_1_length = field_1_length;
-     ptg.field_2_options=field_2_options;
-     ptg.field_3_string=field_3_string;
-     return ptg;
-   }
+    public Object clone() {
+        StringPtg ptg = new StringPtg();
+        ptg.field_1_length = field_1_length;
+        ptg.field_2_options = field_2_options;
+        ptg.field_3_string = field_3_string;
+        return ptg;
+    }
 
+    public String toString() {
+        StringBuffer sb = new StringBuffer(64);
+        sb.append(getClass().getName()).append(" [");
+        sb.append(field_3_string);
+        sb.append("]");
+        return sb.toString();
+    }
 }
-
index 2009ebae1ff4793feaa7139d53ece759cb07511c..789be63240e4e4068200592f9a6af2248f58eaef 100644 (file)
@@ -42,8 +42,10 @@ final class FunctionDataBuilder {
                _mutatingFunctionIndexes = new HashSet();
        }
 
-       public void add(int functionIndex, String functionName, int minParams, int maxParams, boolean hasFootnote) {
-               FunctionMetadata fm = new FunctionMetadata(functionIndex, functionName, minParams, maxParams);
+       public void add(int functionIndex, String functionName, int minParams, int maxParams,
+                       byte returnClassCode, byte[] parameterClassCodes, boolean hasFootnote) {
+               FunctionMetadata fm = new FunctionMetadata(functionIndex, functionName, minParams, maxParams,
+                               returnClassCode, parameterClassCodes);
                
                Integer indexKey = new Integer(functionIndex);
                
index 94f1659b5128bf11d7a561e5527fbc41d6b3de3b..fc5f845437a76bcb767e2eb4e6addc0c556dac7a 100644 (file)
@@ -27,12 +27,17 @@ public final class FunctionMetadata {
        private final String _name;
        private final int _minParams;
        private final int _maxParams;
+       private final byte _returnClassCode;
+       private final byte[] _parameterClassCodes;
 
-       /* package */ FunctionMetadata(int index, String name, int minParams, int maxParams) {
+       /* package */ FunctionMetadata(int index, String name, int minParams, int maxParams,
+                       byte returnClassCode, byte[] parameterClassCodes) {
                _index = index;
                _name = name;
                _minParams = minParams;
                _maxParams = maxParams;
+               _returnClassCode = returnClassCode;
+               _parameterClassCodes = parameterClassCodes;
        }
        public int getIndex() {
                return _index;
@@ -49,6 +54,12 @@ public final class FunctionMetadata {
        public boolean hasFixedArgsLength() {
                return _minParams == _maxParams;
        }
+       public byte getReturnClassCode() {
+               return _returnClassCode;
+       }
+       public byte[] getParameterClassCodes() {
+               return (byte[]) _parameterClassCodes.clone();
+       }
        public String toString() {
                StringBuffer sb = new StringBuffer(64);
                sb.append(getClass().getName()).append(" [");
index bd72a9223374ff4b71e80f6310bcc74b96864f24..2cdc540e6f22fb922422e4a56b63ce7ac5a4a965 100644 (file)
@@ -26,6 +26,8 @@ import java.util.HashSet;
 import java.util.Set;
 import java.util.regex.Pattern;
 
+import org.apache.poi.hssf.record.formula.Ptg;
+
 /**
  * Converts the text meta-data file into a <tt>FunctionMetadataRegistry</tt>
  * 
@@ -36,6 +38,12 @@ final class FunctionMetadataReader {
        private static final String METADATA_FILE_NAME = "functionMetadata.txt";
 
        private static final Pattern TAB_DELIM_PATTERN = Pattern.compile("\t");
+       private static final Pattern SPACE_DELIM_PATTERN = Pattern.compile(" ");
+       private static final byte[] EMPTY_BYTE_ARRAY = { };
+
+       // special characters from the ooo document
+       private static final int CHAR_ELLIPSIS_8230 = 8230;
+       private static final int CHAR_NDASH_8211 = 8211;
        
        private static final String[] DIGIT_ENDING_FUNCTION_NAMES = {
                // Digits at the end of a function might be due to a left-over footnote marker.
@@ -86,14 +94,66 @@ final class FunctionMetadataReader {
                String functionName = parts[1];
                int minParams = parseInt(parts[2]);
                int maxParams = parseInt(parts[3]);
-               // 4 returnClass
-               // 5 parameterClasses
+               byte returnClassCode = parseReturnTypeCode(parts[4]);
+               byte[] parameterClassCodes = parseOperandTypeCodes(parts[5]);
                // 6 isVolatile
                boolean hasNote = parts[7].length() > 0;
 
                validateFunctionName(functionName);
-               // TODO - make POI use returnClass, parameterClasses, isVolatile
-               fdb.add(functionIndex, functionName, minParams, maxParams, hasNote);
+               // TODO - make POI use isVolatile
+               fdb.add(functionIndex, functionName, minParams, maxParams, 
+                               returnClassCode, parameterClassCodes, hasNote);
+       }
+       
+
+       private static byte parseReturnTypeCode(String code) {
+               if(code.length() == 0) {
+                       return Ptg.CLASS_REF; // happens for GETPIVOTDATA
+               }
+               return parseOperandTypeCode(code);
+       }
+
+       private static byte[] parseOperandTypeCodes(String codes) {
+               if(codes.length() < 1) {
+                       return EMPTY_BYTE_ARRAY; // happens for GETPIVOTDATA
+               }
+               if(isDash(codes)) {
+                       // '-' means empty:
+                       return EMPTY_BYTE_ARRAY;
+               }
+               String[] array = SPACE_DELIM_PATTERN.split(codes);
+               int nItems = array.length;
+               if(array[nItems-1].charAt(0) == CHAR_ELLIPSIS_8230) {
+                       nItems --;
+               }
+               byte[] result = new byte[nItems];
+               for (int i = 0; i < nItems; i++) {
+                       result[i] = parseOperandTypeCode(array[i]);
+               }
+               return result;
+       }
+
+       private static boolean isDash(String codes) {
+               if(codes.length() == 1) {
+                       switch (codes.charAt(0)) {
+                               case '-':
+                               case CHAR_NDASH_8211: // this is what the ooo doc has
+                                       return true;
+                       }
+               }
+               return false;
+       }
+
+       private static byte parseOperandTypeCode(String code) {
+               if(code.length() != 1) {
+                       throw new RuntimeException("Bad operand type code format '" + code  + "' expected single char");
+               }
+               switch(code.charAt(0)) {
+                       case 'V': return Ptg.CLASS_VALUE;
+                       case 'R': return Ptg.CLASS_REF;
+                       case 'A': return Ptg.CLASS_ARRAY;
+               }
+               throw new IllegalArgumentException("Unexpected operand type code '" + code + "'");
        }
 
        /**
index 8607e5cf7b0cc558cac2c25602733cc3d897ddcc..077035055392bef94c3b8bb215b5dcdb82c46bde 100644 (file)
@@ -17,7 +17,9 @@
 
 package org.apache.poi.hssf.usermodel;
 
+import org.apache.poi.ddf.EscherBitmapBlip;
 import org.apache.poi.ddf.EscherBlipRecord;
+import org.apache.poi.ddf.EscherMetafileBlip;
 import org.apache.poi.ss.usermodel.PictureData;
 
 /**
@@ -65,19 +67,19 @@ public class HSSFPictureData implements PictureData
      */
     public String suggestFileExtension()
     {
-        switch (blip.getOptions() & FORMAT_MASK)
+        switch (blip.getRecordId())
         {
-            case MSOBI_WMF:
+            case EscherMetafileBlip.RECORD_ID_WMF:
                 return "wmf";
-            case MSOBI_EMF:
+            case EscherMetafileBlip.RECORD_ID_EMF:
                 return "emf";
-            case MSOBI_PICT:
+            case EscherMetafileBlip.RECORD_ID_PICT:
                 return "pict";
-            case MSOBI_PNG:
+            case EscherBitmapBlip.RECORD_ID_PNG:
                 return "png";
-            case MSOBI_JPEG:
+            case EscherBitmapBlip.RECORD_ID_JPEG:
                 return "jpeg";
-            case MSOBI_DIB:
+            case EscherBitmapBlip.RECORD_ID_DIB:
                 return "dib";
             default:
                 return "";
index 230d4bea389ef56a798a929e6c6bf6472d229bfa..c829553325def111d56a1527ec4083af4341e753 100644 (file)
@@ -1728,6 +1728,8 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
 
             HSSFCellStyle style = cell.getCellStyle();
             HSSFFont font = wb.getFontAt(style.getFontIndex());
+            //the number of spaces to indent the text in the cell
+            int indention = style.getIndention();
 
             if (cell.getCellType() == HSSFCell.CELL_TYPE_STRING) {
                 HSSFRichTextString rt = cell.getRichStringCellValue();
@@ -1760,9 +1762,9 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
                         trans.concatenate(
                         AffineTransform.getScaleInstance(1, fontHeightMultiple)
                         );
-                        width = Math.max(width, layout.getOutline(trans).getBounds().getWidth() / defaultCharWidth);
+                        width = Math.max(width, layout.getOutline(trans).getBounds().getWidth() / defaultCharWidth + indention);
                     } else {
-                        width = Math.max(width, layout.getBounds().getWidth() / defaultCharWidth);
+                        width = Math.max(width, layout.getBounds().getWidth() / defaultCharWidth + indention);
                     }
                 }
             } else {
@@ -1805,15 +1807,15 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
                         trans.concatenate(
                         AffineTransform.getScaleInstance(1, fontHeightMultiple)
                         );
-                        width = Math.max(width, layout.getOutline(trans).getBounds().getWidth() / defaultCharWidth);
+                        width = Math.max(width, layout.getOutline(trans).getBounds().getWidth() / defaultCharWidth + indention);
                     } else {
-                        width = Math.max(width, layout.getBounds().getWidth() / defaultCharWidth);
+                        width = Math.max(width, layout.getBounds().getWidth() / defaultCharWidth + indention);
                     }
                 }
             }
 
             if (width != -1) {
-                if (width > Short.MAX_VALUE) { //width can be bigger that Short.MAX_VALUE!
+                if (width > Short.MAX_VALUE) { //calculated width can be greater that Short.MAX_VALUE!
                      width = Short.MAX_VALUE;
                 }
                 sheet.setColumnWidth(column, (short) (width * 256));
index 4b44fb5c4c14d4b1d5936241f9511c5d2e391e08..547f761fb7b17e0279bcb77ee672f66d3b849aa2 100644 (file)
@@ -182,9 +182,45 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
     public HSSFWorkbook(POIFSFileSystem fs, boolean preserveNodes)
             throws IOException
     {
-       this(fs.getRoot(), fs, preserveNodes);
+        this(fs.getRoot(), fs, preserveNodes);
     }
-    
+
+    /**
+     * Normally, the Workbook will be in a POIFS Stream
+     * called "Workbook". However, some weird XLS generators use "WORKBOOK"
+     */
+    private static final String[] WORKBOOK_DIR_ENTRY_NAMES = {
+        "Workbook", // as per BIFF8 spec
+        "WORKBOOK",
+    };
+
+
+    private static String getWorkbookDirEntryName(DirectoryNode directory) {
+
+        String[] potentialNames = WORKBOOK_DIR_ENTRY_NAMES;
+        for (int i = 0; i < potentialNames.length; i++) {
+            String wbName = potentialNames[i];
+            try {
+                directory.getEntry(wbName);
+                return wbName;
+            } catch (FileNotFoundException e) {
+                // continue - to try other options
+            }
+        }
+
+        // check for previous version of file format
+        try {
+            directory.getEntry("Book");
+            throw new IllegalArgumentException("The supplied spreadsheet seems to be Excel 5.0/7.0 (BIFF5) format. "
+                    + "POI only supports BIFF8 format (from Excel versions 97/2000/XP/2003)");
+        } catch (FileNotFoundException e) {
+            // fall through
+        }
+
+        throw new IllegalArgumentException("The supplied POIFSFileSystem does not contain a BIFF8 'Workbook' entry. "
+            + "Is it really an excel file?");
+    }
+
     /**
      * given a POI POIFSFileSystem object, and a specific directory
      *  within it, read in its Workbook and populate the high and
@@ -202,9 +238,11 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
     public HSSFWorkbook(DirectoryNode directory, POIFSFileSystem fs, boolean preserveNodes)
             throws IOException
     {
-       super(directory, fs);
+        super(directory, fs);
+        String workbookName = getWorkbookDirEntryName(directory);
+
         this.preserveNodes = preserveNodes;
-        
+
         // If we're not preserving nodes, don't track the
         //  POIFS any more
         if(! preserveNodes) {
@@ -214,28 +252,9 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
 
         sheets = new ArrayList(INITIAL_CAPACITY);
         names  = new ArrayList(INITIAL_CAPACITY);
-        
-        // Normally, the Workbook will be in a POIFS Stream
-        //  called "Workbook". However, some wierd XLS generators
-        //  put theirs in one called "WORKBOOK"
-        String workbookName = "Workbook";
-        try {
-               directory.getEntry(workbookName);
-               // Is the default name
-        } catch(FileNotFoundException fe) {
-               // Try the upper case form
-               try {
-                       workbookName = "WORKBOOK";
-                       directory.getEntry(workbookName);
-               } catch(FileNotFoundException wfe) {
-                       // Doesn't contain it in either form
-                       throw new IllegalArgumentException("The supplied POIFSFileSystem contained neither a 'Workbook' entry, nor a 'WORKBOOK' entry. Is it really an excel file?");
-               }
-        }
 
-        
         // Grab the data from the workbook stream, however
-        //  it happens to be spelt.
+        //  it happens to be spelled.
         InputStream stream = directory.createDocumentInputStream(workbookName);
 
         EventRecordFactory factory = new EventRecordFactory();
@@ -248,7 +267,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
         int sheetNum = 0;
 
         // convert all LabelRecord records to LabelSSTRecord
-        convertLabelRecords(records, recOffset);        
+        convertLabelRecords(records, recOffset);
         while (recOffset < records.size())
         {
             Sheet sheet = Sheet.createSheet(records, sheetNum++, recOffset );
@@ -305,7 +324,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
 
         // none currently
     }
-    
+
     /**
       * This is basically a kludge to deal with the now obsolete Label records.  If
       * you have to read in a sheet that contains Label records, be aware that the rest
@@ -321,7 +340,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
       * @see org.apache.poi.hssf.record.LabelSSTRecord
       * @see org.apache.poi.hssf.record.SSTRecord
       */
+
      private void convertLabelRecords(List records, int offset)
      {
          if (log.check( POILogger.DEBUG ))
@@ -349,7 +368,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
          if (log.check( POILogger.DEBUG ))
              log.log(POILogger.DEBUG, "convertLabelRecords exit");
      }
-    
+
 
     /**
      * sets the order of appearance for a given sheet.
@@ -362,7 +381,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
         sheets.add(pos,sheets.remove(getSheetIndex(sheetname)));
         workbook.setSheetOrder(sheetname, pos);
     }
-    
+
     /**
      * sets the tab whose data is actually seen when the sheet is opened.
      * This may be different from the "selected sheet" since excel seems to
@@ -374,7 +393,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
     public void setSelectedTab(short index) {
         workbook.getWindowOne().setSelectedTab(index);
     }
-    
+
     /**
      * gets the tab whose data is actually seen when the sheet is opened.
      * This may be different from the "selected sheet" since excel seems to
@@ -385,7 +404,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
     public short getSelectedTab() {
         return workbook.getWindowOne().getSelectedTab();
     }
-    
+
     /**
      * sets the first tab that is displayed in the list of tabs
      * in excel.
@@ -394,7 +413,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
     public void setDisplayedTab(short index) {
         workbook.getWindowOne().setDisplayedTab(index);
     }
-    
+
     /**
      * sets the first tab that is displayed in the list of tabs
      * in excel.
@@ -416,7 +435,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
 
 
     /**
-     * set the sheet name. 
+     * set the sheet name.
      * Will throw IllegalArgumentException if the name is greater than 31 chars
      * or contains /\?*[]
      * @param sheet number (0 based)
@@ -430,19 +449,19 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
         {
             throw new RuntimeException("Sheet out of bounds");
         }
-        
+
         workbook.setSheetName( sheet, name);
     }
 
-    
+
     /**
      * set the sheet name forcing the encoding. Forcing the encoding IS A BAD IDEA!!!
      * @deprecated 3-Jan-2006 POI now automatically detects unicode and sets the encoding
-     * appropriately. Simply use setSheetName(int sheet, String encoding) 
+     * appropriately. Simply use setSheetName(int sheet, String encoding)
      * @throws IllegalArgumentException if the name is greater than 31 chars
      * or contains /\?*[]
      * @param sheet number (0 based)
-     */    
+     */
     public void setSheetName( int sheet, String name, short encoding )
     {
         if (workbook.doesContainsSheetName( name, sheet ))
@@ -497,7 +516,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
 
     /**
      * Hide or unhide a sheet
-     * 
+     *
      * @param sheetnum The sheet number
      * @param hidden True to mark the sheet as hidden, false otherwise
      */
@@ -509,7 +528,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
         }
         workbook.setSheetHidden(sheet,hidden);
     }
-    
+
     /*
      * get the sheet's index
      * @param name  sheet name
@@ -533,23 +552,23 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
      */
     public int getSheetIndex(org.apache.poi.ss.usermodel.Sheet sheet)
     {
-       for(int i=0; i<sheets.size(); i++) {
-               if(sheets.get(i) == sheet) {
-                       return i;
-               }
-       }
-       return -1;
+        for(int i=0; i<sheets.size(); i++) {
+            if(sheets.get(i) == sheet) {
+                return i;
+            }
+        }
+        return -1;
     }
-    
+
     /**
      * Returns the external sheet index of the sheet
      *  with the given internal index, creating one
      *  if needed.
-     * Used by some of the more obscure formula and 
+     * Used by some of the more obscure formula and
      *  named range things.
      */
     public int getExternalSheetIndex(int internalSheetIndex) {
-       return workbook.checkExternSheet(internalSheetIndex);
+        return workbook.checkExternSheet(internalSheetIndex);
     }
 
     /**
@@ -593,18 +612,18 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
         sheets.add(clonedSheet);
         int i=1;
         while (true) {
-               //Try and find the next sheet name that is unique
-               String name = srcName;
-               String index = Integer.toString(i++);
-               if (name.length()+index.length()+2<31)
-                 name = name + "("+index+")";
-               else name = name.substring(0, 31-index.length()-2)+"("+index+")";
-               
-               //If the sheet name is unique, then set it otherwise move on to the next number.
-               if (workbook.getSheetIndex(name) == -1) {
+            //Try and find the next sheet name that is unique
+            String name = srcName;
+            String index = Integer.toString(i++);
+            if (name.length()+index.length()+2<31)
+              name = name + "("+index+")";
+            else name = name.substring(0, 31-index.length()-2)+"("+index+")";
+
+            //If the sheet name is unique, then set it otherwise move on to the next number.
+            if (workbook.getSheetIndex(name) == -1) {
               workbook.setSheetName(sheets.size()-1, name);
               break;
-               }
+            }
         }
         return clonedSheet;
       }
@@ -617,6 +636,8 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
      *
      * @param sheetname     sheetname to set for the sheet.
      * @return HSSFSheet representing the new sheet.
+     * @throws IllegalArgumentException if there is already a sheet present with a case-insensitive
+     *  match for the specified name.
      */
 
     public HSSFSheet createSheet(String sheetname)
@@ -660,9 +681,9 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
     }
 
     /**
-     * Get sheet with the given name
+     * Get sheet with the given name (case insensitive match)
      * @param name of the sheet
-     * @return HSSFSheet with the name provided or null if it does not exist
+     * @return HSSFSheet with the name provided or <code>null</code> if it does not exist
      */
 
     public HSSFSheet getSheet(String name)
@@ -673,16 +694,16 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
         {
             String sheetname = workbook.getSheetName(k);
 
-            if (sheetname.equals(name))
+            if (sheetname.equalsIgnoreCase(name))
             {
                 retval = (HSSFSheet) sheets.get(k);
             }
         }
         return retval;
     }
-    
+
     public SheetReferences getSheetReferences() {
-       return workbook.getSheetReferences();
+        return workbook.getSheetReferences();
     }
 
     /**
@@ -860,7 +881,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
                 {
                     return index;
                 }
-            } 
+            }
             index++;
         }
 
@@ -1010,21 +1031,21 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
         // For tracking what we've written out, used if we're
         //  going to be preserving nodes
         List excepts = new ArrayList(1);
-        
+
         // Write out the Workbook stream
         fs.createDocument(new ByteArrayInputStream(bytes), "Workbook");
-        
+
         // Write out our HPFS properties, if we have them
         writeProperties(fs, excepts);
 
         if (preserveNodes) {
-                       // Don't write out the old Workbook, we'll be doing our new one
+            // Don't write out the old Workbook, we'll be doing our new one
             excepts.add("Workbook");
-                       // If the file had WORKBOOK instead of Workbook, we'll write it
-                       //  out correctly shortly, so don't include the old one
+            // If the file had WORKBOOK instead of Workbook, we'll write it
+            //  out correctly shortly, so don't include the old one
             excepts.add("WORKBOOK");
 
-                       // Copy over all the other nodes to our new poifs
+            // Copy over all the other nodes to our new poifs
             copyNodes(this.filesystem,fs,excepts);
         }
         fs.writeFilesystem(stream);
@@ -1138,7 +1159,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
 
         return result;
     }
-    
+
     /**
      * TODO - make this less cryptic / move elsewhere
      * @param refIndex Index to REF entry in EXTERNSHEET record in the Link Table
@@ -1146,76 +1167,76 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
      * @return the string representation of the defined or external name
      */
     public String resolveNameXText(int refIndex, int definedNameIndex) {
-       return workbook.resolveNameXText(refIndex, definedNameIndex);
+        return workbook.resolveNameXText(refIndex, definedNameIndex);
     }
 
 
-       /**
-        * Sets the printarea for the sheet provided
-        * <p>
-        * i.e. Reference = $A$1:$B$2
-        * @param sheetIndex Zero-based sheet index (0 Represents the first sheet to keep consistent with java)
-        * @param reference Valid name Reference for the Print Area
-        */
-       public void setPrintArea(int sheetIndex, String reference)
-       {
-               NameRecord name = workbook.getSpecificBuiltinRecord(NameRecord.BUILTIN_PRINT_AREA, sheetIndex+1);
-
-
-               if (name == null)
-                       name = workbook.createBuiltInName(NameRecord.BUILTIN_PRINT_AREA, sheetIndex+1);
+    /**
+     * Sets the printarea for the sheet provided
+     * <p>
+     * i.e. Reference = $A$1:$B$2
+     * @param sheetIndex Zero-based sheet index (0 Represents the first sheet to keep consistent with java)
+     * @param reference Valid name Reference for the Print Area
+     */
+    public void setPrintArea(int sheetIndex, String reference)
+    {
+        NameRecord name = workbook.getSpecificBuiltinRecord(NameRecord.BUILTIN_PRINT_AREA, sheetIndex+1);
+
+
+        if (name == null)
+            name = workbook.createBuiltInName(NameRecord.BUILTIN_PRINT_AREA, sheetIndex+1);
        //adding one here because 0 indicates a global named region; doesnt make sense for print areas
 
-           short externSheetIndex = getWorkbook().checkExternSheet(sheetIndex);
-               name.setExternSheetNumber(externSheetIndex);
-               name.setAreaReference(reference);
+        short externSheetIndex = getWorkbook().checkExternSheet(sheetIndex);
+        name.setExternSheetNumber(externSheetIndex);
+        name.setAreaReference(reference);
 
 
-       }
+    }
 
-       /**
-        * For the Convenience of Java Programmers maintaining pointers.
-        * @see #setPrintArea(int, String)
-        * @param sheetIndex Zero-based sheet index (0 = First Sheet)
-        * @param startColumn Column to begin printarea
-        * @param endColumn Column to end the printarea
-        * @param startRow Row to begin the printarea
-        * @param endRow Row to end the printarea
-        */
-       public void setPrintArea(int sheetIndex, int startColumn, int endColumn,
-                                                         int startRow, int endRow) {
-
-               //using absolute references because they don't get copied and pasted anyway
-               CellReference cell = new CellReference(startRow, startColumn, true, true);
-               String reference = cell.formatAsString();
-
-               cell = new CellReference(endRow, endColumn, true, true);
-               reference = reference+":"+cell.formatAsString();
-
-               setPrintArea(sheetIndex, reference);
-       }
+    /**
+     * For the Convenience of Java Programmers maintaining pointers.
+     * @see #setPrintArea(int, String)
+     * @param sheetIndex Zero-based sheet index (0 = First Sheet)
+     * @param startColumn Column to begin printarea
+     * @param endColumn Column to end the printarea
+     * @param startRow Row to begin the printarea
+     * @param endRow Row to end the printarea
+     */
+    public void setPrintArea(int sheetIndex, int startColumn, int endColumn,
+                              int startRow, int endRow) {
 
+        //using absolute references because they don't get copied and pasted anyway
+        CellReference cell = new CellReference(startRow, startColumn, true, true);
+        String reference = cell.formatAsString();
 
-       /**
-        * Retrieves the reference for the printarea of the specified sheet, the sheet name is appended to the reference even if it was not specified.
-        * @param sheetIndex Zero-based sheet index (0 Represents the first sheet to keep consistent with java)
-        * @return String Null if no print area has been defined
-        */
-       public String getPrintArea(int sheetIndex)
-       {
-               NameRecord name = workbook.getSpecificBuiltinRecord(NameRecord.BUILTIN_PRINT_AREA, sheetIndex+1);
-               if (name == null) return null;
-               //adding one here because 0 indicates a global named region; doesnt make sense for print areas
-
-               return name.getAreaReference(this);
-       }
+        cell = new CellReference(endRow, endColumn, true, true);
+        reference = reference+":"+cell.formatAsString();
+
+        setPrintArea(sheetIndex, reference);
+    }
+
+
+    /**
+     * Retrieves the reference for the printarea of the specified sheet, the sheet name is appended to the reference even if it was not specified.
+     * @param sheetIndex Zero-based sheet index (0 Represents the first sheet to keep consistent with java)
+     * @return String Null if no print area has been defined
+     */
+    public String getPrintArea(int sheetIndex)
+    {
+        NameRecord name = workbook.getSpecificBuiltinRecord(NameRecord.BUILTIN_PRINT_AREA, sheetIndex+1);
+        if (name == null) return null;
+        //adding one here because 0 indicates a global named region; doesnt make sense for print areas
+
+        return name.getAreaReference(this);
+    }
 
     /**
      * Delete the printarea for the sheet specified
      * @param sheetIndex Zero-based sheet index (0 = First Sheet)
      */
     public void removePrintArea(int sheetIndex) {
-       getWorkbook().removeBuiltinRecord(NameRecord.BUILTIN_PRINT_AREA, sheetIndex+1);
+        getWorkbook().removeBuiltinRecord(NameRecord.BUILTIN_PRINT_AREA, sheetIndex+1);
     }
 
     /** creates a new named range and add it to the model
@@ -1234,7 +1255,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
     /** gets the named range index by his name
      * <i>Note:</i>Excel named ranges are case-insensitive and
      * this method performs a case-insensitive search.
-     * 
+     *
      * @param name named range name
      * @return named range index
      */
@@ -1271,9 +1292,9 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
      * @see org.apache.poi.hssf.record.Record
      */
     public HSSFDataFormat createDataFormat() {
-       if (formatter == null)
-           formatter = new HSSFDataFormat(workbook);
-       return formatter;
+    if (formatter == null)
+        formatter = new HSSFDataFormat(workbook);
+    return formatter;
     }
 
     /** remove the named range by his name
@@ -1452,9 +1473,9 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
      * Is the workbook protected with a password (not encrypted)?
      */
     public boolean isWriteProtected() {
-       return this.workbook.isWriteProtected();
+        return this.workbook.isWriteProtected();
     }
-    
+
     /**
      * protect a workbook with a password (not encypted, just sets writeprotect
      * flags and the password.
index 2d45b60eb68a096801048360ccf73f646c61f0b3..f69bb726cf638af548fad9e2c620a740a064a328 100644 (file)
@@ -18,7 +18,7 @@
 package org.apache.poi.ss.usermodel;
 
 import org.apache.poi.hssf.usermodel.*;
-import org.apache.poi.ss.util.SheetReferences;
+import org.apache.poi.hssf.util.SheetReferences;
 
 /**
  * This is a JDK 1.4 compatible interface for HSSFWorkbook.
@@ -38,12 +38,10 @@ public interface Workbook {
     String getSheetName(int sheet);
     HSSFSheet getSheetAt(int index);
     SheetReferences getSheetReferences();
-    short getSheetIndex(String name);
-    short getSheetIndex(Sheet sheet);
-    short getSheetIndexFromExternSheetIndex(int externSheetNumber);
-    short getSheetIndexFromExternSheetIndex(short externSheetNumber);
-       short getExternalSheetIndex(int internalSheetIndex);
-    short getExternalSheetIndex(short internalSheetIndex);
+    int getSheetIndex(String name);
+    int getSheetIndex(Sheet sheet);
+    int getSheetIndexFromExternSheetIndex(int externSheetNumber);
+       int getExternalSheetIndex(int internalSheetIndex);
 
     CreationHelper getCreationHelper();
 }
index 5d345e6de4b0d51689c60819dbe0a4d1a9cec64e..1fc4ec9932c28f3787b2a3890447dbca2933cd36 100755 (executable)
@@ -369,5 +369,12 @@ public class AutoShapes {
             }\r
         };\r
 \r
+        shapes[ShapeTypes.StraightConnector1] = new ShapeOutline(){\r
+            public java.awt.Shape getOutline(Shape shape){\r
+                return new Line2D.Float(0, 0, 21600, 21600);\r
+            }\r
+        };\r
+\r
+\r
     }\r
 }\r
index d31237f8db1378ce96a436ae8d25ba7d730b1c77..fb3980a455b34bbf9ad56287e27b723414431820 100755 (executable)
@@ -19,6 +19,7 @@ package org.apache.poi.hslf.model;
 import org.apache.poi.ddf.*;\r
 import org.apache.poi.util.LittleEndian;\r
 import org.apache.poi.util.POILogger;\r
+import org.apache.poi.util.HexDump;\r
 \r
 import java.awt.geom.*;\r
 import java.util.ArrayList;\r
@@ -185,10 +186,6 @@ public class Freeform extends AutoShape {
             return null;\r
         }\r
 \r
-        Rectangle2D bounds = getAnchor2D();\r
-        float right = (float)bounds.getX();\r
-        float bottom = (float)bounds.getY();\r
-\r
         GeneralPath path = new GeneralPath();\r
         int numPoints = verticesProp.getNumberOfElementsInArray();\r
         int numSegments = segmentsProp.getNumberOfElementsInArray();\r
@@ -199,8 +196,8 @@ public class Freeform extends AutoShape {
                 short x = LittleEndian.getShort(p, 0);\r
                 short y = LittleEndian.getShort(p, 2);\r
                 path.moveTo(\r
-                        ((float)x*POINT_DPI/MASTER_DPI + right),\r
-                        ((float)y*POINT_DPI/MASTER_DPI + bottom));\r
+                        ((float)x*POINT_DPI/MASTER_DPI),\r
+                        ((float)y*POINT_DPI/MASTER_DPI));\r
             } else if (Arrays.equals(elem, SEGMENTINFO_CUBICTO) || Arrays.equals(elem, SEGMENTINFO_CUBICTO2)){\r
                 i++;\r
                 byte[] p1 = verticesProp.getElement(j++);\r
@@ -213,9 +210,9 @@ public class Freeform extends AutoShape {
                 short x3 = LittleEndian.getShort(p3, 0);\r
                 short y3 = LittleEndian.getShort(p3, 2);\r
                 path.curveTo(\r
-                        ((float)x1*POINT_DPI/MASTER_DPI + right), ((float)y1*POINT_DPI/MASTER_DPI + bottom),\r
-                        ((float)x2*POINT_DPI/MASTER_DPI + right), ((float)y2*POINT_DPI/MASTER_DPI + bottom),\r
-                        ((float)x3*POINT_DPI/MASTER_DPI + right), ((float)y3*POINT_DPI/MASTER_DPI + bottom));\r
+                        ((float)x1*POINT_DPI/MASTER_DPI), ((float)y1*POINT_DPI/MASTER_DPI),\r
+                        ((float)x2*POINT_DPI/MASTER_DPI), ((float)y2*POINT_DPI/MASTER_DPI),\r
+                        ((float)x3*POINT_DPI/MASTER_DPI), ((float)y3*POINT_DPI/MASTER_DPI));\r
 \r
             } else if (Arrays.equals(elem, SEGMENTINFO_LINETO)){\r
                 i++;\r
@@ -226,18 +223,26 @@ public class Freeform extends AutoShape {
                         short x = LittleEndian.getShort(p, 0);\r
                         short y = LittleEndian.getShort(p, 2);\r
                         path.lineTo(\r
-                                ((float)x*POINT_DPI/MASTER_DPI + right), ((float)y*POINT_DPI/MASTER_DPI + bottom));\r
+                                ((float)x*POINT_DPI/MASTER_DPI), ((float)y*POINT_DPI/MASTER_DPI));\r
                     }\r
                 } else if (Arrays.equals(pnext, SEGMENTINFO_CLOSE)){\r
                     path.closePath();\r
                 }\r
             }\r
         }\r
-\r
         return path;\r
     }\r
 \r
     public java.awt.Shape getOutline(){\r
-        return getPath();\r
+        GeneralPath path =  getPath();\r
+        Rectangle2D anchor = getAnchor2D();\r
+        Rectangle2D bounds = path.getBounds2D();\r
+        AffineTransform at = new AffineTransform();\r
+        at.translate(anchor.getX(), anchor.getY());\r
+        at.scale(\r
+                anchor.getWidth()/bounds.getWidth(),\r
+                anchor.getHeight()/bounds.getHeight()\r
+        );\r
+        return at.createTransformedShape(path);\r
     }\r
 }\r
index 5b1b1016e4f3487875d03a2a6ac8665003cd0db2..d01136d87ee90ee876453ea6239ed168b035a7ab 100644 (file)
@@ -67,4 +67,21 @@ public abstract class MasterSheet extends Sheet {
         }
         return false;
     }
+
+    /**
+     * Return placeholder by text type
+     */
+    public TextShape getPlaceholder(int type){
+        Shape[] shape = getShapes();
+        for (int i = 0; i < shape.length; i++) {
+            if(shape[i] instanceof TextShape){
+                TextShape tx = (TextShape)shape[i];
+                TextRun run = tx.getTextRun();
+                if(run != null && run.getRunType() == type){
+                    return tx;
+                }
+            }
+        }
+        return null;
+    }
 }
index e10986966bf85532c169a7aa8a87da1350175ec2..375249b519c79b15f82068a5eba0901fc1193d8d 100644 (file)
@@ -28,6 +28,7 @@ import javax.imageio.ImageIO;
 import java.awt.image.BufferedImage;
 import java.awt.*;
 import java.awt.geom.Rectangle2D;
+import java.awt.geom.AffineTransform;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
@@ -244,6 +245,9 @@ public class Picture extends SimpleShape {
     }
 
     public void draw(Graphics2D graphics){
+        AffineTransform at = graphics.getTransform();
+        ShapePainter.paint(this, graphics);
+
         PictureData data = getPictureData();
         if (data  instanceof Bitmap){
             BufferedImage img = null;
@@ -260,5 +264,6 @@ public class Picture extends SimpleShape {
         } else {
             logger.log(POILogger.WARN, "Rendering of metafiles is not yet supported. image.type: " + (data == null ? "NA" : data.getClass().getName()));
         }
+        graphics.setTransform(at);
     }
 }
index e96e349002434786e473a7e9b3f79cfdc5e6d24d..fbea17e88f424d4c319bbf4a3102945e8031f4fd 100644 (file)
@@ -341,58 +341,7 @@ public abstract class Shape {
      * @param sh - owning shape
      */
     protected void afterInsert(Sheet sh){
-        PPDrawing ppdrawing = sh.getPPDrawing();
 
-        EscherContainerRecord dgContainer = (EscherContainerRecord) ppdrawing.getEscherRecords()[0];
-
-        EscherDgRecord dg = (EscherDgRecord) Shape.getEscherChild(dgContainer, EscherDgRecord.RECORD_ID);
-
-        int id = allocateShapeId(dg);
-        setShapeId(id);
-    }
-
-    /**
-     * Allocates new shape id for the new drawing group id.
-     *
-     * @param dg  EscherDgRecord of the sheet that owns the shape being created
-     *
-     * @return a new shape id.
-     */
-    protected int allocateShapeId(EscherDgRecord dg)
-    {
-        EscherDggRecord dgg = _sheet.getSlideShow().getDocumentRecord().getPPDrawingGroup().getEscherDggRecord();
-        if(dgg == null){
-            logger.log(POILogger.ERROR, "EscherDggRecord not found");
-            return 0;
-        }
-
-        dgg.setNumShapesSaved( dgg.getNumShapesSaved() + 1 );
-
-        // Add to existing cluster if space available
-        for (int i = 0; i < dgg.getFileIdClusters().length; i++)
-        {
-            EscherDggRecord.FileIdCluster c = dgg.getFileIdClusters()[i];
-            if (c.getDrawingGroupId() == dg.getDrawingGroupId() && c.getNumShapeIdsUsed() != 1024)
-            {
-                int result = c.getNumShapeIdsUsed() + (1024 * (i+1));
-                c.incrementShapeId();
-                dg.setNumShapes( dg.getNumShapes() + 1 );
-                dg.setLastMSOSPID( result );
-                if (result >= dgg.getShapeIdMax())
-                    dgg.setShapeIdMax( result + 1 );
-                return result;
-            }
-        }
-
-        // Create new cluster
-        dgg.addCluster( dg.getDrawingGroupId(), 0 );
-        dgg.getFileIdClusters()[dgg.getFileIdClusters().length-1].incrementShapeId();
-        dg.setNumShapes( dg.getNumShapes() + 1 );
-        int result = (1024 * dgg.getFileIdClusters().length);
-        dg.setLastMSOSPID( result );
-        if (result >= dgg.getShapeIdMax())
-            dgg.setShapeIdMax( result + 1 );
-        return result;
     }
 
     /**
index b57a6e2336d6ba75527d622386877040aa238007..cfa8296713d6c44a1057ebffd904b19e98d8f3fc 100644 (file)
@@ -196,13 +196,8 @@ public class ShapeGroup extends Shape{
 
         Sheet sheet = getSheet();
         shape.setSheet(sheet);
+        shape.setShapeId(sheet.allocateShapeId());
         shape.afterInsert(sheet);
-
-        if (shape instanceof TextShape) {
-            TextShape tbox = (TextShape) shape;
-            EscherTextboxWrapper txWrapper = tbox.getEscherTextboxWrapper();
-            if(txWrapper != null) getSheet().getPPDrawing().addTextboxWrapper(txWrapper);
-        }
     }
 
     /**
@@ -277,20 +272,9 @@ public class ShapeGroup extends Shape{
     }
 
     public void draw(Graphics2D graphics){
-        Rectangle2D anchor = getAnchor2D();
-        Rectangle2D coords = getCoordinates();
 
-        //transform coordinates
         AffineTransform at = graphics.getTransform();
-        /*
-        if(!anchor.equals(coords)){
-            graphics.scale(anchor.getWidth()/coords.getWidth(), anchor.getHeight()/coords.getHeight());
 
-            graphics.translate(
-                    anchor.getX()*coords.getWidth()/anchor.getWidth() - coords.getX(),
-                    anchor.getY()*coords.getHeight()/anchor.getHeight() - coords.getY());
-        }
-        */
         Shape[] sh = getShapes();
         for (int i = 0; i < sh.length; i++) {
             sh[i].draw(graphics);
index d9c8903d5bb3038ea927defe49aa78bb5218f78d..6eb84ca2e75f93a0984ee739550e2c4011230c6a 100644 (file)
 
 package org.apache.poi.hslf.model;
 
-import org.apache.poi.ddf.EscherContainerRecord;
-import org.apache.poi.ddf.EscherDgRecord;
-import org.apache.poi.ddf.EscherRecord;
-import org.apache.poi.ddf.EscherSpRecord;
+import org.apache.poi.ddf.*;
 import org.apache.poi.hslf.record.*;
 import org.apache.poi.hslf.usermodel.SlideShow;
+import org.apache.poi.util.POILogger;
 
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -248,15 +246,47 @@ public abstract class Sheet {
         spgr.addChildRecord(shape.getSpContainer());
 
         shape.setSheet(this);
+        shape.setShapeId(allocateShapeId());
         shape.afterInsert(this);
+    }
 
-        // If it's a TextShape, we need to tell the PPDrawing, as it has to
-        //  track TextboxWrappers specially
-        if (shape instanceof TextShape) {
-            TextShape tbox = (TextShape) shape;
-            EscherTextboxWrapper txWrapper = tbox.getEscherTextboxWrapper();
-            if(txWrapper != null) ppdrawing.addTextboxWrapper(txWrapper);
+    /**
+     * Allocates new shape id for the new drawing group id.
+     *
+     * @return a new shape id.
+     */
+    public int allocateShapeId()
+    {
+        EscherDggRecord dgg = _slideShow.getDocumentRecord().getPPDrawingGroup().getEscherDggRecord();
+        EscherDgRecord dg = _container.getPPDrawing().getEscherDgRecord();
+
+        dgg.setNumShapesSaved( dgg.getNumShapesSaved() + 1 );
+
+        // Add to existing cluster if space available
+        for (int i = 0; i < dgg.getFileIdClusters().length; i++)
+        {
+            EscherDggRecord.FileIdCluster c = dgg.getFileIdClusters()[i];
+            if (c.getDrawingGroupId() == dg.getDrawingGroupId() && c.getNumShapeIdsUsed() != 1024)
+            {
+                int result = c.getNumShapeIdsUsed() + (1024 * (i+1));
+                c.incrementShapeId();
+                dg.setNumShapes( dg.getNumShapes() + 1 );
+                dg.setLastMSOSPID( result );
+                if (result >= dgg.getShapeIdMax())
+                    dgg.setShapeIdMax( result + 1 );
+                return result;
+            }
         }
+
+        // Create new cluster
+        dgg.addCluster( dg.getDrawingGroupId(), 0, false );
+        dgg.getFileIdClusters()[dgg.getFileIdClusters().length-1].incrementShapeId();
+        dg.setNumShapes( dg.getNumShapes() + 1 );
+        int result = (1024 * dgg.getFileIdClusters().length);
+        dg.setLastMSOSPID( result );
+        if (result >= dgg.getShapeIdMax())
+            dgg.setShapeIdMax( result + 1 );
+        return result;
     }
 
     /**
@@ -284,6 +314,13 @@ public abstract class Sheet {
         return lst.remove(shape.getSpContainer());
     }
 
+    /**
+     * Called by SlideShow ater a new sheet is created
+     */
+    public void onCreate(){
+
+    }
+
     /**
      * Return the master sheet .
      */
index ea0719a7f15a7e5635c0064584a058b16c4fad39..e15454d651c90148bc077f88136efde5da8f66c8 100644 (file)
@@ -126,8 +126,8 @@ public class SimpleShape extends Shape {
         EscherSimpleProperty p2 = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.LINESTYLE__NOLINEDRAWDASH);
         int p2val = p2 == null ? 0 : p2.getPropertyValue();
         Color clr = null;
-        if (p1 != null && (p2val  & 0x8) != 0){
-            int rgb = p1.getPropertyValue();
+        if ((p2val  & 0x8) != 0 || (p2val  & 0x10) != 0){
+            int rgb = p1 == null ? 0 : p1.getPropertyValue();
             if (rgb >= 0x8000000) {
                 int idx = rgb % 0x8000000;
                 if(getSheet() != null) {
index e618ae23033372b2869a996a0a9a84906a984396..bb99c1bcadecabfdbbe612949742275507a5a7c8 100644 (file)
 package org.apache.poi.hslf.model;
 
 import java.util.Vector;
+import java.util.Iterator;
 import java.awt.*;
 
 import org.apache.poi.hslf.record.SlideAtom;
 import org.apache.poi.hslf.record.TextHeaderAtom;
 import org.apache.poi.hslf.record.ColorSchemeAtom;
 import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet;
+import org.apache.poi.ddf.EscherDggRecord;
+import org.apache.poi.ddf.EscherContainerRecord;
+import org.apache.poi.ddf.EscherDgRecord;
+import org.apache.poi.ddf.EscherSpRecord;
 
 /**
  * This class represents a slide in a PowerPoint Document. It allows 
@@ -126,6 +131,42 @@ public class Slide extends Sheet
                _slideNo = newSlideNumber;
        }
   
+    /**
+     * Called by SlideShow ater a new slide is created.
+     * <p>
+     * For Slide we need to do the following:
+     *  <li> set id of the drawing group.
+     *  <li> set shapeId for the container descriptor and background
+     * </p>
+     */
+    public void onCreate(){
+        //initialize drawing group id
+        EscherDggRecord dgg = getSlideShow().getDocumentRecord().getPPDrawingGroup().getEscherDggRecord();
+        EscherContainerRecord dgContainer = (EscherContainerRecord)getSheetContainer().getPPDrawing().getEscherRecords()[0];
+        EscherDgRecord dg = (EscherDgRecord) Shape.getEscherChild(dgContainer, EscherDgRecord.RECORD_ID);
+        int dgId = dgg.getMaxDrawingGroupId() + 1;
+        dg.setOptions((short)(dgId << 4));
+        dgg.setDrawingsSaved(dgg.getDrawingsSaved() + 1);
+
+        for (Iterator it = dgContainer.getChildContainers().iterator(); it.hasNext(); ) {
+            EscherContainerRecord c = (EscherContainerRecord)it.next();
+            EscherSpRecord spr = null;
+            switch(c.getRecordId()){
+                case EscherContainerRecord.SPGR_CONTAINER:
+                    EscherContainerRecord dc = (EscherContainerRecord)c.getChildRecords().get(0);
+                    spr = dc.getChildById(EscherSpRecord.RECORD_ID);
+                    break;
+                case EscherContainerRecord.SP_CONTAINER:
+                    spr = c.getChildById(EscherSpRecord.RECORD_ID);
+                    break;
+            }
+            if(spr != null) spr.setShapeId(allocateShapeId());
+        }
+
+        //PPT doen't increment the number of saved shapes for group descriptor and background 
+        dg.setNumShapes(1);
+    }
+
        /**
         * Create a <code>TextBox</code> object that represents the slide's title.
         *
index 26870dbdbbbdeaf9a81cf1ed7bb7152a640f9f83..b48edfc1a1b40bc08a4e575e00fca5bea4004d4d 100644 (file)
@@ -92,6 +92,7 @@ public class SlideMaster extends MasterSheet {
             } else {
                 switch (txtype) {
                     case TextHeaderAtom.CENTRE_BODY_TYPE:
+                    case TextHeaderAtom.HALF_BODY_TYPE:
                     case TextHeaderAtom.QUARTER_BODY_TYPE:
                         txtype = TextHeaderAtom.BODY_TYPE;
                         break;
index c1eadc336c6494af6998d28978484ac9037c4154..6eff54329078d622c775cca914a1131eafe1c873 100755 (executable)
@@ -17,6 +17,7 @@
 package org.apache.poi.hslf.model;\r
 \r
 import org.apache.poi.hslf.usermodel.RichTextRun;\r
+import org.apache.poi.hslf.record.TextRulerAtom;\r
 import org.apache.poi.util.POILogger;\r
 import org.apache.poi.util.POILogFactory;\r
 \r
@@ -38,6 +39,13 @@ import java.util.ArrayList;
 public class TextPainter {\r
     protected POILogger logger = POILogFactory.getLogger(this.getClass());\r
 \r
+    /**\r
+     * Display unicode square if a bullet char can't be displayed,\r
+     * for example, if Wingdings font is used.\r
+     * TODO: map Wingdngs and Symbol to unicode Arial\r
+     */\r
+    protected static final char DEFAULT_BULLET_CHAR = '\u25a0';\r
+\r
     protected TextShape _shape;\r
 \r
     public TextPainter(TextShape shape){\r
@@ -49,6 +57,10 @@ public class TextPainter {
      */\r
     public AttributedString getAttributedString(TextRun txrun){\r
         String text = txrun.getText();\r
+        //TODO: properly process tabs\r
+        text = text.replace('\t', ' ');\r
+        text = text.replace((char)160, ' ');\r
+\r
         AttributedString at = new AttributedString(text);\r
         RichTextRun[] rt = txrun.getRichTextRuns();\r
         for (int i = 0; i < rt.length; i++) {\r
@@ -109,7 +121,24 @@ public class TextPainter {
             }\r
 \r
             float wrappingWidth = (float)anchor.getWidth() - _shape.getMarginLeft() - _shape.getMarginRight();\r
-            wrappingWidth -= rt.getTextOffset();\r
+            int bulletOffset = rt.getBulletOffset();\r
+            int textOffset = rt.getTextOffset();\r
+            int indent = rt.getIndentLevel();\r
+\r
+            TextRulerAtom ruler = run.getTextRuler();\r
+            if(ruler != null) {\r
+                int bullet_val = ruler.getBulletOffsets()[indent]*Shape.POINT_DPI/Shape.MASTER_DPI;\r
+                int text_val = ruler.getTextOffsets()[indent]*Shape.POINT_DPI/Shape.MASTER_DPI;\r
+                if(bullet_val > text_val){\r
+                    int a = bullet_val;\r
+                    bullet_val = text_val;\r
+                    text_val = a;\r
+                }\r
+                if(bullet_val != 0 ) bulletOffset = bullet_val;\r
+                if(text_val != 0) textOffset = text_val;\r
+            }\r
+\r
+            wrappingWidth -= textOffset;\r
 \r
             if (_shape.getWordWrap() == TextShape.WrapNone) {\r
                 wrappingWidth = _shape.getSheet().getSlideShow().getPageSize().width;\r
@@ -141,8 +170,9 @@ public class TextPainter {
             }\r
 \r
             el._align = rt.getAlignment();\r
-            el._text = textLayout;\r
-            el._textOffset = rt.getTextOffset();\r
+            el.advance = textLayout.getAdvance();\r
+            el._textOffset = textOffset;\r
+            el._text = new AttributedString(it, startIndex, endIndex);\r
 \r
             if (prStart){\r
                 int sp = rt.getSpaceBefore();\r
@@ -182,13 +212,25 @@ public class TextPainter {
                 Color clr = rt.getBulletColor();\r
                 if (clr != null) bat.addAttribute(TextAttribute.FOREGROUND, clr);\r
                 else bat.addAttribute(TextAttribute.FOREGROUND, it.getAttribute(TextAttribute.FOREGROUND));\r
-                bat.addAttribute(TextAttribute.FAMILY, it.getAttribute(TextAttribute.FAMILY));\r
-                bat.addAttribute(TextAttribute.SIZE, it.getAttribute(TextAttribute.SIZE));\r
 \r
-                TextLayout bulletLayout = new TextLayout(bat.getIterator(), graphics.getFontRenderContext());\r
+                int fontIdx = rt.getBulletFont();\r
+                if(fontIdx == -1) fontIdx = rt.getFontIndex();\r
+                PPFont bulletFont = _shape.getSheet().getSlideShow().getFont(fontIdx);\r
+                bat.addAttribute(TextAttribute.FAMILY, bulletFont.getFontName());\r
+\r
+                int bulletSize = rt.getBulletSize();\r
+                int fontSize = rt.getFontSize();\r
+                if(bulletSize != -1) fontSize = Math.round(fontSize*bulletSize*0.01f);\r
+                bat.addAttribute(TextAttribute.SIZE, new Float(fontSize));\r
+\r
+                if(!new Font(bulletFont.getFontName(), Font.PLAIN, 1).canDisplay(rt.getBulletChar())){\r
+                    bat.addAttribute(TextAttribute.FAMILY, "Arial");\r
+                    bat = new AttributedString("" + DEFAULT_BULLET_CHAR, bat.getIterator().getAttributes());\r
+                }\r
+\r
                 if(text.substring(startIndex, endIndex).length() > 1){\r
-                    el._bullet = bulletLayout;\r
-                    el._bulletOffset = rt.getBulletOffset();\r
+                    el._bullet = bat;\r
+                    el._bulletOffset = bulletOffset;\r
                 }\r
             }\r
             lines.add(el);\r
@@ -225,29 +267,32 @@ public class TextPainter {
                     break;\r
                 case TextShape.AlignCenter:\r
                     pen.x = anchor.getX() + _shape.getMarginLeft() +\r
-                            (anchor.getWidth() - elem._text.getAdvance() - _shape.getMarginLeft() - _shape.getMarginRight()) / 2;\r
+                            (anchor.getWidth() - elem.advance - _shape.getMarginLeft() - _shape.getMarginRight()) / 2;\r
                     break;\r
                 case TextShape.AlignRight:\r
                     pen.x = anchor.getX() + _shape.getMarginLeft() +\r
-                            (anchor.getWidth() - elem._text.getAdvance() - _shape.getMarginLeft() - _shape.getMarginRight());\r
+                            (anchor.getWidth() - elem.advance - _shape.getMarginLeft() - _shape.getMarginRight());\r
                     break;\r
             }\r
             if(elem._bullet != null){\r
-                elem._bullet.draw(graphics, (float)(pen.x + elem._bulletOffset), (float)pen.y);\r
+                graphics.drawString(elem._bullet.getIterator(), (float)(pen.x + elem._bulletOffset), (float)pen.y);\r
+            }\r
+            AttributedCharacterIterator chIt = elem._text.getIterator();\r
+            if(chIt.getEndIndex() > chIt.getBeginIndex()) {\r
+                graphics.drawString(chIt, (float)(pen.x + elem._textOffset), (float)pen.y);\r
             }\r
-            elem._text.draw(graphics, (float)(pen.x + elem._textOffset), (float)pen.y);\r
-\r
             y0 += elem.descent;\r
         }\r
     }\r
 \r
 \r
-    static class TextElement {\r
-        public TextLayout _text;\r
+    public static class TextElement {\r
+        public AttributedString _text;\r
         public int _textOffset;\r
-        public TextLayout _bullet;\r
+        public AttributedString _bullet;\r
         public int _bulletOffset;\r
         public int _align;\r
         public float ascent, descent;\r
+        public float advance;\r
     }\r
 }\r
index 2f77ac5ffa2c044d8796be07c330aedf10563127..5dac650258bad171f3b50df9978940af4c6506e7 100644 (file)
@@ -535,9 +535,13 @@ public class TextRun
                //  them to \n
                String text = rawText.replace('\r','\n');
 
-        //0xB acts like cariage return in page titles
-        text = text.replace((char) 0x0B, '\n');
-
+        int type = _headerAtom == null ? 0 : _headerAtom.getTextType();
+        if(type == TextHeaderAtom.TITLE_TYPE || type == TextHeaderAtom.CENTER_TITLE_TYPE){
+            //0xB acts like cariage return in page titles and like blank in the others
+            text = text.replace((char) 0x0B, '\n');
+        } else {
+            text = text.replace((char) 0x0B, ' ');
+        }
                return text;
        }
 
@@ -655,4 +659,11 @@ public class TextRun
         return null;
     }
 
+    public TextRulerAtom getTextRuler(){
+        for (int i = 0; i < _records.length; i++) {
+            if(_records[i] instanceof TextRulerAtom) return (TextRulerAtom)_records[i];
+        }
+        return null;
+
+    }
 }
index 7249817bed3b342d5bca22f6e58649c9323189d4..53f8ef97d57cf91c6f359946d6ea1f6ca3c76b7e 100755 (executable)
@@ -261,17 +261,28 @@ public abstract class TextShape extends SimpleShape {
     public int getVerticalAlignment(){\r
         EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);\r
         EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__ANCHORTEXT);\r
-        int valign;\r
+        int valign = TextShape.AnchorTop;\r
         if (prop == null){\r
+            /**\r
+             * If vertical alignment was not found in the shape properties then try to\r
+             * fetch the master shape and search for the align property there.\r
+             */\r
             int type = getTextRun().getRunType();\r
-            switch (type){\r
-                case TextHeaderAtom.TITLE_TYPE:\r
-                case TextHeaderAtom.CENTER_TITLE_TYPE:\r
-                    valign = TextShape.AnchorMiddle;\r
-                    break;\r
-                default:\r
-                    valign = TextShape.AnchorTop;\r
-                    break;\r
+            MasterSheet master = getSheet().getMasterSheet();\r
+            if(master != null){\r
+                TextShape masterShape = master.getPlaceholder(type);\r
+                if(masterShape != null) valign = masterShape.getVerticalAlignment();\r
+            } else {\r
+                //not found in the master sheet. Use the hardcoded defaults.\r
+                switch (type){\r
+                     case TextHeaderAtom.TITLE_TYPE:\r
+                     case TextHeaderAtom.CENTER_TITLE_TYPE:\r
+                         valign = TextShape.AnchorMiddle;\r
+                         break;\r
+                     default:\r
+                         valign = TextShape.AnchorTop;\r
+                         break;\r
+                 }\r
             }\r
         } else {\r
             valign = prop.getPropertyValue();\r
index e42b358b89df78cf2230de8ac01e160473faa166..3a1ed8dbe5c865424407dccfd356f03864b82339 100644 (file)
@@ -24,11 +24,13 @@ import org.apache.poi.util.POILogger;
 
 import org.apache.poi.ddf.*;
 import org.apache.poi.hslf.model.ShapeTypes;
+import org.apache.poi.hslf.model.Shape;
 
 import java.io.IOException;
 import java.io.OutputStream;
 import java.util.List;
 import java.util.Vector;
+import java.util.Iterator;
 
 /**
  * These are actually wrappers onto Escher drawings. Make use of
@@ -52,6 +54,8 @@ public class PPDrawing extends RecordAtom
        private EscherRecord[] childRecords;
        private EscherTextboxWrapper[] textboxWrappers;
 
+    //cached EscherDgRecord
+    private EscherDgRecord dg;
 
        /**
         * Get access to the underlying Escher Records
@@ -296,4 +300,24 @@ public class PPDrawing extends RecordAtom
                tw[textboxWrappers.length] = txtbox;
                textboxWrappers = tw;
        }
+
+    /**
+     * Return EscherDgRecord which keeps track of the number of shapes and shapeId in this drawing group
+     *
+     * @return EscherDgRecord
+     */
+    public EscherDgRecord getEscherDgRecord(){
+        if(dg == null){
+            EscherContainerRecord dgContainer = (EscherContainerRecord)childRecords[0];
+            for(Iterator it = dgContainer.getChildRecords().iterator(); it.hasNext();){
+                EscherRecord r = (EscherRecord) it.next();
+                if(r instanceof EscherDgRecord){
+                    dg = (EscherDgRecord)r;
+                    break;
+                }
+            }
+        }
+        return dg;
+    }
+
 }
index 45038f7a287f1a0c557af79ca420ad47fc0f6803..cedc99ce0a7da6e896bec0f5ce43ed7516c37d7f 100644 (file)
@@ -89,7 +89,7 @@ public class RecordTypes {
     public static final Type TxMasterStyleAtom = new Type(4003,TxMasterStyleAtom.class);
     public static final Type TxCFStyleAtom = new Type(4004,null);
     public static final Type TxPFStyleAtom = new Type(4005,null);
-    public static final Type TextRulerAtom = new Type(4006,null);
+    public static final Type TextRulerAtom = new Type(4006,TextRulerAtom.class);
     public static final Type TextBookmarkAtom = new Type(4007,null);
     public static final Type TextBytesAtom = new Type(4008,TextBytesAtom.class);
     public static final Type TxSIStyleAtom = new Type(4009,null);
index 187dec12a12f8b578d7e4fb27240cd67f37e8cfe..2f3b898a7d1ac7c65ad96526cb412950c2dda11a 100644 (file)
@@ -127,8 +127,8 @@ public class StyleTextPropAtom extends RecordAtom
                                new ParagraphFlagsTextProp(),
                 new TextProp(2, 0x80, "bullet.char"),
                                new TextProp(2, 0x10, "bullet.font"),
+                new TextProp(2, 0x40, "bullet.size"),
                                new TextProp(4, 0x20, "bullet.color"),
-                               new TextProp(2, 0x40, "bullet.size"),
                 new AlignmentTextProp(),
                 new TextProp(2, 0x100, "text.offset"),
                                new TextProp(2, 0x200, "para_unknown_2"),
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/TextRulerAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/TextRulerAtom.java
new file mode 100755 (executable)
index 0000000..71ded85
--- /dev/null
@@ -0,0 +1,194 @@
+/* ====================================================================\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.hslf.record;\r
+\r
+import java.io.ByteArrayInputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.OutputStream;\r
+import java.util.zip.InflaterInputStream;\r
+\r
+import org.apache.poi.util.LittleEndian;\r
+import org.apache.poi.util.POILogger;\r
+\r
+/**\r
+ * Ruler of a text as it differs from the style's ruler settings.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class TextRulerAtom extends RecordAtom {\r
+\r
+    /**\r
+     * Record header.\r
+     */\r
+    private byte[] _header;\r
+\r
+    /**\r
+     * Record data.\r
+     */\r
+    private byte[] _data;\r
+\r
+    //ruler internals\r
+    private int defaultTabSize;\r
+    private int numLevels;\r
+    private int[] tabStops;\r
+    private int[] bulletOffsets = new int[5];\r
+    private int[] textOffsets = new int[5];\r
+\r
+    /**\r
+     * Constructs a new empty ruler atom.\r
+     */\r
+    protected TextRulerAtom() {\r
+        _header = new byte[8];\r
+        _data = new byte[0];\r
+\r
+        LittleEndian.putShort(_header, 2, (short)getRecordType());\r
+        LittleEndian.putInt(_header, 4, _data.length);\r
+    }\r
+\r
+    /**\r
+     * Constructs the ruler atom record from its\r
+     *  source data.\r
+     *\r
+     * @param source the source data as a byte array.\r
+     * @param start the start offset into the byte array.\r
+     * @param len the length of the slice in the byte array.\r
+     */\r
+    protected TextRulerAtom(byte[] source, int start, int len) {\r
+        // Get the header.\r
+        _header = new byte[8];\r
+        System.arraycopy(source,start,_header,0,8);\r
+\r
+        // Get the record data.\r
+        _data = new byte[len-8];\r
+        System.arraycopy(source,start+8,_data,0,len-8);\r
+\r
+        try {\r
+            read();\r
+        } catch (Exception e){\r
+            logger.log(POILogger.ERROR, "Failed to parse TextRulerAtom: " + e.getMessage()); \r
+            e.printStackTrace();\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Gets the record type.\r
+     *\r
+     * @return the record type.\r
+     */\r
+    public long getRecordType() {\r
+        return RecordTypes.TextRulerAtom.typeID;\r
+    }\r
+\r
+    /**\r
+     * Write the contents of the record back, so it can be written\r
+     * to disk.\r
+     *\r
+     * @param out the output stream to write to.\r
+     * @throws java.io.IOException if an error occurs.\r
+     */\r
+    public void writeOut(OutputStream out) throws IOException {\r
+        out.write(_header);\r
+        out.write(_data);\r
+    }\r
+\r
+    /**\r
+     * Read the record bytes and initialize the internal variables\r
+     */\r
+    private void read(){\r
+        int pos = 0;\r
+        short mask = LittleEndian.getShort(_data);  pos += 4;\r
+        short val;\r
+        int[] bits = {1, 0, 2, 3, 8, 4, 9, 5, 10, 6, 11, 7, 12};\r
+        for (int i = 0; i < bits.length; i++) {\r
+            if((mask & 1 << bits[i]) != 0){\r
+                switch (bits[i]){\r
+                    case 0:\r
+                        //defaultTabSize\r
+                        defaultTabSize = LittleEndian.getShort(_data, pos); pos += 2;\r
+                        break;\r
+                    case 1:\r
+                        //numLevels\r
+                        numLevels = LittleEndian.getShort(_data, pos); pos += 2;\r
+                        break;\r
+                    case 2:\r
+                        //tabStops\r
+                        val = LittleEndian.getShort(_data, pos); pos += 2;\r
+                        tabStops = new int[val*2];\r
+                        for (int j = 0; j < tabStops.length; j++) {\r
+                            tabStops[j] = LittleEndian.getUShort(_data, pos); pos += 2;\r
+                        }\r
+                        break;\r
+                    case 3:\r
+                    case 4:\r
+                    case 5:\r
+                    case 6:\r
+                    case 7:\r
+                        //bullet.offset\r
+                        val = LittleEndian.getShort(_data, pos); pos += 2;\r
+                        bulletOffsets[bits[i]-3] = val;\r
+                        break;\r
+                    case 8:\r
+                    case 9:\r
+                    case 10:\r
+                    case 11:\r
+                    case 12:\r
+                        //text.offset\r
+                        val = LittleEndian.getShort(_data, pos); pos += 2;\r
+                        textOffsets[bits[i]-8] = val;\r
+                        break;\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Default distance between tab stops, in master coordinates (576 dpi).\r
+     */\r
+    public int getDefaultTabSize(){\r
+        return defaultTabSize;\r
+    }\r
+\r
+    /**\r
+     * Number of indent levels (maximum 5).\r
+     */\r
+    public int getNumberOfLevels(){\r
+        return numLevels;\r
+    }\r
+\r
+    /**\r
+     * Default distance between tab stops, in master coordinates (576 dpi).\r
+     */\r
+    public int[] getTabStops(){\r
+        return tabStops;\r
+    }\r
+\r
+    /**\r
+     * Paragraph's distance from shape's left margin, in master coordinates (576 dpi).\r
+     */\r
+    public int[] getTextOffsets(){\r
+        return textOffsets;\r
+    }\r
+\r
+    /**\r
+     * First line of paragraph's distance from shape's left margin, in master coordinates (576 dpi).\r
+     */\r
+    public int[] getBulletOffsets(){\r
+        return bulletOffsets;\r
+    }\r
+}\r
index 2a09f2224c3dfa9a4103981838b76ab431215c77..7458df7e637fd64f1195e0d3fb8657cb79f09537 100644 (file)
@@ -32,6 +32,8 @@ import org.apache.poi.hslf.model.textproperties.ParagraphFlagsTextProp;
 import org.apache.poi.hslf.model.textproperties.TextProp;
 import org.apache.poi.hslf.model.textproperties.TextPropCollection;
 import org.apache.poi.hslf.record.ColorSchemeAtom;
+import org.apache.poi.util.POILogger;
+import org.apache.poi.util.POILogFactory;
 
 
 /**
@@ -39,6 +41,8 @@ import org.apache.poi.hslf.record.ColorSchemeAtom;
  * 
  */
 public class RichTextRun {
+    protected POILogger logger = POILogFactory.getLogger(this.getClass());
+
        /** The TextRun we belong to */
        private TextRun parentRun;
        /** The SlideShow we belong to */
@@ -199,10 +203,15 @@ public class RichTextRun {
         }
         if (prop == null){
             Sheet sheet = parentRun.getSheet();
-            int txtype = parentRun.getRunType();
-            MasterSheet master = sheet.getMasterSheet();
-            if (master != null)
-                prop = (BitMaskTextProp)master.getStyleAttribute(txtype, getIndentLevel(), propname, isCharacter);
+            if(sheet != null){
+                int txtype = parentRun.getRunType();
+                MasterSheet master = sheet.getMasterSheet();
+                if (master != null){
+                    prop = (BitMaskTextProp)master.getStyleAttribute(txtype, getIndentLevel(), propname, isCharacter);
+                }
+            } else {
+                logger.log(POILogger.WARN, "MasterSheet is not available");
+            }
         }
 
         return prop == null ? false : prop.getSubValue(index);
@@ -213,7 +222,7 @@ public class RichTextRun {
         *  it if required. 
         */
        private void setCharFlagsTextPropVal(int index, boolean value) {
-        setFlag(true, index, value);
+        if(getFlag(true, index) != value) setFlag(true, index, value);
        }
 
     public void setFlag(boolean isCharacter, int index, boolean value) {
@@ -281,10 +290,14 @@ public class RichTextRun {
         */
        private int getParaTextPropVal(String propName) {
         TextProp prop = null;
+        boolean hardAttribute = false;
         if (paragraphStyle != null){
             prop = paragraphStyle.findByName(propName);
+
+            BitMaskTextProp maskProp = (BitMaskTextProp)paragraphStyle.findByName(ParagraphFlagsTextProp.NAME);
+            hardAttribute = maskProp != null && maskProp.getValue() == 0;
         }
-        if (prop == null){
+        if (prop == null && !hardAttribute){
             Sheet sheet = parentRun.getSheet();
             int txtype = parentRun.getRunType();
             MasterSheet master = sheet.getMasterSheet();
@@ -574,6 +587,13 @@ public class RichTextRun {
         return getFlag(false, ParagraphFlagsTextProp.BULLET_IDX);
     }
 
+    /**
+     * Returns whether this rich text run has bullets
+     */
+    public boolean isBulletHard() {
+        return getFlag(false, ParagraphFlagsTextProp.BULLET_IDX);
+    }
+
     /**
      * Sets the bullet character
      */
index 828255087bf6606764b96cdf20b4d65502348221..d969c5b8810a41104f58ee86fd039b1319b7c247 100644 (file)
@@ -24,10 +24,7 @@ import java.util.*;
 import java.awt.Dimension;
 import java.io.*;
 
-import org.apache.poi.ddf.EscherBSERecord;
-import org.apache.poi.ddf.EscherContainerRecord;
-import org.apache.poi.ddf.EscherOptRecord;
-import org.apache.poi.ddf.EscherRecord;
+import org.apache.poi.ddf.*;
 import org.apache.poi.hslf.*;
 import org.apache.poi.hslf.model.*;
 import org.apache.poi.hslf.model.Notes;
@@ -66,9 +63,7 @@ public class SlideShow
   // Lookup between the PersitPtr "sheet" IDs, and the position
   //  in the mostRecentCoreRecords array
   private Hashtable _sheetIdToCoreRecordsLookup;
-  // Used when adding new core records
-  private int _highestSheetId;
-  
+
   // Records that are interesting
   private Document _documentRecord;
 
@@ -203,8 +198,6 @@ public class SlideShow
        for(int i=0; i<allIDs.length; i++) {
                _sheetIdToCoreRecordsLookup.put(new Integer(allIDs[i]), new Integer(i));
        }
-       // Capture the ID of the highest sheet
-       _highestSheetId = allIDs[(allIDs.length-1)];
 
        // Now convert the byte offsets back into record offsets
        for(int i=0; i<_records.length; i++) {
@@ -612,38 +605,35 @@ public class SlideShow
                                }
                        }
                }
-               
-               // Set up a new  SlidePersistAtom for this slide 
+
+               // Set up a new  SlidePersistAtom for this slide
                SlidePersistAtom sp = new SlidePersistAtom();
 
-               // Reference is the 1-based index of the slide container in 
-               //  the PersistPtr root.
-               // It always starts with 3 (1 is Document, 2 is MainMaster, 3 is 
-               //  the first slide), but quicksaves etc can leave gaps
-               _highestSheetId++;
-               sp.setRefID(_highestSheetId);
                // First slideId is always 256
                sp.setSlideIdentifier(prev == null ? 256 : (prev.getSlideIdentifier() + 1));
-               
+
                // Add this new SlidePersistAtom to the SlideListWithText
                slist.addSlidePersistAtom(sp);
-               
-               
+
+
                // Create a new Slide
                Slide slide = new Slide(sp.getSlideIdentifier(), sp.getRefID(), _slides.length+1);
+        slide.setSlideShow(this);
+        slide.onCreate();
+
                // Add in to the list of Slides
                Slide[] s = new Slide[_slides.length+1];
                System.arraycopy(_slides, 0, s, 0, _slides.length);
                s[_slides.length] = slide;
                _slides = s;
                logger.log(POILogger.INFO, "Added slide " + _slides.length + " with ref " + sp.getRefID() + " and identifier " + sp.getSlideIdentifier());
-               
+
                // Add the core records for this new Slide to the record tree
                org.apache.poi.hslf.record.Slide slideRecord = slide.getSlideRecord();
-               slideRecord.setSheetId(sp.getRefID());
                int slideRecordPos = _hslfSlideShow.appendRootLevelRecord(slideRecord);
                _records = _hslfSlideShow.getRecords();
 
+
                // Add the new Slide into the PersistPtr stuff
                int offset = 0;
                int slideOffset = 0;
@@ -653,7 +643,7 @@ public class SlideShow
                        Record record = _records[i];
                        ByteArrayOutputStream out = new ByteArrayOutputStream();
                        record.writeOut(out);
-                       
+
                        // Grab interesting records as they come past
                        if(_records[i].getRecordType() == RecordTypes.PersistPtrIncrementalBlock.typeID){
                                ptr = (PersistPtrHolder)_records[i];
@@ -661,25 +651,29 @@ public class SlideShow
                        if(_records[i].getRecordType() == RecordTypes.UserEditAtom.typeID) {
                                usr = (UserEditAtom)_records[i];
                        }
-                       
+
                        if(i == slideRecordPos) {
                                slideOffset = offset;
                        }
                        offset += out.size();
                }
-               
+
+        // persist ID is UserEditAtom.maxPersistWritten + 1
+        int psrId = usr.getMaxPersistWritten() + 1;
+        sp.setRefID(psrId);
+        slideRecord.setSheetId(psrId);
+
+        // Last view is now of the slide
+        usr.setLastViewType((short)UserEditAtom.LAST_VIEW_SLIDE_VIEW);
+        usr.setMaxPersistWritten(psrId);    //increment the number of persit objects
+
                // Add the new slide into the last PersistPtr
                // (Also need to tell it where it is)
                slideRecord.setLastOnDiskOffset(slideOffset);
                ptr.addSlideLookup(sp.getRefID(), slideOffset);
                logger.log(POILogger.INFO, "New slide ended up at " + slideOffset);
 
-               // Last view is now of the slide
-               usr.setLastViewType((short)UserEditAtom.LAST_VIEW_SLIDE_VIEW);
-               usr.setMaxPersistWritten(_highestSheetId);
-
                // All done and added
-               slide.setSlideShow(this);
                return slide;
        }
 
index 5e7e7cc4b7e359abcd075e3ed767a45987bd951b..56d5a099cb08de70eaba7fe7e9e71f49c77d9c4c 100755 (executable)
@@ -52,7 +52,7 @@ public class TestFreeform extends TestCase {
         Freeform p = new Freeform();\r
         p.setPath(path1);\r
 \r
-        GeneralPath path2 = p.getPath();\r
+        java.awt.Shape path2 = p.getOutline();\r
         assertTrue(new Area(path1).equals(new Area(path2)));\r
     }\r
 \r
@@ -63,7 +63,7 @@ public class TestFreeform extends TestCase {
         Freeform p = new Freeform();\r
         p.setPath(path1);\r
 \r
-        GeneralPath path2 = p.getPath();\r
+        java.awt.Shape path2 = p.getOutline();\r
         assertTrue(new Area(path1).equals(new Area(path2)));\r
     }\r
 \r
@@ -74,7 +74,7 @@ public class TestFreeform extends TestCase {
         Freeform p = new Freeform();\r
         p.setPath(path1);\r
 \r
-        GeneralPath path2 = p.getPath();\r
+        java.awt.Shape path2 = p.getOutline();\r
         assertTrue(new Area(path1).equals(new Area(path2)));\r
-    }\r
+   }\r
 }\r
index 511ef7f7453fc6d27317c0da4ae3f09610b05caf..3ce6b91caf285bed18a5313b35342b1e36a9514c 100644 (file)
@@ -20,6 +20,8 @@ import junit.framework.TestCase;
 import org.apache.poi.hslf.usermodel.SlideShow;
 import org.apache.poi.hslf.usermodel.RichTextRun;
 import org.apache.poi.hslf.HSLFSlideShow;
+import org.apache.poi.ddf.EscherDggRecord;
+import org.apache.poi.ddf.EscherDgRecord;
 
 import java.awt.*;
 import java.awt.Rectangle;
@@ -311,18 +313,49 @@ public class TestShapes extends TestCase {
     public void testShapeId() throws IOException {
         SlideShow ppt = new SlideShow();
         Slide slide = ppt.createSlide();
-        Shape shape;
-
-        shape = new Line();
-        assertEquals(0, shape.getShapeId());
-        slide.addShape(shape);
-        assertTrue(shape.getShapeId() > 0);
+        Shape shape = null;
+
+        //EscherDgg is a document-level record which keeps track of the drawing groups
+        EscherDggRecord dgg = ppt.getDocumentRecord().getPPDrawingGroup().getEscherDggRecord();
+        EscherDgRecord dg = slide.getSheetContainer().getPPDrawing().getEscherDgRecord();
+
+        int dggShapesUsed = dgg.getNumShapesSaved();   //total number of shapes in the ppt
+        int dggMaxId = dgg.getShapeIdMax();            //max number of shapeId
+
+        int dgMaxId = dg.getLastMSOSPID();   //max shapeId in the slide
+        int dgShapesUsed = dg.getNumShapes();          // number of shapes in the slide
+        //insert 3 shapes and make sure the Ids are properly incremented
+        for (int i = 0; i < 3; i++) {
+            shape = new Line();
+            assertEquals(0, shape.getShapeId());
+            slide.addShape(shape);
+            assertTrue(shape.getShapeId() > 0);
+
+            //check that EscherDgRecord is updated
+            assertEquals(shape.getShapeId(), dg.getLastMSOSPID());
+            assertEquals(dgMaxId + 1, dg.getLastMSOSPID());
+            assertEquals(dgShapesUsed + 1, dg.getNumShapes());
+
+            //check that EscherDggRecord is updated
+            assertEquals(shape.getShapeId() + 1, dgg.getShapeIdMax());
+            assertEquals(dggMaxId + 1, dgg.getShapeIdMax());
+            assertEquals(dggShapesUsed + 1, dgg.getNumShapesSaved());
+
+            dggShapesUsed = dgg.getNumShapesSaved();
+            dggMaxId = dgg.getShapeIdMax();
+            dgMaxId = dg.getLastMSOSPID();
+            dgShapesUsed = dg.getNumShapes();
+        }
 
-        int shapeId = shape.getShapeId();
 
-        shape = new Line();
-        assertEquals(0, shape.getShapeId());
-        slide.addShape(shape);
-        assertEquals(shapeId + 1, shape.getShapeId());
+        //For each drawing group PPT allocates clusters with size=1024
+        //if the number of shapes is greater that 1024 a new cluster is allocated
+        //make sure it is so
+        int numClusters = dgg.getNumIdClusters();
+        for (int i = 0; i < 1025; i++) {
+            shape = new Line();
+            slide.addShape(shape);
+        }
+        assertEquals(numClusters + 1, dgg.getNumIdClusters());
     }
 }
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextRulerAtom.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextRulerAtom.java
new file mode 100755 (executable)
index 0000000..0b610cb
--- /dev/null
@@ -0,0 +1,76 @@
+\r
+/* ====================================================================\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
+\r
+\r
+package org.apache.poi.hslf.record;\r
+\r
+import org.apache.poi.hslf.HSLFSlideShow;\r
+import org.apache.poi.hslf.model.textproperties.CharFlagsTextProp;\r
+import org.apache.poi.hslf.model.textproperties.TextProp;\r
+import org.apache.poi.hslf.model.textproperties.TextPropCollection;\r
+import org.apache.poi.hslf.record.StyleTextPropAtom.*;\r
+import org.apache.poi.hslf.usermodel.SlideShow;\r
+import org.apache.poi.util.HexDump;\r
+\r
+import junit.framework.TestCase;\r
+import java.io.ByteArrayOutputStream;\r
+import java.util.LinkedList;\r
+import java.util.Arrays;\r
+\r
+/**\r
+ * Tests TextRulerAtom\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class TestTextRulerAtom extends TestCase {\r
+\r
+    //from a real file\r
+       private byte[] data_1 = new byte[] {\r
+               0x00, 0x00, (byte)0xA6, 0x0F, 0x18, 0x00, 0x00, 0x00,\r
+        (byte)0xF8, 0x1F, 0x00, 0x00, 0x75, 0x00, (byte)0xE2, 0x00, 0x59,\r
+        0x01, (byte)0xC3, 0x01, 0x1A, 0x03, (byte)0x87, 0x03, (byte)0xF8,\r
+        0x03, 0x69, 0x04, (byte)0xF6, 0x05, (byte)0xF6, 0x05\r
+       };\r
+\r
+\r
+    public void testReadRuler() throws Exception {\r
+               TextRulerAtom ruler = new TextRulerAtom(data_1, 0, data_1.length);\r
+        assertEquals(ruler.getNumberOfLevels(), 0);\r
+        assertEquals(ruler.getDefaultTabSize(), 0);\r
+\r
+        int[] tabStops = ruler.getTabStops();\r
+        assertNull(tabStops);\r
+\r
+        int[] textOffsets = ruler.getTextOffsets();\r
+        assertTrue(Arrays.equals(new int[]{226, 451, 903, 1129, 1526}, textOffsets));\r
+\r
+        int[] bulletOffsets = ruler.getBulletOffsets();\r
+        assertTrue(Arrays.equals(new int[]{117, 345, 794, 1016, 1526}, bulletOffsets));\r
+\r
+       }\r
+\r
+    public void testWriteRuler() throws Exception {\r
+               TextRulerAtom ruler = new TextRulerAtom(data_1, 0, data_1.length);\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        ruler.writeOut(out);\r
+\r
+        byte[] result = out.toByteArray();\r
+        assertTrue(Arrays.equals(result, data_1));\r
+       }\r
+}\r
index 0fecdab97b4a9e8ea474f7c3cc66624d9dcd4f61..eda6589c4299fa450e48d18453a55635f616d498 100644 (file)
@@ -90,9 +90,11 @@ public class TestRichTextRun extends TestCase {
                
                // Now set it to not bold
                rtr.setBold(false);
-               assertNotNull(rtr._getRawCharacterStyle());
-               assertNotNull(rtr._getRawParagraphStyle());
-               assertFalse(rtr.isBold());
+               //setting bold=false doesn't change the internal state
+        assertNull(rtr._getRawCharacterStyle());
+               assertNull(rtr._getRawParagraphStyle());
+
+        assertFalse(rtr.isBold());
                
                // And now make it bold
                rtr.setBold(true);
index 9b5fc0bfef772b84cc910bce2fdeca140c7a49f9..ca82ac83cfef3338f52419cd37abf3e0f3d30a1c 100755 (executable)
@@ -42,6 +42,7 @@ public final class AllPOIDDFTests {
         result.addTestSuite(TestEscherSplitMenuColorsRecord.class);
         result.addTestSuite(TestEscherSpRecord.class);
         result.addTestSuite(TestUnknownEscherRecord.class);
+        result.addTestSuite(TestEscherBlipRecord.class);
         return result;
     }
 }
diff --git a/src/testcases/org/apache/poi/ddf/TestEscherBlipRecord.java b/src/testcases/org/apache/poi/ddf/TestEscherBlipRecord.java
new file mode 100755 (executable)
index 0000000..f7fecb6
--- /dev/null
@@ -0,0 +1,156 @@
+\r
+/* ====================================================================\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.ddf;\r
+\r
+import junit.framework.TestCase;\r
+import org.apache.poi.util.HexRead;\r
+import org.apache.poi.util.HexDump;\r
+\r
+import java.io.IOException;\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.util.Iterator;\r
+import java.util.Arrays;\r
+\r
+/**\r
+ * Test read/serialize of escher blip records\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class TestEscherBlipRecord extends TestCase\r
+{\r
+    protected String cwd = System.getProperty("DDF.testdata.path");\r
+\r
+    //test reading/serializing of a PNG blip\r
+    public void testReadPNG() throws IOException {\r
+        //provided in bug-44886\r
+        byte[] data = read(new File(cwd, "Container.dat"));\r
+\r
+        EscherContainerRecord record = new EscherContainerRecord();\r
+        record.fillFields(data, 0, new DefaultEscherRecordFactory());\r
+        EscherContainerRecord bstore = (EscherContainerRecord)record.getChildRecords().get(1);\r
+        EscherBSERecord bse1 = (EscherBSERecord)bstore.getChildRecords().get(0);\r
+        assertEquals(EscherBSERecord.BT_PNG, bse1.getBlipTypeWin32());\r
+        assertEquals(EscherBSERecord.BT_PNG, bse1.getBlipTypeMacOS());\r
+        assertTrue(Arrays.equals(new byte[]{\r
+            0x65, 0x07, 0x4A, (byte)0x8D, 0x3E, 0x42, (byte)0x8B, (byte)0xAC,\r
+            0x1D, (byte)0x89, 0x35, 0x4F, 0x48, (byte)0xFA, 0x37, (byte)0xC2\r
+        }, bse1.getUid()));\r
+        assertEquals(255, bse1.getTag());\r
+        assertEquals(32308, bse1.getSize());\r
+\r
+        EscherBitmapBlip blip1 = (EscherBitmapBlip)bse1.getBlipRecord();\r
+        assertEquals(0x6E00, blip1.getOptions());\r
+        assertEquals(EscherBitmapBlip.RECORD_ID_PNG, blip1.getRecordId());\r
+        assertTrue(Arrays.equals(new byte[]{\r
+            0x65, 0x07, 0x4A, (byte)0x8D, 0x3E, 0x42, (byte)0x8B, (byte)0xAC,\r
+            0x1D, (byte)0x89, 0x35, 0x4F, 0x48, (byte)0xFA, 0x37, (byte)0xC2\r
+        }, blip1.getUID()));\r
+\r
+        //serialize and read again\r
+        byte[] ser = bse1.serialize();\r
+        EscherBSERecord bse2 = new EscherBSERecord();\r
+        bse2.fillFields(ser, 0, new DefaultEscherRecordFactory());\r
+        assertEquals(bse1.getRecordId(), bse2.getRecordId());\r
+        assertEquals(bse1.getBlipTypeWin32(), bse2.getBlipTypeWin32());\r
+        assertEquals(bse1.getBlipTypeMacOS(), bse2.getBlipTypeMacOS());\r
+        assertTrue(Arrays.equals(bse1.getUid(), bse2.getUid()));\r
+        assertEquals(bse1.getTag(), bse2.getTag());\r
+        assertEquals(bse1.getSize(), bse2.getSize());\r
+\r
+        EscherBitmapBlip blip2 = (EscherBitmapBlip)bse1.getBlipRecord();\r
+        assertEquals(blip1.getOptions(), blip2.getOptions());\r
+        assertEquals(blip1.getRecordId(), blip2.getRecordId());\r
+        assertEquals(blip1.getUID(), blip2.getUID());\r
+\r
+        assertTrue(Arrays.equals(blip1.getPicturedata(), blip1.getPicturedata()));\r
+    }\r
+\r
+    //test reading/serializing of a PICT metafile\r
+    public void testReadPICT() throws IOException {\r
+        //provided in bug-44886\r
+        byte[] data = read(new File(cwd, "Container.dat"));\r
+\r
+        EscherContainerRecord record = new EscherContainerRecord();\r
+        record.fillFields(data, 0, new DefaultEscherRecordFactory());\r
+        EscherContainerRecord bstore = (EscherContainerRecord)record.getChildRecords().get(1);\r
+        EscherBSERecord bse1 = (EscherBSERecord)bstore.getChildRecords().get(1);\r
+        //System.out.println(bse1);\r
+        assertEquals(EscherBSERecord.BT_WMF, bse1.getBlipTypeWin32());\r
+        assertEquals(EscherBSERecord.BT_PICT, bse1.getBlipTypeMacOS());\r
+        assertTrue(Arrays.equals(new byte[]{\r
+            (byte)0xC7, 0x15, 0x69, 0x2D, (byte)0xE5, (byte)0x89, (byte)0xA3, 0x6F,\r
+            0x66, 0x03, (byte)0xD6, 0x24, (byte)0xF7, (byte)0xDB, 0x1D, 0x13\r
+        }, bse1.getUid()));\r
+        assertEquals(255, bse1.getTag());\r
+        assertEquals(1133, bse1.getSize());\r
+\r
+        EscherMetafileBlip blip1 = (EscherMetafileBlip)bse1.getBlipRecord();\r
+        assertEquals(0x5430, blip1.getOptions());\r
+        assertEquals(EscherMetafileBlip.RECORD_ID_PICT, blip1.getRecordId());\r
+        assertTrue(Arrays.equals(new byte[]{\r
+            0x57, 0x32, 0x7B, (byte)0x91, 0x23, 0x5D, (byte)0xDB, 0x36,\r
+            0x7A, (byte)0xDB, (byte)0xFF, 0x17, (byte)0xFE, (byte)0xF3, (byte)0xA7, 0x05\r
+        }, blip1.getUID()));\r
+        assertTrue(Arrays.equals(new byte[]{\r
+            (byte)0xC7, 0x15, 0x69, 0x2D, (byte)0xE5, (byte)0x89, (byte)0xA3, 0x6F,\r
+            0x66, 0x03, (byte)0xD6, 0x24, (byte)0xF7, (byte)0xDB, 0x1D, 0x13\r
+        }, blip1.getPrimaryUID()));\r
+\r
+        //serialize and read again\r
+        byte[] ser = bse1.serialize();\r
+        EscherBSERecord bse2 = new EscherBSERecord();\r
+        bse2.fillFields(ser, 0, new DefaultEscherRecordFactory());\r
+        assertEquals(bse1.getRecordId(), bse2.getRecordId());\r
+        assertEquals(bse1.getOptions(), bse2.getOptions());\r
+        assertEquals(bse1.getBlipTypeWin32(), bse2.getBlipTypeWin32());\r
+        assertEquals(bse1.getBlipTypeMacOS(), bse2.getBlipTypeMacOS());\r
+        assertTrue(Arrays.equals(bse1.getUid(), bse2.getUid()));\r
+        assertEquals(bse1.getTag(), bse2.getTag());\r
+        assertEquals(bse1.getSize(), bse2.getSize());\r
+\r
+        EscherMetafileBlip blip2 = (EscherMetafileBlip)bse1.getBlipRecord();\r
+        assertEquals(blip1.getOptions(), blip2.getOptions());\r
+        assertEquals(blip1.getRecordId(), blip2.getRecordId());\r
+        assertEquals(blip1.getUID(), blip2.getUID());\r
+        assertEquals(blip1.getPrimaryUID(), blip2.getPrimaryUID());\r
+\r
+        assertTrue(Arrays.equals(blip1.getPicturedata(), blip1.getPicturedata()));\r
+    }\r
+\r
+    //integral test: check that the read-write-read round trip is consistent\r
+    public void testContainer() throws IOException {\r
+        byte[] data = read(new File(cwd, "Container.dat"));\r
+\r
+        EscherContainerRecord record = new EscherContainerRecord();\r
+        record.fillFields(data, 0, new DefaultEscherRecordFactory());\r
+\r
+        byte[] ser = record.serialize();\r
+        assertTrue(Arrays.equals(data, ser));\r
+    }\r
+\r
+    private byte[] read(File file) throws IOException {\r
+        byte[] data = new byte[(int)file.length()];\r
+        FileInputStream is = new FileInputStream(file);\r
+        is.read(data);\r
+        is.close();\r
+        return data;\r
+    }\r
+\r
+}\r
index 7c139827b6bf3e39723626e84a7249f0fae9d9e7..0f1fc9c7330e1d4f690cc3160a9c5cd225282c9e 100644 (file)
         
 package org.apache.poi.ddf;
 
+import java.io.File;
+import java.io.FileInputStream;
+
 import junit.framework.TestCase;
 import org.apache.poi.util.HexRead;
 import org.apache.poi.util.HexDump;
+import org.apache.poi.util.IOUtils;
 
 public class TestEscherContainerRecord extends TestCase
 {
-    public void testFillFields() throws Exception
+       private String ESCHER_DATA_PATH;
+       
+       protected void setUp() throws Exception {
+               super.setUp();
+               ESCHER_DATA_PATH = System.getProperty("DDF.testdata.path");
+       }
+
+       public void testFillFields() throws Exception
     {
         EscherRecordFactory f = new DefaultEscherRecordFactory();
         byte[] data = HexRead.readFromString( "0F 02 11 F1 00 00 00 00" );
@@ -137,4 +148,19 @@ public class TestEscherContainerRecord extends TestCase
         assertEquals(18, r.getRecordSize());
     }
 
+    /**
+     * We were having problems with reading too much data on an UnknownEscherRecord,
+     *  but hopefully we now read the correct size.
+     */
+    public void testBug44857() throws Exception {
+       File f = new File(ESCHER_DATA_PATH, "Container.dat");
+       assertTrue(f.exists());
+       
+       FileInputStream finp = new FileInputStream(f);
+       byte[] data = IOUtils.toByteArray(finp);
+       
+       // This used to fail with an OutOfMemory
+       EscherContainerRecord record = new EscherContainerRecord();
+       record.fillFields(data, 0, new DefaultEscherRecordFactory());
+    }
 }
diff --git a/src/testcases/org/apache/poi/ddf/data/Container.dat b/src/testcases/org/apache/poi/ddf/data/Container.dat
new file mode 100644 (file)
index 0000000..c1b5447
Binary files /dev/null and b/src/testcases/org/apache/poi/ddf/data/Container.dat differ
index 703551f88e7e0e3ab2ab07d7960eabab640e5cbe..0e64637f97280aa3897ebfd894e5eff63fdd8cb0 100644 (file)
@@ -172,4 +172,28 @@ public final class HSSFTestDataSamples {
                        throw new RuntimeException(e);\r
                }\r
        }\r
+\r
+       /**\r
+        * @return byte array of sample file content from file found in standard hssf test data dir \r
+        */\r
+       public static byte[] getTestDataFileContent(String fileName) {\r
+               ByteArrayOutputStream bos = new ByteArrayOutputStream();\r
+\r
+               try {\r
+                       InputStream fis = HSSFTestDataSamples.openSampleFileStream(fileName);\r
+\r
+                       byte[] buf = new byte[512];\r
+                       while (true) {\r
+                               int bytesRead = fis.read(buf);\r
+                               if (bytesRead < 1) {\r
+                                       break;\r
+                               }\r
+                               bos.write(buf, 0, bytesRead);\r
+                       }\r
+                       fis.close();\r
+               } catch (IOException e) {\r
+                       throw new RuntimeException(e);\r
+               }\r
+               return bos.toByteArray();\r
+       }\r
 }\r
diff --git a/src/testcases/org/apache/poi/hssf/data/44235.xls b/src/testcases/org/apache/poi/hssf/data/44235.xls
new file mode 100755 (executable)
index 0000000..39b7658
Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/44235.xls differ
diff --git a/src/testcases/org/apache/poi/hssf/data/44840.xls b/src/testcases/org/apache/poi/hssf/data/44840.xls
new file mode 100644 (file)
index 0000000..66dd918
Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/44840.xls differ
diff --git a/src/testcases/org/apache/poi/hssf/data/44861.xls b/src/testcases/org/apache/poi/hssf/data/44861.xls
new file mode 100644 (file)
index 0000000..bab82b3
Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/44861.xls differ
diff --git a/src/testcases/org/apache/poi/hssf/data/44891.xls b/src/testcases/org/apache/poi/hssf/data/44891.xls
new file mode 100644 (file)
index 0000000..30082aa
Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/44891.xls differ
diff --git a/src/testcases/org/apache/poi/hssf/data/ex42570-20305.xls b/src/testcases/org/apache/poi/hssf/data/ex42570-20305.xls
new file mode 100644 (file)
index 0000000..e3d4cc8
Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/ex42570-20305.xls differ
diff --git a/src/testcases/org/apache/poi/hssf/data/ex44921-21902.xls b/src/testcases/org/apache/poi/hssf/data/ex44921-21902.xls
new file mode 100644 (file)
index 0000000..d0fbf3c
Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/ex44921-21902.xls differ
index 31b4dd180abbe4e9b07c6e9f33913a36275d5770..1895705a564bb9bc961a66b5f85c56d362a88849 100644 (file)
@@ -65,6 +65,7 @@ public final class TestFormulaParser extends TestCase {
      * @return parsed token array already confirmed not <code>null</code>
      */
     private static Ptg[] parseFormula(String s) {
+       // TODO - replace multiple copies of this code with calls to this method
         FormulaParser fp = new FormulaParser(s, null);
         fp.parse();
         Ptg[] result = fp.getRPNPtg();
@@ -86,7 +87,6 @@ public final class TestFormulaParser extends TestCase {
         assertTrue("",(ptgs[0] instanceof IntPtg));
         assertTrue("",(ptgs[1] instanceof IntPtg));
         assertTrue("",(ptgs[2] instanceof AddPtg));
-
     }
 
     public void testFormulaWithSpace2() {
@@ -169,8 +169,6 @@ public final class TestFormulaParser extends TestCase {
                assertEquals("If FALSE offset", (short)7, ifPtg.getData());
                
                FuncVarPtg funcPtg = (FuncVarPtg)asts[8];
-               
-               
        }
 
        /**
@@ -190,8 +188,6 @@ public final class TestFormulaParser extends TestCase {
                assertTrue("It is not an if", ifFunc.isOptimizedIf());
                
                assertTrue("Average Function set correctly", (asts[5] instanceof FuncVarPtg));
-               
-                                       
        }
        
        public void testIfSingleCondition(){
@@ -213,8 +209,6 @@ public final class TestFormulaParser extends TestCase {
                assertTrue("Ptg is not a Variable Function", (asts[6] instanceof FuncVarPtg));
                FuncVarPtg funcPtg = (FuncVarPtg)asts[6];
                assertEquals("Arguments", 2, funcPtg.getNumberOfOperands());
-               
-               
        }
 
        public void testSumIf() {
@@ -223,7 +217,6 @@ public final class TestFormulaParser extends TestCase {
                fp.parse();
                Ptg[] asts = fp.getRPNPtg();
                assertEquals("4 Ptgs expected", 4, asts.length);
-               
        }
        
        /**
@@ -235,51 +228,35 @@ public final class TestFormulaParser extends TestCase {
                String currencyCell = "F3";
                String function="\"TOTAL[\"&"+currencyCell+"&\"]\"";
 
-               FormulaParser fp = new FormulaParser(function, null);
-               fp.parse();
-               Ptg[] asts = fp.getRPNPtg();
+               Ptg[] asts = parseFormula(function);
                assertEquals("5 ptgs expected", 5, asts.length);
                assertTrue ("Ptg[0] is a string", (asts[0] instanceof StringPtg));
                StringPtg firstString = (StringPtg)asts[0];             
                
                assertEquals("TOTAL[", firstString.getValue());
                //the PTG order isn't 100% correct but it still works - dmui
-               
-                                       
        }
 
        public void testSimpleLogical() {
-               FormulaParser fp=new FormulaParser("IF(A1<A2,B1,B2)",null);
-               fp.parse();
-      Ptg[] ptgs = fp.getRPNPtg();
-      assertTrue("Ptg array should not be null", ptgs !=null);
+      Ptg[] ptgs = parseFormula("IF(A1<A2,B1,B2)");
       assertEquals("Ptg array length", 9, ptgs.length);
-      assertEquals("3rd Ptg is less than",LessThanPtg.class,ptgs[2].getClass());
-
-
+      assertEquals("3rd Ptg is less than", LessThanPtg.class, ptgs[2].getClass());
        }
        
        public void testParenIf() {
-               FormulaParser fp=new FormulaParser("IF((A1+A2)<=3,\"yes\",\"no\")",null);
-               fp.parse();
-               Ptg[] ptgs = fp.getRPNPtg();
-               assertTrue("Ptg array should not be null", ptgs !=null);
+               Ptg[] ptgs = parseFormula("IF((A1+A2)<=3,\"yes\",\"no\")");
                assertEquals("Ptg array length", 12, ptgs.length);
                assertEquals("6th Ptg is less than equal",LessEqualPtg.class,ptgs[5].getClass());
                assertEquals("11th Ptg is not a goto (Attr) ptg",AttrPtg.class,ptgs[10].getClass());
        }
        
        public void testEmbeddedIf() {
-               FormulaParser fp=new FormulaParser("IF(3>=1,\"*\",IF(4<>1,\"first\",\"second\"))",null);
-               fp.parse();
-               Ptg[] ptgs = fp.getRPNPtg();
-               assertTrue("Ptg array should not be null", ptgs !=null);
+               Ptg[] ptgs = parseFormula("IF(3>=1,\"*\",IF(4<>1,\"first\",\"second\"))");
                assertEquals("Ptg array length", 17, ptgs.length);
                
                assertEquals("6th Ptg is not a goto (Attr) ptg",AttrPtg.class,ptgs[5].getClass());
                assertEquals("9th Ptg is not a not equal ptg",NotEqualPtg.class,ptgs[8].getClass());
                assertEquals("15th Ptg is not the inner IF variable function ptg",FuncVarPtg.class,ptgs[14].getClass());
-               
        }
        
     public void testMacroFunction() {
@@ -302,16 +279,15 @@ public final class TestFormulaParser extends TestCase {
         Ptg[] ptg = fp.getRPNPtg();
         assertTrue("first ptg is string",ptg[0] instanceof StringPtg);
         assertTrue("second ptg is string",ptg[1] instanceof StringPtg);
-
     }
 
-    public void testConcatenate(){
-         FormulaParser fp = new FormulaParser("CONCATENATE(\"first\",\"second\")",null);
-         fp.parse();
-         Ptg[] ptg = fp.getRPNPtg();
-        assertTrue("first ptg is string",ptg[0] instanceof StringPtg);
-        assertTrue("second ptg is string",ptg[1] instanceof StringPtg);
-    }
+    public void testConcatenate() {
+               FormulaParser fp = new FormulaParser("CONCATENATE(\"first\",\"second\")", null);
+               fp.parse();
+               Ptg[] ptg = fp.getRPNPtg();
+               assertTrue("first ptg is string", ptg[0] instanceof StringPtg);
+               assertTrue("second ptg is string", ptg[1] instanceof StringPtg);
+       }
 
     public void testWorksheetReferences()
     {
@@ -395,16 +371,16 @@ public final class TestFormulaParser extends TestCase {
 
        /** bug 33160, testcase by Amol Deshmukh*/
        public void testSimpleLongFormula() {
-                       FormulaParser fp = new FormulaParser("40000/2", null);
-                       fp.parse();
-                       Ptg[] ptgs = fp.getRPNPtg();
-                       assertTrue("three tokens expected, got "+ptgs.length,ptgs.length == 3);
-                       assertTrue("IntPtg",(ptgs[0] instanceof IntPtg));
-                       assertTrue("IntPtg",(ptgs[1] instanceof IntPtg));
-                       assertTrue("DividePtg",(ptgs[2] instanceof DividePtg));
+               FormulaParser fp = new FormulaParser("40000/2", null);
+               fp.parse();
+               Ptg[] ptgs = fp.getRPNPtg();
+               assertTrue("three tokens expected, got " + ptgs.length, ptgs.length == 3);
+               assertTrue("IntPtg", (ptgs[0] instanceof IntPtg));
+               assertTrue("IntPtg", (ptgs[1] instanceof IntPtg));
+               assertTrue("DividePtg", (ptgs[2] instanceof DividePtg));
        }
        
-       /** bug 35027, underscore in sheet name*/
+       /** bug 35027, underscore in sheet name */
        public void testUnderscore() {
                HSSFWorkbook wb = new HSSFWorkbook();
        
@@ -592,7 +568,7 @@ public final class TestFormulaParser extends TestCase {
        HSSFWorkbook book = new HSSFWorkbook();
 
         Ptg[] ptgs = {
-                new FuncPtg(10, 0),
+                new FuncPtg(10),
         };
         assertEquals("NA()", FormulaParser.toFormulaString(book, ptgs));
     }
@@ -775,8 +751,34 @@ public final class TestFormulaParser extends TestCase {
         StringPtg sp = (StringPtg) parseSingleToken(formula, StringPtg.class);
         assertEquals(expectedValue, sp.getValue());
     }
+    public void testParseStringLiterals_bug28754() {
+
+        StringPtg sp;
+        try {
+            sp = (StringPtg) parseSingleToken("\"test\"\"ing\"", StringPtg.class);
+        } catch (RuntimeException e) {
+            if(e.getMessage().startsWith("Cannot Parse")) {
+                throw new AssertionFailedError("Identified bug 28754a");
+            }
+            throw e;
+        }
+        assertEquals("test\"ing", sp.getValue());
+
+        HSSFWorkbook wb = new HSSFWorkbook();
+        HSSFSheet sheet = wb.createSheet();
+        wb.setSheetName(0, "Sheet1");
+
+        HSSFRow row = sheet.createRow(0);
+        HSSFCell cell = row.createCell((short)0);
+        cell.setCellFormula("right(\"test\"\"ing\", 3)");
+        String actualCellFormula = cell.getCellFormula();
+        if("RIGHT(\"test\"ing\",3)".equals(actualCellFormula)) {
+            throw new AssertionFailedError("Identified bug 28754b");
+        }
+        assertEquals("RIGHT(\"test\"\"ing\",3)", actualCellFormula);
+    }
 
-    public void testPaseStringLiterals() {
+    public void testParseStringLiterals() {
         confirmStringParse("goto considered harmful");
 
         confirmStringParse("goto 'considered' harmful");
@@ -810,10 +812,8 @@ public final class TestFormulaParser extends TestCase {
         parseExpectedException("#DIV/ 0+2");
 
 
-        if (false) { // TODO - add functionality to detect func arg count mismatch
-            parseExpectedException("IF(TRUE)");
-            parseExpectedException("countif(A1:B5, C1, D1)");
-        }
+        parseExpectedException("IF(TRUE)");
+        parseExpectedException("countif(A1:B5, C1, D1)");
     }
 
     private static void parseExpectedException(String formula) {
@@ -887,8 +887,16 @@ public final class TestFormulaParser extends TestCase {
             assertTrue(e.getMessage().startsWith("Too few arguments suppled to operation token"));
         }
     }
+    /**
+     * Make sure that POI uses the right Func Ptg when encoding formulas.  Functions with variable
+     * number of args should get FuncVarPtg, functions with fixed args should get FuncPtg.<p/>
+     * 
+     * Prior to the fix for bug 44675 POI would encode FuncVarPtg for all functions.  In many cases
+     * Excel tolerates the wrong Ptg and evaluates the formula OK (e.g. SIN), but in some cases 
+     * (e.g. COUNTIF) Excel fails to evaluate the formula, giving '#VALUE!' instead. 
+     */
     public void testFuncPtgSelection() {
-       HSSFWorkbook book = new HSSFWorkbook();
+        HSSFWorkbook book = new HSSFWorkbook();
         Ptg[] ptgs;
         ptgs = FormulaParser.parse("countif(A1:A2, 1)", book);
         assertEquals(3, ptgs.length);
@@ -900,4 +908,21 @@ public final class TestFormulaParser extends TestCase {
         assertEquals(2, ptgs.length);
         assertEquals(FuncPtg.class, ptgs[1].getClass());
     }
+    
+    public void testWrongNumberOfFunctionArgs() {
+        confirmArgCountMsg("sin()", "Too few arguments to function 'SIN'. Expected 1 but got 0.");
+        confirmArgCountMsg("countif(1, 2, 3, 4)", "Too many arguments to function 'COUNTIF'. Expected 2 but got 4.");
+        confirmArgCountMsg("index(1, 2, 3, 4, 5, 6)", "Too many arguments to function 'INDEX'. At most 4 were expected but got 6.");
+        confirmArgCountMsg("vlookup(1, 2)", "Too few arguments to function 'VLOOKUP'. At least 3 were expected but got 2.");
+    }
+
+    private static void confirmArgCountMsg(String formula, String expectedMessage) {
+        HSSFWorkbook book = new HSSFWorkbook();
+        try {
+            FormulaParser.parse(formula, book);
+            throw new AssertionFailedError("Didn't get parse exception as expected");
+        } catch (FormulaParseException e) {
+            assertEquals(expectedMessage, e.getMessage());
+        }
+    }
 }
index d2a1b368410cc834d320750f6b205593bc9a3b71..2b8e3c3ab557f2c6dd95a3ce4d63f778517c1348 100755 (executable)
@@ -67,6 +67,7 @@ public final class AllRecordTests {
                result.addTestSuite(TestFormulaRecord.class);
                result.addTestSuite(TestFrameRecord.class);
                result.addTestSuite(TestHyperlinkRecord.class);
+               result.addTestSuite(TestLabelRecord.class);
                result.addTestSuite(TestLegendRecord.class);
                result.addTestSuite(TestLineFormatRecord.class);
                result.addTestSuite(TestLinkedDataRecord.class);
diff --git a/src/testcases/org/apache/poi/hssf/record/TestLabelRecord.java b/src/testcases/org/apache/poi/hssf/record/TestLabelRecord.java
new file mode 100644 (file)
index 0000000..10f3636
--- /dev/null
@@ -0,0 +1,41 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hssf.record;
+
+import org.apache.poi.hssf.HSSFTestDataSamples;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+/**
+ * Tests for <tt>LabelRecord</tt>
+ * 
+ * @author Josh Micich
+ */
+public final class TestLabelRecord extends TestCase {
+
+       public void testEmptyString() {
+               HSSFWorkbook wb;
+               try {
+                       wb = HSSFTestDataSamples.openSampleWorkbook("ex42570-20305.xls");
+               } catch (NullPointerException e) {
+                       throw new AssertionFailedError("Identified bug 42570");
+               }
+               HSSFTestDataSamples.writeOutAndReadBack(wb);
+       }
+}
index 3a704502230367f8bc7a7fcaebd254415cf076cf..a685fd2cb690254059e8a3448b73a3d90ee9abaf 100644 (file)
@@ -40,9 +40,11 @@ public final class AllFormulaTests {
                result.addTestSuite(TestArea3DPtg.class);
                result.addTestSuite(TestAreaErrPtg.class);
                result.addTestSuite(TestAreaPtg.class);
+               result.addTestSuite(TestArrayPtg.class);
                result.addTestSuite(TestErrPtg.class);
                result.addTestSuite(TestExternalFunctionFormulas.class);
                result.addTestSuite(TestFuncPtg.class);
+               result.addTestSuite(TestFuncVarPtg.class);
                result.addTestSuite(TestIntersectionPtg.class);
                result.addTestSuite(TestPercentPtg.class);
                result.addTestSuite(TestRangePtg.class);
diff --git a/src/testcases/org/apache/poi/hssf/record/formula/TestArrayPtg.java b/src/testcases/org/apache/poi/hssf/record/formula/TestArrayPtg.java
new file mode 100644 (file)
index 0000000..16f80bb
--- /dev/null
@@ -0,0 +1,95 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hssf.record.formula;
+
+import java.util.Arrays;
+
+import org.apache.poi.hssf.record.TestcaseRecordInputStream;
+import org.apache.poi.hssf.record.UnicodeString;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+/**
+ * Tests for <tt>ArrayPtg</tt>
+ * 
+ * @author Josh Micich
+ */
+public final class TestArrayPtg extends TestCase {
+
+       private static final byte[] ENCODED_PTG_DATA = {
+               0x40, 0x00,
+               0x08, 0x00,
+               0, 0, 0, 0, 0, 0, 0, 0, 
+       };
+       private static final byte[] ENCODED_CONSTANT_DATA = {
+               2,    // 3 columns
+               1, 0, // 2 rows
+               4, 1, 0, 0, 0, 0, 0, 0, 0, // TRUE
+               2, 4, 0, 0, 65, 66, 67, 68, // "ABCD"
+               2, 1, 0, 0, 69, // "E"
+               1, 0, 0, 0, 0, 0, 0, 0, 0, // 0
+               4, 0, 0, 0, 0, 0, 0, 0, 0, // FALSE
+               2, 2, 0, 0, 70, 71, // "FG"
+       };
+
+       /**
+        * Lots of problems with ArrayPtg's encoding of 
+        */
+       public void testReadWriteTokenValueBytes() {
+               
+               ArrayPtg ptg = new ArrayPtgV(new TestcaseRecordInputStream(ArrayPtgV.sid, ENCODED_PTG_DATA));
+               
+               ptg.readTokenValues(new TestcaseRecordInputStream(0, ENCODED_CONSTANT_DATA));
+               assertEquals(3, ptg.getColumnCount());
+               assertEquals(2, ptg.getRowCount());
+               Object[] values = ptg.token_3_arrayValues;
+               assertEquals(6, values.length);
+               
+               
+               assertEquals(Boolean.TRUE, values[0]);
+               assertEquals(new UnicodeString("ABCD"), values[1]);
+               assertEquals(new Double(0), values[3]);
+               assertEquals(Boolean.FALSE, values[4]);
+               assertEquals(new UnicodeString("FG"), values[5]);
+               
+               byte[] outBuf = new byte[ENCODED_CONSTANT_DATA.length];
+               ptg.writeTokenValueBytes(outBuf, 0);
+               
+               if(outBuf[0] == 4) {
+                       throw new AssertionFailedError("Identified bug 42564b");
+               }
+               assertTrue(Arrays.equals(ENCODED_CONSTANT_DATA, outBuf));
+       }
+
+       /**
+        * make sure constant elements are stored row by row 
+        */
+       public void testElementOrdering() {
+               ArrayPtg ptg = new ArrayPtgV(new TestcaseRecordInputStream(ArrayPtgV.sid, ENCODED_PTG_DATA));
+               ptg.readTokenValues(new TestcaseRecordInputStream(0, ENCODED_CONSTANT_DATA));
+               assertEquals(3, ptg.getColumnCount());
+               assertEquals(2, ptg.getRowCount());
+               
+               assertEquals(0, ptg.getValueIndex(0, 0));
+               assertEquals(1, ptg.getValueIndex(1, 0));
+               assertEquals(2, ptg.getValueIndex(2, 0));
+               assertEquals(3, ptg.getValueIndex(0, 1));
+               assertEquals(4, ptg.getValueIndex(1, 1));
+               assertEquals(5, ptg.getValueIndex(2, 1));
+       }
+}
diff --git a/src/testcases/org/apache/poi/hssf/record/formula/TestFuncVarPtg.java b/src/testcases/org/apache/poi/hssf/record/formula/TestFuncVarPtg.java
new file mode 100644 (file)
index 0000000..58ae55f
--- /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.hssf.record.formula;
+
+import org.apache.poi.hssf.model.FormulaParser;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+/**
+ * @author Josh Micich
+ */
+public final class TestFuncVarPtg extends TestCase {
+
+       /**
+        * The first fix for bugzilla 44675 broke the encoding of SUM formulas (and probably others).
+        * The operand classes of the parameters to SUM() should be coerced to 'reference' not 'value'.
+        * In the case of SUM, Excel evaluates the formula to '#VALUE!' if a parameter operand class is
+        * wrong.  In other cases Excel seems to tolerate bad operand classes.</p>
+        * This functionality is related to the setParameterRVA() methods of <tt>FormulaParser</tt>
+        */
+       public void testOperandClass() {
+               HSSFWorkbook book = new HSSFWorkbook();
+               Ptg[] ptgs = FormulaParser.parse("sum(A1:A2)", book);
+               assertEquals(2, ptgs.length);
+               assertEquals(AreaPtg.class, ptgs[0].getClass());
+               
+               switch(ptgs[0].getPtgClass()) {
+                       case Ptg.CLASS_REF:
+                               // correct behaviour
+                               break;
+                       case Ptg.CLASS_VALUE:
+                               throw new AssertionFailedError("Identified bug 44675b");
+                       default:
+                               throw new RuntimeException("Unexpected operand class");
+               }
+       }
+}
index 226b14400d8c18eb4b417aacac6289d26276ea23..0bddecd3ff559ae51659a6198040578fa3a8bfca 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
 
 package org.apache.poi.hssf.record.formula;
 
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
+import org.apache.poi.hssf.HSSFTestDataSamples;
 import org.apache.poi.hssf.usermodel.HSSFSheet;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 
 /**
  * Tests for {@link ReferencePtg}.
  */
-public class TestReferencePtg extends AbstractPtgTestCase
-{
+public final class TestReferencePtg extends TestCase {
     /**
      * Tests reading a file containing this ptg.
      */
-    public void testReading() throws Exception
-    {
-        HSSFWorkbook workbook = loadWorkbook("ReferencePtg.xls");
+    public void testReading() {
+        HSSFWorkbook workbook = HSSFTestDataSamples.openSampleWorkbook("ReferencePtg.xls");
         HSSFSheet sheet = workbook.getSheetAt(0);
 
         // First row
@@ -72,6 +73,18 @@ public class TestReferencePtg extends AbstractPtgTestCase
         assertEquals("Wrong formula string for reference", "A32770",
                 sheet.getRow(32769).getCell((short) 1).getCellFormula());
     }
+    
+    public void testBug44921() {
+        HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("ex44921-21902.xls");
+        
+        try {
+            HSSFTestDataSamples.writeOutAndReadBack(wb);
+        } catch (RuntimeException e) {
+            if(e.getMessage().equals("Coding Error: This method should never be called. This ptg should be converted")) {
+                throw new AssertionFailedError("Identified bug 44921");
+            }
+            throw e;
+        }
+    }
 }
 
-
index 6d6b053a63d8f1e23f9c5d1d9abe4392344fd543..47c2c481ea9dd0d40a0ebad7128cd15a0008170a 100755 (executable)
@@ -14,7 +14,7 @@
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
-       
+
 package org.apache.poi.hssf.usermodel;
 
 import junit.framework.Test;
@@ -46,6 +46,7 @@ public class AllUserModelTests {
                result.addTestSuite(TestHSSFHeaderFooter.class);
                result.addTestSuite(TestHSSFHyperlink.class);
                result.addTestSuite(TestHSSFPalette.class);
+               result.addTestSuite(TestHSSFPatriarch.class);
                result.addTestSuite(TestHSSFPicture.class);
                result.addTestSuite(TestHSSFPictureData.class);
                result.addTestSuite(TestHSSFRichTextString.class);
index 259e3d0c86b65244a8aa55c5fc9ed0629e4d00af..2985278c549f6c019d5c68f2003c7c359ccd40ed 100644 (file)
@@ -733,7 +733,7 @@ public final class TestBugs extends TestCase {
      *  with the NameRecord, once you get past the BOFRecord
      *  issue.
      */
-    public void DISABLEDtest42564Alt() {
+    public void test42564Alt() {
         HSSFWorkbook wb = openSample("42564-2.xls");
         writeOutAndReadBack(wb);
     }
@@ -883,10 +883,32 @@ public final class TestBugs extends TestCase {
      * Bug 28774: Excel will crash when opening xls-files with images.
      */
     public void test28774() {
-
         HSSFWorkbook wb = openSample("28774.xls");
         assertTrue("no errors reading sample xls", true);
         writeOutAndReadBack(wb);
         assertTrue("no errors writing sample xls", true);
     }
+    
+    /**
+     * Had a problem apparently, not sure what as it
+     *  works just fine...
+     */
+    public void test44891() throws Exception {
+       HSSFWorkbook wb = openSample("44891.xls");
+        assertTrue("no errors reading sample xls", true);
+        writeOutAndReadBack(wb);
+        assertTrue("no errors writing sample xls", true);
+    }
+
+    /**
+     * Bug 44235: Ms Excel can't open save as excel file
+     *
+     * Works fine with poi-3.1-beta1.
+     */
+    public void test44235() throws Exception {
+       HSSFWorkbook wb = openSample("44235.xls");
+        assertTrue("no errors reading sample xls", true);
+        writeOutAndReadBack(wb);
+        assertTrue("no errors writing sample xls", true);
+    }
 }
index 19069d32b25f645d7ad00ed7772a46c116b44d5d..0ef642917349eab49a6891ea9746dec298bb139d 100644 (file)
@@ -19,6 +19,7 @@ package org.apache.poi.hssf.usermodel;
 
 import java.io.File;
 import java.io.FileOutputStream;
+import java.util.Iterator;
 import java.util.List;
 
 import junit.framework.TestCase;
@@ -252,4 +253,29 @@ public final class TestFormulaEvaluatorBugs extends TestCase {
                }
                assertEquals(true, cell.getBooleanCellValue());
        }
-}
+       
+       public void testClassCast_bug44861() throws Exception {
+               HSSFWorkbook wb = HSSFTestDataSamples.
+                       openSampleWorkbook("44861.xls");
+               
+               // Check direct
+               HSSFFormulaEvaluator.evaluateAllFormulaCells(wb);
+               
+               // And via calls
+               int numSheets = wb.getNumberOfSheets();
+               for(int i=0; i<numSheets; i++) {
+                       HSSFSheet s = wb.getSheetAt(i);
+                       HSSFFormulaEvaluator eval = new HSSFFormulaEvaluator(s,wb);
+                       
+                       for(Iterator rows = s.rowIterator(); rows.hasNext();) {
+                       HSSFRow r = (HSSFRow)rows.next();
+                       eval.setCurrentRow(r);
+                       
+                       for(Iterator cells = r.cellIterator(); cells.hasNext();) {
+                               HSSFCell c = (HSSFCell)cells.next();
+                               eval.evaluateFormulaCell(c);
+                       }
+                       }
+               }
+       }
+}
\ No newline at end of file
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFPatriarch.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFPatriarch.java
new file mode 100644 (file)
index 0000000..f0d00dc
--- /dev/null
@@ -0,0 +1,71 @@
+/* ====================================================================\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.usermodel;\r
+\r
+import org.apache.poi.hssf.HSSFTestDataSamples;\r
+\r
+import junit.framework.AssertionFailedError;\r
+import junit.framework.TestCase;\r
+\r
+/**\r
+ * @author Josh Micich\r
+ */\r
+public final class TestHSSFPatriarch extends TestCase {\r
+\r
+       public void testBasic() {\r
+\r
+               HSSFWorkbook wb = new HSSFWorkbook();\r
+               HSSFSheet sheet = wb.createSheet();\r
+\r
+               HSSFPatriarch patr = sheet.createDrawingPatriarch();\r
+\r
+               assertNotNull(patr);\r
+\r
+               // assert something more interesting\r
+       }\r
+\r
+       // TODO - fix bug 44916 (1-May-2008)\r
+       public void DISABLED_test44916() {\r
+\r
+               HSSFWorkbook wb = new HSSFWorkbook();\r
+               HSSFSheet sheet = wb.createSheet();\r
+\r
+               // 1. Create drawing patriarch\r
+               HSSFPatriarch patr = sheet.createDrawingPatriarch();\r
+\r
+               // 2. Try to re-get the patriarch\r
+               HSSFPatriarch existingPatr;\r
+               try {\r
+                       existingPatr = sheet.getDrawingPatriarch();\r
+               } catch (NullPointerException e) {\r
+                       throw new AssertionFailedError("Identified bug 44916");\r
+               }\r
+\r
+               // 3. Use patriarch\r
+               HSSFClientAnchor anchor = new HSSFClientAnchor(0, 0, 600, 245, (short) 1, 1, (short) 1, 2);\r
+               anchor.setAnchorType(3);\r
+               byte[] pictureData = HSSFTestDataSamples.getTestDataFileContent("logoKarmokar4.png");\r
+               int idx1 = wb.addPicture(pictureData, HSSFWorkbook.PICTURE_TYPE_PNG);\r
+               patr.createPicture(anchor, idx1);\r
+\r
+               // 4. Try to re-use patriarch later\r
+               existingPatr = sheet.getDrawingPatriarch();\r
+               assertNotNull(existingPatr);\r
+       }\r
+\r
+}\r
index 539f64f36f1af4c3a92113ae331be6124562ed01..87578ae01bb628be0e9ccc0c4ba33fc10bac9970 100644 (file)
 */\r
 package org.apache.poi.hssf.usermodel;\r
 \r
-import java.io.ByteArrayOutputStream;\r
-import java.io.IOException;\r
-import java.io.InputStream;\r
-\r
 import junit.framework.TestCase;\r
 \r
 import org.apache.poi.hssf.HSSFTestDataSamples;\r
@@ -36,7 +32,7 @@ public final class TestHSSFPicture extends TestCase{
         HSSFSheet sh1 = wb.createSheet();\r
         HSSFPatriarch p1 = sh1.createDrawingPatriarch();\r
 \r
-        byte[] pictureData = getTestDataFileContent("logoKarmokar4.png");\r
+        byte[] pictureData = HSSFTestDataSamples.getTestDataFileContent("logoKarmokar4.png");\r
         int idx1 = wb.addPicture( pictureData, HSSFWorkbook.PICTURE_TYPE_PNG );\r
         HSSFPicture picture1 = p1.createPicture(new HSSFClientAnchor(), idx1);\r
         HSSFClientAnchor anchor1 = picture1.getPreferredSize();\r
@@ -51,28 +47,4 @@ public final class TestHSSFPicture extends TestCase{
         assertEquals(848, anchor1.getDx2());\r
         assertEquals(240, anchor1.getDy2());\r
     }\r
-\r
-    /**\r
-     * Copied from org.apache.poi.hssf.usermodel.examples.OfficeDrawing\r
-     */\r
-     private static byte[] getTestDataFileContent(String fileName) {\r
-        ByteArrayOutputStream bos = new ByteArrayOutputStream();\r
-\r
-        try {\r
-            InputStream fis = HSSFTestDataSamples.openSampleFileStream(fileName);\r
-\r
-            byte[] buf = new byte[512];\r
-            while(true) {\r
-                int bytesRead = fis.read(buf);\r
-                if(bytesRead < 1) {\r
-                    break;\r
-                }\r
-                bos.write(buf, 0, bytesRead);\r
-            }\r
-            fis.close();\r
-        } catch (IOException e) {\r
-            throw new RuntimeException(e);\r
-        }\r
-        return bos.toByteArray();\r
-     }\r
 }\r
index 3696bb940ec433fe7d09ce9447e18b24f1d7fffa..4c156e7b6f5e482a90088bd53358bf2fc2c10e64 100644 (file)
@@ -17,6 +17,7 @@
 
 package org.apache.poi.hssf.usermodel;
 
+import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;
 
 import org.apache.poi.hssf.HSSFTestDataSamples;
@@ -39,7 +40,24 @@ public final class TestHSSFWorkbook extends TestCase {
         NameRecord nameRecord = b.getWorkbook().getNameRecord( 0 );
         assertEquals( 3, nameRecord.getIndexToSheet() );
     }
-    
+
+    public void testCaseInsensitiveNames() {
+        HSSFWorkbook b = new HSSFWorkbook( );
+        HSSFSheet originalSheet = b.createSheet("Sheet1");
+        HSSFSheet fetchedSheet = b.getSheet("sheet1");
+        if(fetchedSheet == null) {
+            throw new AssertionFailedError("Identified bug 44892");
+        }
+        assertEquals(originalSheet, fetchedSheet);
+        try {
+            b.createSheet("sHeeT1");
+            fail("should have thrown exceptiuon due to duplicate sheet name");
+        } catch (IllegalArgumentException e) {
+            // expected during successful test
+            assertEquals("The workbook already contains a sheet of this name", e.getMessage());
+        }
+    }
+
     public void testDuplicateNames() {
         HSSFWorkbook b = new HSSFWorkbook( );
         b.createSheet("Sheet1");