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

........
  r671322 | nick | 2008-06-24 20:53:53 +0100 (Tue, 24 Jun 2008) | 1 line

  Make a start on being able to process formulas in the eventusermodel code
........
  r672230 | nick | 2008-06-27 11:12:11 +0100 (Fri, 27 Jun 2008) | 1 line

  Add MethodNotFound exceptions to the faq
........
  r672550 | nick | 2008-06-28 18:04:09 +0100 (Sat, 28 Jun 2008) | 1 line

  Finish the EventWorkbookBuilder, now does sheet references in formulas properly
........
  r672553 | nick | 2008-06-28 18:12:38 +0100 (Sat, 28 Jun 2008) | 1 line

  Update changelog about EventWorkbookBuilder, and tweak XLS2CSVmra to use it if formulas required
........
  r672562 | nick | 2008-06-28 19:21:21 +0100 (Sat, 28 Jun 2008) | 1 line

  Avoid spurious missing lines with the MissingRecordAware event code, and odd files that contain RowRecords in the middle of the cell Records.
........
  r672567 | nick | 2008-06-28 19:48:35 +0100 (Sat, 28 Jun 2008) | 1 line

  Patch from dnapoletano from bug #45175 - Support for variable length operands in org.apache.poi.hwpf.sprm.SprmOperation
........
  r672569 | nick | 2008-06-28 19:54:02 +0100 (Sat, 28 Jun 2008) | 1 line

  Patch from N. Hira from bug #45001 - Further fix for HWPF Range.delete() and unicode characters
........
  r672570 | nick | 2008-06-28 19:58:23 +0100 (Sat, 28 Jun 2008) | 1 line

  Patch from N.Hira from bug #45252 - Improvement for HWPF Range.replaceText()
........
  r673050 | yegor | 2008-07-01 11:32:29 +0100 (Tue, 01 Jul 2008) | 1 line

  updated status of the latest release, 3.1-FINAL
........
  r673853 | josh | 2008-07-03 23:20:18 +0100 (Thu, 03 Jul 2008) | 1 line

  Fix for bug 45334 - formula parser needs to handle dots in identifiers
........
  r673863 | josh | 2008-07-04 00:09:08 +0100 (Fri, 04 Jul 2008) | 1 line

  Fix for bug 45334 - added impl for ERROR.TYPE()
........
  r673987 | nick | 2008-07-04 10:58:21 +0100 (Fri, 04 Jul 2008) | 1 line

  Fix bug #45336 - Fix HSSFColor.getTripletHash()
........
  r673997 | nick | 2008-07-04 11:46:59 +0100 (Fri, 04 Jul 2008) | 1 line

  Fix bug #45338 - Fix HSSFWorkbook to give you the same HSSFFont every time, and then fix it to find newly added fonts
........

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

25 files changed:
src/documentation/content/xdocs/changes.xml
src/documentation/content/xdocs/faq.xml
src/documentation/content/xdocs/index.xml
src/documentation/content/xdocs/status.xml
src/examples/src/org/apache/poi/hssf/eventusermodel/examples/XLS2CSVmra.java
src/java/org/apache/poi/hssf/eventusermodel/EventWorkbookBuilder.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/eventusermodel/MissingRecordAwareHSSFListener.java
src/java/org/apache/poi/hssf/model/FormulaParser.java
src/java/org/apache/poi/hssf/record/formula/functions/Errortype.java
src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
src/java/org/apache/poi/hssf/util/HSSFColor.java
src/scratchpad/src/org/apache/poi/hwpf/model/TextPiece.java
src/scratchpad/src/org/apache/poi/hwpf/sprm/SprmOperation.java
src/scratchpad/src/org/apache/poi/hwpf/usermodel/Range.java
src/scratchpad/testcases/org/apache/poi/hwpf/data/testRangeDelete.doc [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestRangeDelete.java [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestRangeReplacement.java
src/testcases/org/apache/poi/hssf/data/3dFormulas.xls [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls
src/testcases/org/apache/poi/hssf/data/MRExtraLines.xls [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/eventusermodel/TestEventWorkbookBuilder.java [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/eventusermodel/TestMissingRecordAwareHSSFListener.java
src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java
src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java
src/testcases/org/apache/poi/hssf/util/TestHSSFColor.java [new file with mode: 0644]

index 561ab0d4d290220559c725f8a035d3067272a668..4ed05d00f1acfa0d6477d4414fe49e4d7a20d71b 100644 (file)
@@ -36,7 +36,7 @@
     </devs>
 
                <!-- Don't forget to update status.xml too! -->
-        <release version="3.5.1-alpha1" date="2008-04-??">
+        <release version="3.5.1-beta1" date="2008-07-11">
            <action dev="POI-DEVELOPERS" type="add">45018 - Support for fetching embeded documents from within an OOXML file</action>
            <action dev="POI-DEVELOPERS" type="add">Port support for setting a policy on missing / blank cells when fetching, to XSSF too</action>
            <action dev="POI-DEVELOPERS" type="add">Common text extraction factory, which returns the correct POITextExtractor for the supplied data</action>
            <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.2-alpha1" date="2008-??-??">
-          <action dev="POI-DEVELOPERS" type="add"><!-- to keep forrest dtd quiet--></action>
+        <release version="3.1.1-alpha1" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="fix">45338 - Fix HSSFWorkbook to give you the same HSSFFont every time, and then fix it to find newly added fonts</action>
+           <action dev="POI-DEVELOPERS" type="fix">45336 - Fix HSSFColor.getTripletHash()</action>
+           <action dev="POI-DEVELOPERS" type="fix">45334 - Fixed formula parser to handle dots in identifiers</action>
+           <action dev="POI-DEVELOPERS" type="fix">45252 - Improvement for HWPF Range.replaceText()</action>
+           <action dev="POI-DEVELOPERS" type="fix">45001 - Further fix for HWPF Range.delete() and unicode characters</action>
+           <action dev="POI-DEVELOPERS" type="add">45175 - Support for variable length operands in org.apache.poi.hwpf.sprm.SprmOperation</action>
+           <action dev="POI-DEVELOPERS" type="fix">Avoid spurious missing lines with the MissingRecordAware event code, and odd files that contain RowRecords in the middle of the cell Records.</action>
+           <action dev="POI-DEVELOPERS" type="add">Support for parsing formulas during EventUserModel processing, via the new EventWorkbookBuilder</action>
         </release>
         <release version="3.1-final" date="2008-06-29">
            <action dev="POI-DEVELOPERS" type="fix">30978 - Fixed re-serialization of tRefErr3d and tAreaErr3d</action>
index e74b6f0901fede7219b9207c7e86818e17debfaf..22c1a4cb4da22df065c6d7571cf5b38635d37cd0 100644 (file)
 <!DOCTYPE faqs PUBLIC "-//APACHE//DTD FAQ V1.1//EN" "./dtd/faq-v11.dtd">
 
 <faqs title="Frequently Asked Questions">
+    <faq>
+        <question>
+          My code uses some new HSSF feature, compiles fine but fails when live with a "MethodNotFoundException"
+        </question>
+               <answer>
+                <p>You almost certainly have an older version of POI earlier 
+                on your classpath. Quite a few runtimes and other packages 
+                will ship an older version of POI, so this is an easy problem
+                to hit without realising.</p>
+                <p>The best way to identify the offending earlier jar file is
+                with a few lines of java. These will load one of the core POI
+                classes, and report where it came from.</p>
+                <source>
+ClassLoader classloader = org.apache.poi.poifs.filesystem.POIFSFileSystem.class.getClassLoader();
+URL res = classloader.getResource("org/apache/poi/poifs/filesystem/POIFSFileSystem.class">
+String path = res.getPath();
+System.out.println("Core POI came from " + path);
+                </source>
+               </answer>
+       </faq>
+    <faq>
+        <question>
+          My code uses the scratchpad, compiles fine but fails to run with a "MethodNotFoundException"
+        </question>
+               <answer>
+                <p>You almost certainly have an older version earlier on your
+                classpath. See the answer to the similar question above for
+                how to track this down.</p>
+               </answer>
+       </faq>
     <faq>
         <question>
           Why is reading a simple sheet taking so long?
index b881cc0a2a1c9218cbafaaea35c2a23069c56cd9..b632fb93ffa0af64cd4b601f9a9b9ee48af637a8 100644 (file)
   </header>
 
   <body>
-    <section><title>Office Open XML Support</title>
+    <section><title>POI 3.5.1 beta 1, and Office Open XML Support (2008-07-11)</title>
       <p>We are currently working to support the new Office Open XML
        file formats, such as XLSX and PPTX, which were introduced in
        Office 2007.</p>
-      <p>Support for these is currently only available in an svn branch,
-       but we hope to have a full release including it by the summer.
-       People interested should follow the
+      <p>Development for this is in a svn branch, but we are please to
+       announce our first preview release containing this support.
+       Users interested in the OOXML support should download the
+       <link href="http://www.apache.org/dyn/closer.cgi/poi/dev/">POI 3.5.1 beta 1</link>
+       the source and binaries from their
+       <link href="http://www.apache.org/dyn/closer.cgi/poi/dev/">local mirror</link>.
+       People interested should also follow the
        <link href="mailinglists.html">dev list</link> to track progress.</p>
     </section>
-    <section><title>POI 3.1-BETA2 Released (2008-05-28)</title>
+    <section><title>POI 3.1-FINAL Released (2008-06-29)</title>
       <p>
-        The POI team is pleased to announce the release of 3.1 BETA2 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-beta2".
-      </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 
+        The POI team is pleased to announce the release of 3.1 FINAL, the latest release of Apache POI.
+        There have been many important bug fixes since the 3.0.2 release and a lot of new features. 
+       </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/release/">download</link>
                the source and binaries from your
       </p>
       <p>
         The release is also available from the central Maven repository 
-        under Group ID "org.apache.poi" and Version "3.0.2-FINAL".
+        under Group ID "org.apache.poi" and Version "3.1-FINAL".
       </p>  
-      <p>We would also like to confirm that verions 3.0 and 3.0.1 of Apache 
-               POI do
-               <em>not</em> contain any viruses. Users of broken virus checkers
-               which do detect a 94 byte file, sci_cec.db, as containing one are
-               advised to contact their vendor for a fix.</p>
     </section>
 
     <section><title>Purpose</title>
index ca47e0f20975674d7991ecbbe5b4b9861f346c05..f96dfc33abf7836a6c9ced0f91e0da6708817ee3 100644 (file)
@@ -33,7 +33,7 @@
 
        <!-- Don't forget to update changes.xml too! -->
     <changes>
-        <release version="3.5.1-alpha1" date="2008-04-??">
+        <release version="3.5.1-beta1" date="2008-07-11">
            <action dev="POI-DEVELOPERS" type="add">45018 - Support for fetching embeded documents from within an OOXML file</action>
            <action dev="POI-DEVELOPERS" type="add">Port support for setting a policy on missing / blank cells when fetching, to XSSF too</action>
            <action dev="POI-DEVELOPERS" type="add">Common text extraction factory, which returns the correct POITextExtractor for the supplied data</action>
            <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.2-alpha1" date="2008-??-??">
-          <action dev="POI-DEVELOPERS" type="add"><!-- to keep forrest dtd quiet--></action>
+        <release version="3.1.1-alpha1" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="fix">45338 - Fix HSSFWorkbook to give you the same HSSFFont every time, and then fix it to find newly added fonts</action>
+           <action dev="POI-DEVELOPERS" type="fix">45336 - Fix HSSFColor.getTripletHash()</action>
+           <action dev="POI-DEVELOPERS" type="fix">45334 - Fixed formula parser to handle dots in identifiers</action>
+           <action dev="POI-DEVELOPERS" type="fix">45252 - Improvement for HWPF Range.replaceText()</action>
+           <action dev="POI-DEVELOPERS" type="fix">45001 - Further fix for HWPF Range.delete() and unicode characters</action>
+           <action dev="POI-DEVELOPERS" type="add">45175 - Support for variable length operands in org.apache.poi.hwpf.sprm.SprmOperation</action>
+           <action dev="POI-DEVELOPERS" type="fix">Avoid spurious missing lines with the MissingRecordAware event code, and odd files that contain RowRecords in the middle of the cell Records.</action>
+           <action dev="POI-DEVELOPERS" type="add">Support for parsing formulas during EventUserModel processing, via the new EventWorkbookBuilder</action>
         </release>
         <release version="3.1-final" date="2008-06-29">
            <action dev="POI-DEVELOPERS" type="fix">30978 - Fixed re-serialization of tRefErr3d and tAreaErr3d</action>
index 9bebd3a837b71209f9735a9159b3208a89d992b1..1c9b220356b736dc7d025ec1c6f76ec617110fb0 100644 (file)
@@ -30,9 +30,11 @@ import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
 import org.apache.poi.hssf.eventusermodel.HSSFListener;
 import org.apache.poi.hssf.eventusermodel.HSSFRequest;
 import org.apache.poi.hssf.eventusermodel.MissingRecordAwareHSSFListener;
+import org.apache.poi.hssf.eventusermodel.EventWorkbookBuilder.SheetRecordCollectingListener;
 import org.apache.poi.hssf.eventusermodel.dummyrecord.LastCellOfRowDummyRecord;
 import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingCellDummyRecord;
 import org.apache.poi.hssf.model.FormulaParser;
+import org.apache.poi.hssf.record.BOFRecord;
 import org.apache.poi.hssf.record.BlankRecord;
 import org.apache.poi.hssf.record.BoolErrRecord;
 import org.apache.poi.hssf.record.CellValueRecordInterface;
@@ -46,6 +48,7 @@ import org.apache.poi.hssf.record.Record;
 import org.apache.poi.hssf.record.SSTRecord;
 import org.apache.poi.hssf.record.StringRecord;
 import org.apache.poi.hssf.usermodel.HSSFDateUtil;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 
 /**
@@ -64,6 +67,10 @@ public class XLS2CSVmra implements HSSFListener {
        /** Should we output the formula, or the value it has? */
        private boolean outputFormulaValues = true;
        
+       /** For parsing Formulas */
+       private SheetRecordCollectingListener workbookBuildingListener;
+       private HSSFWorkbook stubWorkbook;
+       
        // Records we pick up as we process
        private SSTRecord sstRecord;
        private FormatTrackingHSSFListener formatListener;
@@ -108,7 +115,13 @@ public class XLS2CSVmra implements HSSFListener {
                
                HSSFEventFactory factory = new HSSFEventFactory();
                HSSFRequest request = new HSSFRequest();
-               request.addListenerForAllRecords(formatListener);
+               
+               if(outputFormulaValues) {
+                       request.addListenerForAllRecords(formatListener);
+               } else {
+                       workbookBuildingListener = new SheetRecordCollectingListener(formatListener);
+                       request.addListenerForAllRecords(workbookBuildingListener);
+               }
                
                factory.processWorkbookEvents(request, fs);
        }
@@ -124,6 +137,16 @@ public class XLS2CSVmra implements HSSFListener {
                
                switch (record.getSid())
         {
+               case BOFRecord.sid:
+                       BOFRecord br = (BOFRecord)record;
+                       if(br.getType() == BOFRecord.TYPE_WORKSHEET) {
+                               // Create sub workbook if required
+                               if(workbookBuildingListener != null && stubWorkbook == null) {
+                                       stubWorkbook = workbookBuildingListener.getStubHSSFWorkbook();
+                               }
+                       }
+                       break;
+               
                case SSTRecord.sid:
                        sstRecord = (SSTRecord) record;
                        break;
@@ -161,7 +184,7 @@ public class XLS2CSVmra implements HSSFListener {
                        }
                } else {
                        thisStr = '"' + 
-                               FormulaParser.toFormulaString(null, frec.getParsedExpression()) + '"';
+                               FormulaParser.toFormulaString(stubWorkbook, frec.getParsedExpression()) + '"';
                }
             break;
         case StringRecord.sid:
diff --git a/src/java/org/apache/poi/hssf/eventusermodel/EventWorkbookBuilder.java b/src/java/org/apache/poi/hssf/eventusermodel/EventWorkbookBuilder.java
new file mode 100644 (file)
index 0000000..0ae5f3f
--- /dev/null
@@ -0,0 +1,199 @@
+/* ====================================================================
+   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.eventusermodel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.poi.hssf.model.FormulaParser;
+import org.apache.poi.hssf.model.Workbook;
+import org.apache.poi.hssf.record.BoundSheetRecord;
+import org.apache.poi.hssf.record.EOFRecord;
+import org.apache.poi.hssf.record.ExternSheetRecord;
+import org.apache.poi.hssf.record.Record;
+import org.apache.poi.hssf.record.SSTRecord;
+import org.apache.poi.hssf.record.SupBookRecord;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+
+/**
+ * When working with the EventUserModel, if you want to 
+ *  process formulas, you need an instance of
+ *  {@link Workbook} to pass to a {@link HSSFWorkbook},
+ *  to finally give to {@link FormulaParser}, 
+ *  and this will build you stub ones.
+ * Since you're working with the EventUserModel, you
+ *  wouldn't want to get a full {@link Workbook} and
+ *  {@link HSSFWorkbook}, as they would eat too much memory.
+ *  Instead, you should collect a few key records as they
+ *  go past, then call this once you have them to build a
+ *  stub {@link Workbook}, and from that a stub
+ *  {@link HSSFWorkbook}, to use with the {@link FormulaParser}.
+ * 
+ * The records you should collect are:
+ *  * {@link ExternSheetRecord}
+ *  * {@link BoundSheetRecord}
+ * You should probably also collect {@link SSTRecord}, 
+ *  but it's not required to pass this in.
+ *  
+ * To help, this class includes a HSSFListener wrapper
+ *  that will do the collecting for you.
+ */
+public class EventWorkbookBuilder {
+       /**
+        * Wraps up your stub {@link Workbook} as a stub
+        *  {@link HSSFWorkbook}, ready for passing to
+        *  {@link FormulaParser}
+        * @param workbook A stub {@link Workbook}
+        */
+       public static HSSFWorkbook createStubHSSFWorkbook(Workbook workbook) {
+               return new StubHSSFWorkbook(workbook);
+       }
+       
+       /**
+        * Creates a stub Workbook from the supplied records,
+        *  suitable for use with the {@link FormulaParser}
+        * @param externs The ExternSheetRecords in your file
+        * @param bounds The BoundSheetRecords in your file
+        * @param sst The SSTRecord in your file.
+        * @return A stub Workbook suitable for use with {@link FormulaParser}
+        */
+       public static Workbook createStubWorkbook(ExternSheetRecord[] externs,
+                       BoundSheetRecord[] bounds, SSTRecord sst) {
+               List wbRecords = new ArrayList();
+               
+               // Core Workbook records go first
+               if(bounds != null) {
+                       for(int i=0; i<bounds.length; i++) {
+                               wbRecords.add(bounds[i]);
+                       }
+               }
+               if(sst != null) {
+                       wbRecords.add(sst);
+               }
+               
+               // Now we can have the ExternSheetRecords,
+               //  preceded by a SupBookRecord
+               if(externs != null) {
+                       wbRecords.add(SupBookRecord.createInternalReferences(
+                                       (short)externs.length));
+                       for(int i=0; i<externs.length; i++) {
+                               wbRecords.add(externs[i]);
+                       }
+               }
+               
+               // Finally we need an EoF record
+               wbRecords.add(new EOFRecord());
+               
+               return Workbook.createWorkbook(wbRecords);
+       }
+       
+       /**
+        * Creates a stub workbook from the supplied records,
+        *  suitable for use with the {@link FormulaParser}
+        * @param externs The ExternSheetRecords in your file
+        * @param bounds The BoundSheetRecords in your file
+        * @return A stub Workbook suitable for use with {@link FormulaParser}
+        */
+       public static Workbook createStubWorkbook(ExternSheetRecord[] externs,
+                       BoundSheetRecord[] bounds) {
+               return createStubWorkbook(externs, bounds, null);
+       }
+       
+       
+       /**
+        * A wrapping HSSFListener which will collect 
+        *  {@link BoundSheetRecord}s and {@link ExternSheetRecord}s as
+        *  they go past, so you can create a Stub {@link Workbook} from
+        *  them once required.
+        */
+       public static class SheetRecordCollectingListener implements HSSFListener {
+               private HSSFListener childListener;
+               private List boundSheetRecords = new ArrayList();
+               private List externSheetRecords = new ArrayList();
+               private SSTRecord sstRecord = null;
+               
+               public SheetRecordCollectingListener(HSSFListener childListener) {
+                       this.childListener = childListener;
+               }
+               
+               
+               public BoundSheetRecord[] getBoundSheetRecords() {
+                       return (BoundSheetRecord[])boundSheetRecords.toArray(
+                                       new BoundSheetRecord[boundSheetRecords.size()]
+                       );
+               }
+               public ExternSheetRecord[] getExternSheetRecords() {
+                       return (ExternSheetRecord[])externSheetRecords.toArray(
+                                       new ExternSheetRecord[externSheetRecords.size()]
+                       );
+               }
+               public SSTRecord getSSTRecord() {
+                       return sstRecord;
+               }
+               
+               public HSSFWorkbook getStubHSSFWorkbook() {
+                       return createStubHSSFWorkbook(
+                                       getStubWorkbook()
+                       );
+               }
+               public Workbook getStubWorkbook() {
+                       return createStubWorkbook(
+                                       getExternSheetRecords(), getBoundSheetRecords(), 
+                                       getSSTRecord()
+                       );
+               }
+               
+               
+               /**
+                * Process this record ourselves, and then
+                *  pass it on to our child listener
+                */
+               public void processRecord(Record record) {
+                       // Handle it ourselves
+                       processRecordInternally(record);
+                       
+                       // Now pass on to our child
+                       childListener.processRecord(record);
+               }
+               
+               /**
+                * Process the record ourselves, but do not
+                *  pass it on to the child Listener.
+                */
+               public void processRecordInternally(Record record) {
+                       if(record instanceof BoundSheetRecord) {
+                               boundSheetRecords.add(record);
+                       }
+                       else if(record instanceof ExternSheetRecord) {
+                               externSheetRecords.add(record);
+                       }
+                       else if(record instanceof SSTRecord) {
+                               sstRecord = (SSTRecord)record;
+                       }
+               }
+       }
+       
+       /**
+        * Let us at the {@link Workbook} constructor on
+        *  {@link HSSFWorkbook}
+        */
+       private static class StubHSSFWorkbook extends HSSFWorkbook {
+               private StubHSSFWorkbook(Workbook wb) {
+                       super(wb);
+               }
+       }
+}
\ No newline at end of file
index a727d064b9df91d934cfa3f431f88c93a3375eee..0bdcb1d3d9ddf0e26bd2dfe6eedb9ffed1774cd8 100644 (file)
@@ -46,8 +46,14 @@ import org.apache.poi.hssf.record.RowRecord;
  */
 public class MissingRecordAwareHSSFListener implements HSSFListener {
        private HSSFListener childListener;
-       private int lastSeenRow = -1;
-       private int lastSeenColumn = -1;
+       
+       // Need to have different counters for cell rows and
+       //  row rows, as you sometimes get a RowRecord in the
+       //  middle of some cells, and that'd break everything
+       private int lastRowRow = -1;
+       
+       private int lastCellRow = -1;
+       private int lastCellColumn = -1;
        
        /**
         * Constructs a new MissingRecordAwareHSSFListener, which
@@ -71,14 +77,16 @@ public class MissingRecordAwareHSSFListener implements HSSFListener {
                 if (bof.getType() == bof.TYPE_WORKBOOK)
                 {
                        // Reset the row and column counts - new workbook
-                       lastSeenRow = -1;
-                       lastSeenColumn = -1;
+                       lastRowRow = -1;
+                       lastCellRow = -1;
+                       lastCellColumn = -1;
                     //System.out.println("Encountered workbook");
                 } else if (bof.getType() == bof.TYPE_WORKSHEET)
                 {
                        // Reset the row and column counts - new sheet
-                       lastSeenRow = -1;
-                       lastSeenColumn = -1;
+                       lastRowRow = -1;
+                       lastCellRow = -1;
+                       lastCellColumn = -1;
                     //System.out.println("Encountered sheet reference");
                 }
                 break;
@@ -92,15 +100,15 @@ public class MissingRecordAwareHSSFListener implements HSSFListener {
                 //        + rowrec.getFirstCol() + " last column at " + rowrec.getLastCol());
                 
                 // If there's a jump in rows, fire off missing row records
-                if(lastSeenRow+1 < rowrec.getRowNumber()) {
-                       for(int i=(lastSeenRow+1); i<rowrec.getRowNumber(); i++) {
+                if(lastRowRow+1 < rowrec.getRowNumber()) {
+                       for(int i=(lastRowRow+1); i<rowrec.getRowNumber(); i++) {
                                MissingRowDummyRecord dr = new MissingRowDummyRecord(i);
                                childListener.processRecord(dr);
                        }
                 }
                 
                 // Record this as the last row we saw
-                lastSeenRow = rowrec.getRowNumber();
+                lastRowRow = rowrec.getRowNumber();
                 break;
                 
                 
@@ -157,45 +165,49 @@ public class MissingRecordAwareHSSFListener implements HSSFListener {
                break;
         }
                
-               // Do we need to fire dummy end-of-row records?
-               if(thisRow != lastSeenRow) {
-                       for(int i=lastSeenRow; i<thisRow; i++) {
+               // If we're on cells, and this cell isn't in the same
+               //  row as the last one, then fire the 
+               //  dummy end-of-row records?
+               if(thisRow != lastCellRow && lastCellRow > -1) {
+                       for(int i=lastCellRow; i<thisRow; i++) {
                                int cols = -1;
-                               if(i == lastSeenRow) {
-                                       cols = lastSeenColumn;
+                               if(i == lastCellRow) {
+                                       cols = lastCellColumn;
                                }
                                LastCellOfRowDummyRecord r = new LastCellOfRowDummyRecord(i, cols);
                                childListener.processRecord(r);
                        }
                }
-               // If we've finished with the columns, then fire the final
-               //  dummy end-of-row record
-               if(lastSeenRow != -1 && lastSeenColumn != -1 && thisRow == -1) {
-                       LastCellOfRowDummyRecord r = new LastCellOfRowDummyRecord(lastSeenRow, lastSeenColumn);
+               
+               // If we've just finished with the cells, then fire the
+               //  final dummy end-of-row record
+               if(lastCellRow != -1 && lastCellColumn != -1 && thisRow == -1) {
+                       LastCellOfRowDummyRecord r = new LastCellOfRowDummyRecord(lastCellRow, lastCellColumn);
                        childListener.processRecord(r);
                        
-                       lastSeenRow = -1;
-                       lastSeenColumn = -1;
+                       lastCellRow = -1;
+                       lastCellColumn = -1;
                }
                
                // If we've moved onto a new row, the ensure we re-set
                //  the column counter
-               if(thisRow != lastSeenRow) {
-                       lastSeenColumn = -1;
+               if(thisRow != lastCellRow) {
+                       lastCellColumn = -1;
                }
                
-               // Do we need to fire dummy cell records?
-               if(lastSeenColumn != (thisColumn-1)) {
-                       for(int i=lastSeenColumn+1; i<thisColumn; i++) {
+               // If there's a gap in the cells, then fire
+               //  the dummy cell records?
+               if(lastCellColumn != (thisColumn-1)) {
+                       for(int i=lastCellColumn+1; i<thisColumn; i++) {
                                MissingCellDummyRecord r = new MissingCellDummyRecord(thisRow, i);
                                childListener.processRecord(r);
                        }
                }
                
-               // Update cell and row counts if doing cells
+               // Update cell and row counts as needed
                if(thisColumn != -1) {
-                       lastSeenRow = thisRow;
-                       lastSeenColumn = thisColumn;
+                       lastCellColumn = thisColumn;
+                       lastCellRow = thisRow;
                }
                
                childListener.processRecord(record);
index 501b030ecc7810484591f618e8d1ba4c08ee5f8e..2cc2a805311f2ce91cbee9dbdcdca23389889d49 100644 (file)
@@ -22,11 +22,13 @@ import java.util.List;
 import java.util.Stack;
 import java.util.regex.Pattern;
 
-//import PTG's .. since we need everything, import *
+//import PTGs .. since we need everything, import *
 import org.apache.poi.hssf.record.formula.*;
 import org.apache.poi.hssf.record.formula.function.FunctionMetadata;
 import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
 import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.hssf.util.AreaReference;
+import org.apache.poi.hssf.util.CellReference;
 
 /**
  * This class parses a formula string into a List of tokens in RPN order.
@@ -178,8 +180,14 @@ public final class FormulaParser {
         GetChar();
     }
 
-    /** Get an Identifier */
-    private String GetName() {
+    /**
+     * Parses a sheet name, named range name, or simple cell reference.<br/>
+     * Note - identifiers in Excel can contain dots, so this method may return a String
+     * which may need to be converted to an area reference.  For example, this method 
+     * may return a value like "A1..B2", in which case the caller must convert it to 
+     * an area reference like "A1:B2"
+     */
+    private String parseIdentifier() {
         StringBuffer Token = new StringBuffer();
         if (!IsAlpha(look) && look != '\'') {
             throw expected("Name");
@@ -201,7 +209,9 @@ public final class FormulaParser {
         }
         else
         {
-            while (IsAlNum(look)) {
+            // allow for any sequence of dots and identifier chars
+            // special case of two consecutive dots is best treated in the calling code
+            while (IsAlNum(look) || look == '.') {
                 Token.append(look);
                 GetChar();
             }
@@ -220,15 +230,22 @@ public final class FormulaParser {
         return value.length() == 0 ? null : value.toString();
     }
 
-    private ParseNode parseFunctionOrIdentifier() {
-        String name = GetName();
+    private ParseNode parseFunctionReferenceOrName() {
+        String name = parseIdentifier();
         if (look == '('){
             //This is a function
             return function(name);
         }
-        return new ParseNode(parseIdentifier(name));
+        return new ParseNode(parseNameOrReference(name));
     }
-    private Ptg parseIdentifier(String name) {
+
+    private Ptg parseNameOrReference(String name) {
+        
+        AreaReference areaRef = parseArea(name);
+        if (areaRef != null) {
+            // will happen if dots are used instead of colon
+            return new AreaPtg(areaRef.formatAsString());
+        }
 
         if (look == ':' || look == '.') { // this is a AreaReference
             GetChar();
@@ -238,23 +255,28 @@ public final class FormulaParser {
             }
 
             String first = name;
-            String second = GetName();
+            String second = parseIdentifier();
             return new AreaPtg(first+":"+second);
         }
 
         if (look == '!') {
             Match('!');
             String sheetName = name;
-            String first = GetName();
+            String first = parseIdentifier();
             short externIdx = (short)book.getExternalSheetIndex(book.getSheetIndex(sheetName));
+            areaRef = parseArea(name);
+            if (areaRef != null) {
+                // will happen if dots are used instead of colon
+                return new Area3DPtg(areaRef.formatAsString(), externIdx);
+            }
             if (look == ':') {
                 Match(':');
-                String second=GetName();
+                String second=parseIdentifier();
                 if (look == '!') {
                     //The sheet name was included in both of the areas. Only really
                     //need it once
                     Match('!');
-                    String third=GetName();
+                    String third=parseIdentifier();
 
                     if (!sheetName.equals(second))
                         throw new RuntimeException("Unhandled double sheet reference.");
@@ -271,9 +293,7 @@ public final class FormulaParser {
 
         // This can be either a cell ref or a named range
         // Try to spot which it is
-        boolean cellRef = CELL_REFERENCE_PATTERN.matcher(name).matches();
-
-        if (cellRef) {
+        if (isValidCellReference(name)) {
             return new RefPtg(name);
         }
 
@@ -287,6 +307,41 @@ public final class FormulaParser {
                     + name + "\", but that named range wasn't defined!");
     }
 
+    /**
+     * @return <code>null</code> if name cannot be split at a dot
+     */
+    private AreaReference parseArea(String name) {
+        int dotPos = name.indexOf('.');
+        if (dotPos < 0) {
+            return null;
+        }
+        int dotCount = 1;
+        while (dotCount<name.length() && name.charAt(dotPos+dotCount) == '.') {
+            dotCount++;
+            if (dotCount>3) {
+                // four or more consecutive dots does not convert to ':'
+                return null;
+            }
+        }
+        String partA = name.substring(0, dotPos);
+        if (!isValidCellReference(partA)) {
+            return null;
+        }
+        String partB = name.substring(dotPos+dotCount);
+        if (!isValidCellReference(partB)) {
+            return null;
+        }
+        CellReference topLeft = new CellReference(partA);
+        CellReference bottomRight = new CellReference(partB);
+        return new AreaReference(topLeft, bottomRight);
+    }
+
+    private static boolean isValidCellReference(String str) {
+        // TODO - exact rules for recognising cell references may be too complicated for regex 
+        return CELL_REFERENCE_PATTERN.matcher(str).matches();
+    }
+    
+    
     /**
      * Note - Excel function names are 'case aware but not case sensitive'.  This method may end
      * up creating a defined name record in the workbook if the specified name is not an internal
@@ -465,7 +520,7 @@ public final class FormulaParser {
                 return new ParseNode(parseStringLiteral());
         }
         if (IsAlpha(look) || look == '\''){
-            return parseFunctionOrIdentifier();
+            return parseFunctionReferenceOrName();
         }
         // else - assume number
         return new ParseNode(parseNumber());
@@ -510,7 +565,7 @@ public final class FormulaParser {
 
     private ErrPtg parseErrorLiteral() {
         Match('#');
-        String part1 = GetName().toUpperCase();
+        String part1 = parseIdentifier().toUpperCase();
 
         switch(part1.charAt(0)) {
             case 'V':
index 51268c9aa0b6e18f45f38e06cd9ae52c4e8efbad..dd72eb5c7924fdf548628ff6892d209a16b709f9 100644 (file)
@@ -1,25 +1,78 @@
-/*
-* 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.
-*/
-/*
- * Created on May 15, 2005
- *
- */
+/* ====================================================================
+   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.functions;
 
-public class Errortype extends NotImplementedFunction {
+import org.apache.poi.hssf.record.formula.eval.ErrorEval;
+import org.apache.poi.hssf.record.formula.eval.Eval;
+import org.apache.poi.hssf.record.formula.eval.EvaluationException;
+import org.apache.poi.hssf.record.formula.eval.NumberEval;
+import org.apache.poi.hssf.record.formula.eval.OperandResolver;
+import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
+
+/**
+ * Implementation for the ERROR.TYPE() Excel function.<p/>
+ * 
+ * <b>Syntax:</b><br/>
+ * <b>ERROR.TYPE</b>(<b>errorValue</b>)<p/>
+ * 
+ * Returns a number corresponding to the error type of the supplied argument.<p/>
+ * 
+ *    <table border="1" cellpadding="1" cellspacing="1" summary="Return values for ERROR.TYPE()">
+ *      <tr><td>errorValue</td><td>Return Value</td></tr>
+ *      <tr><td>#NULL!</td><td>1</td></tr>
+ *      <tr><td>#DIV/0!</td><td>2</td></tr>
+ *      <tr><td>#VALUE!</td><td>3</td></tr>
+ *      <tr><td>#REF!</td><td>4</td></tr>
+ *      <tr><td>#NAME?</td><td>5</td></tr>
+ *      <tr><td>#NUM!</td><td>6</td></tr>
+ *      <tr><td>#N/A!</td><td>7</td></tr>
+ *      <tr><td>everything else</td><td>#N/A!</td></tr>
+ *    </table>
+ * 
+ * Note - the results of ERROR.TYPE() are different to the constants defined in 
+ * <tt>HSSFErrorConstants</tt>.
+ * 
+ * @author Josh Micich
+ */
+public final class Errortype implements Function {
+
+       public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
+               
+               try {
+                       OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
+                       return ErrorEval.NA;
+               } catch (EvaluationException e) {
+                       int result = translateErrorCodeToErrorTypeValue(e.getErrorEval().getErrorCode());
+                       return new NumberEval(result);
+               }
+       }
+
+       private int translateErrorCodeToErrorTypeValue(int errorCode) {
+               switch (errorCode) {
+                       case HSSFErrorConstants.ERROR_NULL:  return 1;
+                       case HSSFErrorConstants.ERROR_DIV_0: return 2;
+                       case HSSFErrorConstants.ERROR_VALUE: return 3;
+                       case HSSFErrorConstants.ERROR_REF:   return 4;
+                       case HSSFErrorConstants.ERROR_NAME:  return 5;
+                       case HSSFErrorConstants.ERROR_NUM:   return 6;
+                       case HSSFErrorConstants.ERROR_NA :   return 7;
+               }
+               throw new IllegalArgumentException("Invalid error code (" + errorCode + ")");
+       }
 
 }
index 3aac4b599216fbbc85186786fba42283a3ad2638..b80ccb790551b0e22ea9dfefccf6f811a164b126 100644 (file)
@@ -24,6 +24,7 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Stack;
@@ -105,6 +106,12 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
      */
 
     private ArrayList names;
+    
+    /**
+     * this holds the HSSFFont objects attached to this workbook.
+     * We only create these from the low level records as required.
+     */
+    private Hashtable fonts;
 
     /**
      * holds whether or not to preserve other nodes in the POIFS.  Used
@@ -1021,9 +1028,10 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
         if(fontindex == Short.MAX_VALUE){
             throw new IllegalArgumentException("Maximum number of fonts was exceeded");
         }
-        HSSFFont retval = new HSSFFont(fontindex, font);
-
-        return retval;
+        
+        // Ask getFontAt() to build it for us,
+        //  so it gets properly cached
+        return getFontAt(fontindex);
     }
 
     /**
@@ -1033,15 +1041,11 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
                              String name, boolean italic, boolean strikeout,
                              short typeOffset, byte underline)
     {
-//        System.out.println( boldWeight + ", " + color + ", " + fontHeight + ", " + name + ", " + italic + ", " + strikeout + ", " + typeOffset + ", " + underline );
-        for (short i = 0; i < workbook.getNumberOfFontRecords(); i++)
-        {
-            if (i == 4)
-                continue;
-
-            FontRecord font = workbook.getFontRecordAt(i);
-            HSSFFont hssfFont = new HSSFFont(i, font);
-//            System.out.println( hssfFont.getBoldweight() + ", " + hssfFont.getColor() + ", " + hssfFont.getFontHeight() + ", " + hssfFont.getFontName() + ", " + hssfFont.getItalic() + ", " + hssfFont.getStrikeout() + ", " + hssfFont.getTypeOffset() + ", " + hssfFont.getUnderline() );
+       for (short i=0; i<=getNumberOfFonts(); i++) {
+               // Remember - there is no 4!
+               if(i == 4) continue;
+               
+               HSSFFont hssfFont = getFontAt(i);
             if (hssfFont.getBoldweight() == boldWeight
                     && hssfFont.getColor() == color
                     && hssfFont.getFontHeight() == fontHeight
@@ -1051,12 +1055,10 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
                     && hssfFont.getTypeOffset() == typeOffset
                     && hssfFont.getUnderline() == underline)
             {
-//                System.out.println( "Found font" );
                 return hssfFont;
             }
         }
 
-//        System.out.println( "No font found" );
         return null;
     }
 
@@ -1071,15 +1073,26 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
     }
 
     /**
-     * get the font at the given index number
+     * Get the font at the given index number
      * @param idx  index number
      * @return HSSFFont at the index
      */
 
     public HSSFFont getFontAt(short idx)
     {
+       if(fonts == null) fonts = new Hashtable();
+       
+       // So we don't confuse users, give them back
+       //  the same object every time, but create
+       //  them lazily
+       Short sIdx = Short.valueOf(idx);
+       if(fonts.containsKey(sIdx)) {
+               return (HSSFFont)fonts.get(sIdx);
+       }
+       
         FontRecord font = workbook.getFontRecordAt(idx);
         HSSFFont retval = new HSSFFont(idx, font);
+        fonts.put(sIdx, retval);
 
         return retval;
     }
index 2c51b3d20854554e779f2b4f915a4d680bb83432..d13baecf80264f9289dfc583305924a784c048ce 100644 (file)
@@ -155,8 +155,12 @@ public class HSSFColor implements Color {
 
             String hexString = color.getHexString();
             if (result.containsKey(hexString)) {
-                throw new RuntimeException("Dup color hexString (" + hexString
-                        + ") for color (" + color.getClass().getName() + ")");
+               HSSFColor other = (HSSFColor)result.get(hexString);
+                throw new RuntimeException(
+                               "Dup color hexString (" + hexString
+                        + ") for color (" + color.getClass().getName() + ") - "
+                        + " already taken by (" + other.getClass().getName() + ")"
+                );
             }
             result.put(hexString, color);
         }
@@ -1511,9 +1515,9 @@ public class HSSFColor implements Color {
         public final static short   index     = 0x19;
         public final static short[] triplet   =
         {
-            153, 51, 102
+            127, 0, 0
         };
-        public final static String  hexString = "9999:3333:6666";
+        public final static String  hexString = "8000:0:0";
 
         public short getIndex()
         {
index bc33954dff63e548328e28a20f1ac4915e5f55ca..227200ab5d9b811a89ed8a4257a0fe9547c2a1fb 100644 (file)
@@ -91,15 +91,18 @@ public class TextPiece extends PropertyNode implements Comparable
    public void adjustForDelete(int start, int length)
    {
 
+          // length is expected to be the number of code-points,
+          // not the number of characters
+          int numChars = length;
           if (usesUnicode()) {
 
                   start /= 2;
-                  length /= 2;
+                  numChars = (length / 2);
           }
 
           int myStart = getStart();
           int myEnd = getEnd();
-          int end = start + length;
+          int end = start + numChars;
 
           /* do we have to delete from this text piece? */
           if (start <= myEnd && end >= myStart) {
@@ -108,9 +111,14 @@ public class TextPiece extends PropertyNode implements Comparable
                   int overlapStart = Math.max(myStart, start);
                   int overlapEnd = Math.min(myEnd, end);
                   ((StringBuffer)_buf).delete(overlapStart, overlapEnd);
-                  
-                  super.adjustForDelete(start, length);
           }
+
+          // We need to invoke this even if text from this piece is not being
+          // deleted because the adjustment must propagate to all subsequent
+          // text pieces i.e., if text from tp[n] is being deleted, then
+          // tp[n + 1], tp[n + 2], etc. will need to be adjusted.
+          // The superclass is expected to use a separate sentry for this.
+          super.adjustForDelete(start, length);
    }
 
    public int characterLength()
index cacbbaaa38e60dea15c3d744421ec06e3aa8ad6c..764fc06c4dcf5cee54c5f925af8649cf63d5885c 100644 (file)
@@ -101,7 +101,14 @@ public class SprmOperation
       case 3:
         return LittleEndian.getInt(_grpprl, _gOffset);
       case 6:
-        throw new UnsupportedOperationException("This SPRM contains a variable length operand");
+          byte operandLength = _grpprl[_gOffset + 1];   //surely shorter than an int...
+         
+          byte [] codeBytes = new byte[LittleEndian.INT_SIZE]; //initialized to zeros by JVM
+          for(int i = 0; i < operandLength; i++)
+              if(_gOffset + i < _grpprl.length)
+                         codeBytes[i] = _grpprl[_gOffset + 1 + i];
+
+          return LittleEndian.getInt(codeBytes, 0);
       case 7:
         byte threeByteInt[] = new byte[4];
         threeByteInt[0] = _grpprl[_gOffset];
index 80e9b7526c0c182a61ba6e5ec3a2d1b29d442b05..0ef944f1366e3f1a4c3f0893ab4fd0a878c52be1 100644 (file)
@@ -333,7 +333,7 @@ public class Range
     _doc.getCharacterTable().adjustForInsert(_charStart, adjustedLength);
     _doc.getParagraphTable().adjustForInsert(_parStart, adjustedLength);
     _doc.getSectionTable().adjustForInsert(_sectionStart, adjustedLength);
-    adjustForInsert(text.length());
+       adjustForInsert(adjustedLength);
 
        // update the FIB.CCPText field
        adjustFIB(text.length());
@@ -656,8 +656,15 @@ public class Range
             );
     }
 
+       // this Range isn't a proper parent of the subRange() so we'll have to keep
+       // track of an updated endOffset on our own
+       int previousEndOffset = subRange.getEndOffset();
+
     subRange.insertBefore(pValue);
 
+       if (subRange.getEndOffset() != previousEndOffset)
+               _end += (subRange.getEndOffset() - previousEndOffset);
+
     // re-create the sub-range so we can delete it
     subRange = new Range(
             (absPlaceHolderIndex + pValue.length()),
@@ -671,9 +678,30 @@ public class Range
                                          (pValue.length() * 2)), getDocument()
             );
 
+       // deletes are automagically propagated
     subRange.delete();
   }
 
+  /**
+   * Replace (all instances of) a piece of text with another...
+   *
+   * @param pPlaceHolder    The text to be replaced (e.g., "${organization}")
+   * @param pValue          The replacement text (e.g., "Apache Software Foundation")
+   */
+  public void replaceText(String pPlaceHolder, String pValue)
+  {
+               boolean keepLooking = true;
+               while (keepLooking) {
+
+                       String text = text();
+                       int offset = text.indexOf(pPlaceHolder);
+                       if (offset >= 0)
+                               replaceText(pPlaceHolder, pValue, offset);
+                       else
+                               keepLooking = false;
+               }
+  }
+
   /**
    * Gets the character run at index. The index is relative to this range.
    *
@@ -915,7 +943,7 @@ public class Range
 
   /**
    * adjust this range after an insert happens.
-   * @param length the length to adjust for
+   * @param length the length to adjust for (expected to be a count of code-points, not necessarily chars)
    */
   private void adjustForInsert(int length)
   {
diff --git a/src/scratchpad/testcases/org/apache/poi/hwpf/data/testRangeDelete.doc b/src/scratchpad/testcases/org/apache/poi/hwpf/data/testRangeDelete.doc
new file mode 100644 (file)
index 0000000..8961083
Binary files /dev/null and b/src/scratchpad/testcases/org/apache/poi/hwpf/data/testRangeDelete.doc differ
diff --git a/src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestRangeDelete.java b/src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestRangeDelete.java
new file mode 100644 (file)
index 0000000..1becc23
--- /dev/null
@@ -0,0 +1,196 @@
+
+/* ====================================================================
+   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.hwpf.usermodel;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.util.List;
+
+import org.apache.poi.hwpf.HWPFDocument;
+import org.apache.poi.hwpf.model.PicturesTable;
+import org.apache.poi.hwpf.usermodel.Picture;
+
+import junit.framework.TestCase;
+
+/**
+ *     Test to see if Range.delete() works even if the Range contains a
+ *     CharacterRun that uses Unicode characters.
+ */
+public class TestRangeDelete extends TestCase {
+
+       // u201c and u201d are "smart-quotes"
+       private String originalText =
+               "It is used to confirm that text delete works even if Unicode characters (such as \u201c\u2014\u201d (U+2014), \u201c\u2e8e\u201d (U+2E8E), or \u201c\u2714\u201d (U+2714)) are present.  Everybody should be thankful to the ${organization} ${delete} and all the POI contributors for their assistance in this matter.\r";
+       private String searchText = "${delete}";
+       private String expectedText1 = " This is an MS-Word 97 formatted document created using NeoOffice v. 2.2.4 Patch 0 (OpenOffice.org v. 2.2.1).\r";
+       private String expectedText2 =
+               "It is used to confirm that text delete works even if Unicode characters (such as \u201c\u2014\u201d (U+2014), \u201c\u2e8e\u201d (U+2E8E), or \u201c\u2714\u201d (U+2714)) are present.  Everybody should be thankful to the ${organization}  and all the POI contributors for their assistance in this matter.\r";
+       private String expectedText3 = "Thank you, ${organization} !\r";
+
+       private String illustrativeDocFile;
+
+       protected void setUp() throws Exception {
+
+               String dirname = System.getProperty("HWPF.testdata.path");
+
+               illustrativeDocFile = dirname + "/testRangeDelete.doc";
+       }
+
+       /**
+        * Test just opening the files
+        */
+       public void testOpen() throws Exception {
+
+               HWPFDocument docA = new HWPFDocument(new FileInputStream(illustrativeDocFile));
+       }
+
+       /**
+        * Test (more "confirm" than test) that we have the general structure that we expect to have.
+        */
+       public void testDocStructure() throws Exception {
+
+               HWPFDocument daDoc = new HWPFDocument(new FileInputStream(illustrativeDocFile));
+
+               Range range = daDoc.getRange();
+
+               assertEquals(1, range.numSections());
+               Section section = range.getSection(0);
+
+               assertEquals(5, section.numParagraphs());
+               Paragraph para = section.getParagraph(2);
+
+               assertEquals(5, para.numCharacterRuns());
+
+               assertEquals(originalText, para.text());
+       }
+
+       /**
+        * Test that we can delete text (one instance) from our Range with Unicode text.
+        */
+       public void testRangeDeleteOne() throws Exception {
+
+               HWPFDocument daDoc = new HWPFDocument(new FileInputStream(illustrativeDocFile));
+
+               Range range = daDoc.getRange();
+               assertEquals(1, range.numSections());
+
+               Section section = range.getSection(0);
+               assertEquals(5, section.numParagraphs());
+
+               Paragraph para = section.getParagraph(2);
+
+               String text = para.text();
+               assertEquals(originalText, text);
+
+               int offset = text.indexOf(searchText);
+               assertEquals(192, offset);
+
+               int absOffset = para.getStartOffset() + offset;
+               if (para.usesUnicode())
+                       absOffset = para.getStartOffset() + (offset * 2);
+
+               Range subRange = new Range(absOffset, (absOffset + searchText.length()), para.getDocument());
+               if (subRange.usesUnicode())
+                       subRange = new Range(absOffset, (absOffset + (searchText.length() * 2)), para.getDocument());
+
+               assertEquals(searchText, subRange.text());
+
+               subRange.delete();
+
+               // we need to let the model re-calculate the Range before we evaluate it
+               range = daDoc.getRange();
+
+               assertEquals(1, range.numSections());
+               section = range.getSection(0);
+
+               assertEquals(5, section.numParagraphs());
+               para = section.getParagraph(2);
+
+               text = para.text();
+               assertEquals(expectedText2, text);
+
+               // this can lead to a StringBufferOutOfBoundsException, so we will add it
+               // even though we don't have an assertion for it
+               Range daRange = daDoc.getRange();
+               daRange.text();
+       }
+
+       /**
+        * Test that we can delete text (all instances of) from our Range with Unicode text.
+        */
+       public void testRangeDeleteAll() throws Exception {
+
+               HWPFDocument daDoc = new HWPFDocument(new FileInputStream(illustrativeDocFile));
+
+               Range range = daDoc.getRange();
+               assertEquals(1, range.numSections());
+
+               Section section = range.getSection(0);
+               assertEquals(5, section.numParagraphs());
+
+               Paragraph para = section.getParagraph(2);
+
+               String text = para.text();
+               assertEquals(originalText, text);
+
+               boolean keepLooking = true;
+               while (keepLooking) {
+
+                       int offset = range.text().indexOf(searchText);
+                       if (offset >= 0) {
+
+                               int absOffset = range.getStartOffset() + offset;
+                               if (range.usesUnicode())
+                                       absOffset = range.getStartOffset() + (offset * 2);
+
+                               Range subRange = new Range(
+                                       absOffset, (absOffset + searchText.length()), range.getDocument());
+                               if (subRange.usesUnicode())
+                                       subRange = new Range(
+                                               absOffset, (absOffset + (searchText.length() * 2)), range.getDocument());
+
+                               assertEquals(searchText, subRange.text());
+
+                               subRange.delete();
+
+                       } else
+                               keepLooking = false;
+               }
+
+               // we need to let the model re-calculate the Range before we use it
+               range = daDoc.getRange();
+
+               assertEquals(1, range.numSections());
+               section = range.getSection(0);
+
+               assertEquals(5, section.numParagraphs());
+
+               para = section.getParagraph(1);
+               text = para.text();
+               assertEquals(expectedText1, text);
+
+               para = section.getParagraph(2);
+               text = para.text();
+               assertEquals(expectedText2, text);
+
+               para = section.getParagraph(3);
+               text = para.text();
+               assertEquals(expectedText3, text);
+       }
+}
index 4b2b9ce3701108c59ac550cc8d0a045452426d5a..bda615e943712036657be554c6e0fe1f5e6f17db 100644 (file)
@@ -39,8 +39,9 @@ public class TestRangeReplacement extends TestCase {
                "It is used to confirm that text replacement works even if Unicode characters (such as \u201c\u2014\u201d (U+2014), \u201c\u2e8e\u201d (U+2E8E), or \u201c\u2714\u201d (U+2714)) are present.  Everybody should be thankful to the ${organization} and all the POI contributors for their assistance in this matter.\r";
        private String searchText = "${organization}";
        private String replacementText = "Apache Software Foundation";
-       private String expectedText =
+       private String expectedText2 =
                "It is used to confirm that text replacement works even if Unicode characters (such as \u201c\u2014\u201d (U+2014), \u201c\u2e8e\u201d (U+2E8E), or \u201c\u2714\u201d (U+2714)) are present.  Everybody should be thankful to the Apache Software Foundation and all the POI contributors for their assistance in this matter.\r";
+       private String expectedText3 = "Thank you, Apache Software Foundation!\r";
 
        private String illustrativeDocFile;
 
@@ -84,7 +85,7 @@ public class TestRangeReplacement extends TestCase {
        /**
         * Test that we can replace text in our Range with Unicode text.
         */
-       public void testRangeReplacement() throws Exception {
+       public void testRangeReplacementOne() throws Exception {
 
                HWPFDocument daDoc = new HWPFDocument(new FileInputStream(illustrativeDocFile));
 
@@ -104,16 +105,46 @@ public class TestRangeReplacement extends TestCase {
 
                para.replaceText(searchText, replacementText, offset);
 
-               // we need to let the model re-calculate the Range before we evaluate it
-               range = daDoc.getRange();
-
                assertEquals(1, range.numSections());
                section = range.getSection(0);
 
+               assertEquals(4, section.numParagraphs());
+               para = section.getParagraph(2);
+
+               text = para.text();
+               assertEquals(expectedText2, text);
+       }
+
+       /**
+        * Test that we can replace text in our Range with Unicode text.
+        */
+       public void testRangeReplacementAll() throws Exception {
+
+               HWPFDocument daDoc = new HWPFDocument(new FileInputStream(illustrativeDocFile));
+
+               Range range = daDoc.getRange();
+               assertEquals(1, range.numSections());
+
+               Section section = range.getSection(0);
+               assertEquals(5, section.numParagraphs());
+
+               Paragraph para = section.getParagraph(2);
+
+               String text = para.text();
+               assertEquals(originalText, text);
+
+               range.replaceText(searchText, replacementText);
+
+               assertEquals(1, range.numSections());
+               section = range.getSection(0);
                assertEquals(5, section.numParagraphs());
+
                para = section.getParagraph(2);
+               text = para.text();
+               assertEquals(expectedText2, text);
 
+               para = section.getParagraph(3);
                text = para.text();
-               assertEquals(expectedText, text);
+               assertEquals(expectedText3, text);
        }
 }
diff --git a/src/testcases/org/apache/poi/hssf/data/3dFormulas.xls b/src/testcases/org/apache/poi/hssf/data/3dFormulas.xls
new file mode 100644 (file)
index 0000000..82519ed
Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/3dFormulas.xls differ
index ce94050789f600447ede6c710dd38492496b5e5e..7be92c5fa43389d199e7ad4e75b75e1c7a2043f7 100644 (file)
Binary files a/src/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls and b/src/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls differ
diff --git a/src/testcases/org/apache/poi/hssf/data/MRExtraLines.xls b/src/testcases/org/apache/poi/hssf/data/MRExtraLines.xls
new file mode 100644 (file)
index 0000000..e82e4f6
Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/MRExtraLines.xls differ
diff --git a/src/testcases/org/apache/poi/hssf/eventusermodel/TestEventWorkbookBuilder.java b/src/testcases/org/apache/poi/hssf/eventusermodel/TestEventWorkbookBuilder.java
new file mode 100644 (file)
index 0000000..adf0843
--- /dev/null
@@ -0,0 +1,160 @@
+/* ====================================================================
+   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.eventusermodel;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.hssf.HSSFTestDataSamples;
+import org.apache.poi.hssf.eventusermodel.EventWorkbookBuilder.SheetRecordCollectingListener;
+import org.apache.poi.hssf.model.FormulaParser;
+import org.apache.poi.hssf.model.Workbook;
+import org.apache.poi.hssf.record.FormulaRecord;
+import org.apache.poi.hssf.record.Record;
+import org.apache.poi.hssf.record.formula.Ref3DPtg;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.hssf.util.SheetReferences;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+/**
+ * Tests for {@link EventWorkbookBuilder}
+ */
+public final class TestEventWorkbookBuilder extends TestCase {
+       private MockHSSFListener mockListen;
+       private SheetRecordCollectingListener listener;
+       
+       public void setUp() {
+               HSSFRequest req = new HSSFRequest();
+               mockListen = new MockHSSFListener();
+               listener = new SheetRecordCollectingListener(mockListen);
+               req.addListenerForAllRecords(listener);
+               
+               HSSFEventFactory factory = new HSSFEventFactory();
+               try {
+                       InputStream is = HSSFTestDataSamples.openSampleFileStream("3dFormulas.xls");
+                       POIFSFileSystem fs = new POIFSFileSystem(is);
+                       factory.processWorkbookEvents(req, fs);
+               } catch (IOException e) {
+                       throw new RuntimeException(e);
+               }
+       } 
+       
+       public void testBasics() throws Exception {
+               assertNotNull(listener.getSSTRecord());
+               assertNotNull(listener.getBoundSheetRecords());
+               assertNotNull(listener.getExternSheetRecords());
+       }
+       
+       public void testGetStubWorkbooks() throws Exception {
+               assertNotNull(listener.getStubWorkbook());
+               assertNotNull(listener.getStubHSSFWorkbook());
+               
+               assertNotNull(listener.getStubWorkbook().getSheetReferences());
+               assertNotNull(listener.getStubHSSFWorkbook().getSheetReferences());
+       }
+       
+       public void testContents() throws Exception {
+               assertEquals(2, listener.getSSTRecord().getNumStrings());
+               assertEquals(3, listener.getBoundSheetRecords().length);
+               assertEquals(1, listener.getExternSheetRecords().length);
+               
+               assertEquals(3, listener.getStubWorkbook().getNumSheets());
+               
+               SheetReferences ref = listener.getStubWorkbook().getSheetReferences();
+               assertEquals("Sh3", ref.getSheetName(0));
+               assertEquals("Sheet1", ref.getSheetName(1));
+               assertEquals("S2", ref.getSheetName(2));
+       }
+       
+       public void testFormulas() throws Exception {
+               FormulaRecord fr;
+               
+               // Check our formula records
+               assertEquals(6, mockListen._frecs.size());
+               
+               Workbook stubWB = listener.getStubWorkbook();
+               assertNotNull(stubWB);
+               HSSFWorkbook stubHSSF = listener.getStubHSSFWorkbook();
+               assertNotNull(stubHSSF);
+               
+               // Check these stubs have the right stuff on them
+               assertEquals("Sheet1", stubWB.getSheetName(0));
+               assertEquals("S2", stubWB.getSheetName(1));
+               assertEquals("Sh3", stubWB.getSheetName(2));
+               
+               // Check we can get the formula without breaking
+               for(int i=0; i<mockListen._frecs.size(); i++) {
+                       fr = (FormulaRecord)mockListen._frecs.get(i);
+                       FormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression());
+               }
+               
+               // Peer into just one formula, and check that
+               //  all the ptgs give back the right things
+               List ptgs = ((FormulaRecord)mockListen._frecs.get(0)).getParsedExpression();
+               assertEquals(1, ptgs.size());
+               assertTrue(ptgs.get(0) instanceof Ref3DPtg);
+               
+               Ref3DPtg ptg = (Ref3DPtg)ptgs.get(0);
+               assertEquals("Sheet1!A1", ptg.toFormulaString(stubHSSF));
+               
+               
+               // Now check we get the right formula back for
+               //  a few sample ones
+               
+               // Sheet 1 A2 is on same sheet
+               fr = (FormulaRecord)mockListen._frecs.get(0);
+               assertEquals(1, fr.getRow());
+               assertEquals(0, fr.getColumn());
+               assertEquals("Sheet1!A1", FormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression()));
+               
+               // Sheet 1 A5 is to another sheet
+               fr = (FormulaRecord)mockListen._frecs.get(3);
+               assertEquals(4, fr.getRow());
+               assertEquals(0, fr.getColumn());
+               assertEquals("'S2'!A1", FormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression()));
+               
+               // Sheet 1 A7 is to another sheet, range
+               fr = (FormulaRecord)mockListen._frecs.get(5);
+               assertEquals(6, fr.getRow());
+               assertEquals(0, fr.getColumn());
+               assertEquals("SUM('Sh3'!A1:A4)", FormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression()));
+               
+               
+               // Now, load via Usermodel and re-check
+               InputStream is = HSSFTestDataSamples.openSampleFileStream("3dFormulas.xls");
+               POIFSFileSystem fs = new POIFSFileSystem(is);
+               HSSFWorkbook wb = new HSSFWorkbook(fs);
+               assertEquals("Sheet1!A1", wb.getSheetAt(0).getRow(1).getCell(0).getCellFormula());
+               assertEquals("SUM('Sh3'!A1:A4)", wb.getSheetAt(0).getRow(6).getCell(0).getCellFormula());
+       }
+       
+       private static final class MockHSSFListener implements HSSFListener {
+               public MockHSSFListener() {}
+               private final List _records = new ArrayList();
+               private final List _frecs = new ArrayList();
+
+               public void processRecord(Record record) {
+                       _records.add(record);
+                       if(record instanceof FormulaRecord) {
+                               _frecs.add(record);
+                       }
+               }
+       }
+}
\ No newline at end of file
index 16a406cba1640d9c1c576b08cbcf816c2d6677ba..37e594940cd8ff56f42415b714cbbf073b4250b0 100644 (file)
@@ -16,8 +16,6 @@
 ==================================================================== */
 
 package org.apache.poi.hssf.eventusermodel;
-import java.io.File;
-import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
@@ -29,6 +27,7 @@ import org.apache.poi.hssf.HSSFTestDataSamples;
 import org.apache.poi.hssf.eventusermodel.dummyrecord.LastCellOfRowDummyRecord;
 import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingCellDummyRecord;
 import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingRowDummyRecord;
+import org.apache.poi.hssf.record.BOFRecord;
 import org.apache.poi.hssf.record.LabelSSTRecord;
 import org.apache.poi.hssf.record.Record;
 import org.apache.poi.hssf.record.RowRecord;
@@ -40,8 +39,7 @@ public final class TestMissingRecordAwareHSSFListener extends TestCase {
        
        private Record[] r;
 
-       public void setUp() {
-
+       public void openNormal() {
                HSSFRequest req = new HSSFRequest();
                MockHSSFListener mockListen = new MockHSSFListener();
                MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(mockListen);
@@ -55,10 +53,31 @@ public final class TestMissingRecordAwareHSSFListener extends TestCase {
                } catch (IOException e) {
                        throw new RuntimeException(e);
                }
+               
                r = mockListen.getRecords();
+               assertTrue(r.length > 100);
+       } 
+       public void openAlt() {
+               HSSFRequest req = new HSSFRequest();
+               MockHSSFListener mockListen = new MockHSSFListener();
+               MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(mockListen);
+               req.addListenerForAllRecords(listener);
+               
+               HSSFEventFactory factory = new HSSFEventFactory();
+               try {
+                       InputStream is = HSSFTestDataSamples.openSampleFileStream("MRExtraLines.xls");
+                       POIFSFileSystem fs = new POIFSFileSystem(is);
+                       factory.processWorkbookEvents(req, fs);
+               } catch (IOException e) {
+                       throw new RuntimeException(e);
+               }
+               
+               r = mockListen.getRecords();
+               assertTrue(r.length > 100);
        } 
        
        public void testMissingRowRecords() throws Exception {
+               openNormal();
                
                // We have rows 0, 1, 2, 20 and 21
                int row0 = -1;
@@ -108,6 +127,7 @@ public final class TestMissingRecordAwareHSSFListener extends TestCase {
        }
        
        public void testEndOfRowRecords() throws Exception {
+               openNormal();
                
                // Find the cell at 0,0
                int cell00 = -1;
@@ -194,7 +214,7 @@ public final class TestMissingRecordAwareHSSFListener extends TestCase {
                assertTrue(r[cell00+57] instanceof LastCellOfRowDummyRecord);
                
                // Check the numbers of the last seen columns
-               LastCellOfRowDummyRecord[] lrs = new LastCellOfRowDummyRecord[23];
+               LastCellOfRowDummyRecord[] lrs = new LastCellOfRowDummyRecord[24];
                int lrscount = 0;
                for(int i=0; i<r.length; i++) {
                        if(r[i] instanceof LastCellOfRowDummyRecord) {
@@ -229,6 +249,7 @@ public final class TestMissingRecordAwareHSSFListener extends TestCase {
        
        
        public void testMissingCellRecords() throws Exception {
+               openNormal();
                
                // Find the cell at 0,0
                int cell00 = -1;
@@ -326,10 +347,38 @@ public final class TestMissingRecordAwareHSSFListener extends TestCase {
                assertEquals(22, mc.getRow());
                assertEquals(10, mc.getColumn());
        }
+       
+       // Make sure we don't put in any extra new lines
+       //  that aren't already there
+       public void testNoExtraNewLines() throws Exception {
+               // Load a different file
+               openAlt();
+               
+               
+               // This file has has something in lines 1-33
+               List lcor = new ArrayList();
+               for(int i=0; i<r.length; i++) {
+                       if(r[i] instanceof LastCellOfRowDummyRecord)
+                               lcor.add( (LastCellOfRowDummyRecord)r[i] );
+               }
+               
+               // Check we got the 33 rows
+               assertEquals(33, lcor.size());
+               LastCellOfRowDummyRecord[] rowEnds = (LastCellOfRowDummyRecord[])
+                       lcor.toArray(new LastCellOfRowDummyRecord[lcor.size()]);
+               assertEquals(33, rowEnds.length);
+               
+               // And check they have the right stuff in them,
+               //  no repeats
+               for(int i=0; i<rowEnds.length; i++) {
+                       assertEquals(i, rowEnds[i].getRow());
+               }
+       }
 
        private static final class MockHSSFListener implements HSSFListener {
                public MockHSSFListener() {}
                private final List _records = new ArrayList();
+               private boolean logToStdOut = false;
 
                public void processRecord(Record record) {
                        _records.add(record);
@@ -346,9 +395,20 @@ public final class TestMissingRecordAwareHSSFListener extends TestCase {
                                LastCellOfRowDummyRecord lc = (LastCellOfRowDummyRecord)record;
                                log("Got end-of row, row was " + lc.getRow() + ", last column was " + lc.getLastColumnNumber());
                        }
+                       
+                       if(record instanceof BOFRecord) {
+                               BOFRecord r = (BOFRecord)record;
+                               if(r.getType() == BOFRecord.TYPE_WORKSHEET) {
+                                       log("On new sheet");
+                               }
+                       }
+                       if(record instanceof RowRecord) {
+                               RowRecord rr = (RowRecord)record;
+                               log("Starting row #" + rr.getRowNumber());
+                       }
                }
-               private static void log(String msg) {
-                       if(false) { // successful tests should be quiet
+               private void log(String msg) {
+                       if(logToStdOut) {
                                System.out.println(msg);
                        }
                }
index 987ad0b0960f1fbd72309ad9aed0d71e53e6d6df..115a7e081b35dc05f5122b1b580081f5da98881a 100644 (file)
@@ -759,7 +759,7 @@ public final class TestFormulaParser extends TestCase {
                }
        }
 
-       public void testParseErrorExpecteMsg() {
+       public void testParseErrorExpectedMsg() {
 
                try {
                        parseFormula("round(3.14;2)");
@@ -768,4 +768,27 @@ public final class TestFormulaParser extends TestCase {
                        assertEquals("Parse error near char 10 ';' in specified formula 'round(3.14;2)'. Expected ',' or ')'", e.getMessage());
                }
        }
+       
+       /**
+        * this function name has a dot in it.
+        */
+       public void testParseErrorTypeFunction() {
+
+               Ptg[] ptgs;
+               try {
+                       ptgs = parseFormula("error.type(A1)");
+                       
+                       
+               } catch (IllegalArgumentException e) {
+                       if (e.getMessage().equals("Invalid Formula cell reference: 'error'")) {
+                               throw new AssertionFailedError("Identified bug 45334");
+                       }
+                       throw e;
+               }
+               assertEquals(2, ptgs.length);
+               assertEquals(FuncPtg.class, ptgs[1].getClass());
+               FuncPtg funcPtg = (FuncPtg) ptgs[1];
+               assertEquals("ERROR.TYPE", funcPtg.getName());
+       }
+       
 }
index 79ef47be38ddd51e0fe6d8df86384fef767a0057..cf48573682e0367e02fe436f9f293a4ce2b06420 100644 (file)
@@ -1052,4 +1052,88 @@ public final class TestBugs extends TestCase {
                assertTrue(nd.get(0) instanceof DeletedArea3DPtg);
         }
     }
+    
+    /**
+     * Test that fonts get added properly
+     */
+    public void test45338() throws Exception {
+       HSSFWorkbook wb = new HSSFWorkbook();
+       assertEquals(4, wb.getNumberOfFonts());
+       
+       HSSFSheet s = wb.createSheet();
+       s.createRow(0);
+       s.createRow(1);
+       HSSFCell c1 = s.getRow(0).createCell((short)0);
+       HSSFCell c2 = s.getRow(1).createCell((short)0);
+       
+       assertEquals(4, wb.getNumberOfFonts());
+       
+       HSSFFont f1 = wb.getFontAt((short)0);
+       assertEquals(400, f1.getBoldweight());
+       
+       // Check that asking for the same font
+       //  multiple times gives you the same thing.
+       // Otherwise, our tests wouldn't work!
+       assertEquals(
+                       wb.getFontAt((short)0),
+                       wb.getFontAt((short)0)
+       );
+       assertEquals(
+                       wb.getFontAt((short)2),
+                       wb.getFontAt((short)2)
+       );
+       assertTrue(
+                       wb.getFontAt((short)0)
+                       !=
+                       wb.getFontAt((short)2)
+       );
+       
+       // Look for a new font we have
+       //  yet to add
+       assertNull(
+               wb.findFont(
+                       (short)11, (short)123, (short)22, 
+                       "Thingy", false, true, (short)2, (byte)2
+               )
+       );
+       
+       HSSFFont nf = wb.createFont();
+       assertEquals(5, wb.getNumberOfFonts());
+       
+       assertEquals(5, nf.getIndex());
+       assertEquals(nf, wb.getFontAt((short)5));
+       
+       nf.setBoldweight((short)11);
+       nf.setColor((short)123);
+       nf.setFontHeight((short)22);
+       nf.setFontName("Thingy");
+       nf.setItalic(false);
+       nf.setStrikeout(true);
+       nf.setTypeOffset((short)2);
+       nf.setUnderline((byte)2);
+       
+       assertEquals(5, wb.getNumberOfFonts());
+       assertEquals(nf, wb.getFontAt((short)5));
+       
+       // Find it now
+       assertNotNull(
+               wb.findFont(
+                       (short)11, (short)123, (short)22, 
+                       "Thingy", false, true, (short)2, (byte)2
+               )
+       );
+       assertEquals(
+               5,
+               wb.findFont(
+                               (short)11, (short)123, (short)22, 
+                               "Thingy", false, true, (short)2, (byte)2
+                       ).getIndex()
+       );
+       assertEquals(nf,
+                       wb.findFont(
+                               (short)11, (short)123, (short)22, 
+                               "Thingy", false, true, (short)2, (byte)2
+                       )
+       );
+    }
 }
diff --git a/src/testcases/org/apache/poi/hssf/util/TestHSSFColor.java b/src/testcases/org/apache/poi/hssf/util/TestHSSFColor.java
new file mode 100644 (file)
index 0000000..42571eb
--- /dev/null
@@ -0,0 +1,52 @@
+/* ====================================================================
+   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.util;
+
+import java.util.Hashtable;
+
+import junit.framework.TestCase;
+
+public final class TestHSSFColor extends TestCase {
+       public void testBasics() {
+               assertNotNull(HSSFColor.YELLOW.class);
+               assertTrue(HSSFColor.YELLOW.index > 0);
+               assertTrue(HSSFColor.YELLOW.index2 > 0);
+       }
+       
+       public void testContents() {
+               assertEquals(3, HSSFColor.YELLOW.triplet.length);
+               assertEquals(255, HSSFColor.YELLOW.triplet[0]);
+               assertEquals(255, HSSFColor.YELLOW.triplet[1]);
+               assertEquals(0, HSSFColor.YELLOW.triplet[2]);
+               
+               assertEquals("FFFF:FFFF:0", HSSFColor.YELLOW.hexString);
+       }
+       
+       public void testTrippletHash() {
+               Hashtable tripplets = HSSFColor.getTripletHash();
+               
+               assertEquals(
+                               HSSFColor.MAROON.class,
+                               tripplets.get(HSSFColor.MAROON.hexString).getClass()
+               );
+               assertEquals(
+                               HSSFColor.YELLOW.class,
+                               tripplets.get(HSSFColor.YELLOW.hexString).getClass()
+               );
+       }
+}