]> 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, 7 Sep 2008 20:11:32 +0000 (20:11 +0000)
committerNick Burch <nick@apache.org>
Sun, 7 Sep 2008 20:11:32 +0000 (20:11 +0000)
https://svn.apache.org/repos/asf/poi/trunk

........
  r691533 | yegor | 2008-09-03 09:04:07 +0100 (Wed, 03 Sep 2008) | 1 line

  fixed bug #45728: SlideShow.reorderSlide didn't work properly
........
  r691687 | josh | 2008-09-03 18:03:02 +0100 (Wed, 03 Sep 2008) | 1 line

  Fixed ArrayPtg.toString to not crash when partially initialised
........
  r691740 | josh | 2008-09-03 20:22:53 +0100 (Wed, 03 Sep 2008) | 1 line

  Initial work on bug 45720 - copy 'FilterDatabase' named record when cloning sheets. Some clean-up in NameRecord.
........
  r692239 | josh | 2008-09-04 21:58:37 +0100 (Thu, 04 Sep 2008) | 1 line

  Fixed 2 small bugs in RelationalOperationEval (added junits).  Refactored hierarchy.
........
  r692241 | josh | 2008-09-04 22:01:48 +0100 (Thu, 04 Sep 2008) | 1 line

  Fix unused import (correction to r692239)
........
  r692243 | josh | 2008-09-04 22:05:50 +0100 (Thu, 04 Sep 2008) | 1 line

  Fixed compiler warnings, linked junit test to suite
........
  r692255 | josh | 2008-09-04 22:32:17 +0100 (Thu, 04 Sep 2008) | 1 line

  Made HSSFFormulaEvaluator capable of handling simple named ranges
........
  r692300 | josh | 2008-09-05 00:16:15 +0100 (Fri, 05 Sep 2008) | 1 line

  Fix for bug 45376 - added caching to HSSFFormulaEvaluator
........
  r692506 | josh | 2008-09-05 19:22:30 +0100 (Fri, 05 Sep 2008) | 1 line

  Minor fixes for numeric operators - junit added.  Some refactoring.
........
  r692538 | josh | 2008-09-05 21:38:51 +0100 (Fri, 05 Sep 2008) | 1 line

  Modified formula evaluator to handle whole column refs
........
  r692541 | josh | 2008-09-05 21:43:37 +0100 (Fri, 05 Sep 2008) | 1 line

  reverted changes accidentally submitted with r692538
........
  r692612 | josh | 2008-09-06 06:30:31 +0100 (Sat, 06 Sep 2008) | 1 line

  Fixes for special cases of lookup functions (test cases added)
........
  r692614 | josh | 2008-09-06 07:04:01 +0100 (Sat, 06 Sep 2008) | 1 line

  Minor fixes to YEARFRAC(). Added ISEVEN() and ISODD(). Added test cases.
........
  r692893 | yegor | 2008-09-07 17:30:35 +0100 (Sun, 07 Sep 2008) | 1 line

  fixed bug #45720: cloneSheet breaks autofilters.
........

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

72 files changed:
src/documentation/content/xdocs/changes.xml
src/documentation/content/xdocs/status.xml
src/java/org/apache/poi/ddf/EscherContainerRecord.java
src/java/org/apache/poi/hssf/model/DrawingManager2.java
src/java/org/apache/poi/hssf/model/Workbook.java
src/java/org/apache/poi/hssf/record/NameRecord.java
src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java
src/java/org/apache/poi/hssf/record/formula/Ref3DPtg.java
src/java/org/apache/poi/hssf/record/formula/atp/AnalysisToolPak.java
src/java/org/apache/poi/hssf/record/formula/atp/ParityFunction.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/record/formula/atp/YearFrac.java
src/java/org/apache/poi/hssf/record/formula/eval/AddEval.java
src/java/org/apache/poi/hssf/record/formula/eval/DivideEval.java
src/java/org/apache/poi/hssf/record/formula/eval/EqualEval.java
src/java/org/apache/poi/hssf/record/formula/eval/ExternalFunction.java
src/java/org/apache/poi/hssf/record/formula/eval/GreaterEqualEval.java
src/java/org/apache/poi/hssf/record/formula/eval/GreaterThanEval.java
src/java/org/apache/poi/hssf/record/formula/eval/LazyAreaEval.java
src/java/org/apache/poi/hssf/record/formula/eval/LazyRefEval.java
src/java/org/apache/poi/hssf/record/formula/eval/LessEqualEval.java
src/java/org/apache/poi/hssf/record/formula/eval/LessThanEval.java
src/java/org/apache/poi/hssf/record/formula/eval/MultiplyEval.java
src/java/org/apache/poi/hssf/record/formula/eval/NameEval.java
src/java/org/apache/poi/hssf/record/formula/eval/NotEqualEval.java
src/java/org/apache/poi/hssf/record/formula/eval/NumericOperationEval.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/eval/PercentEval.java
src/java/org/apache/poi/hssf/record/formula/eval/PowerEval.java
src/java/org/apache/poi/hssf/record/formula/eval/RelationalOperationEval.java
src/java/org/apache/poi/hssf/record/formula/eval/SubtractEval.java
src/java/org/apache/poi/hssf/record/formula/eval/TwoOperandNumericOperation.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/record/formula/eval/UnaryMinusEval.java
src/java/org/apache/poi/hssf/record/formula/eval/UnaryPlusEval.java
src/java/org/apache/poi/hssf/record/formula/eval/ValueEvalToNumericXlator.java
src/java/org/apache/poi/hssf/record/formula/functions/Hlookup.java
src/java/org/apache/poi/hssf/record/formula/functions/LookupUtils.java
src/java/org/apache/poi/hssf/record/formula/functions/Vlookup.java
src/java/org/apache/poi/hssf/usermodel/HSSFName.java
src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
src/java/org/apache/poi/hssf/usermodel/OperationEvaluatorFactory.java [new file with mode: 0755]
src/java/org/apache/poi/ss/usermodel/EvaluationCache.java [new file with mode: 0644]
src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java
src/java/org/apache/poi/ss/usermodel/OperationEvaluatorFactory.java
src/java/org/apache/poi/ss/util/AreaReference.java
src/java/org/apache/poi/ss/util/CellReference.java
src/scratchpad/src/org/apache/poi/hslf/record/Document.java
src/scratchpad/src/org/apache/poi/hslf/record/RecordContainer.java
src/scratchpad/src/org/apache/poi/hslf/record/SlideListWithText.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java
src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestReOrderingSlides.java
src/testcases/org/apache/poi/hssf/data/45376.xls [deleted file]
src/testcases/org/apache/poi/hssf/data/45720.xls [new file with mode: 0755]
src/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls
src/testcases/org/apache/poi/hssf/data/LookupFunctionsTestCaseData.xls
src/testcases/org/apache/poi/hssf/data/externalFunctionExample.xls
src/testcases/org/apache/poi/hssf/data/testNames.xls
src/testcases/org/apache/poi/hssf/record/formula/TestExternalFunctionFormulas.java
src/testcases/org/apache/poi/hssf/record/formula/atp/TestYearFracCalculator.java
src/testcases/org/apache/poi/hssf/record/formula/eval/AllFormulaEvalTests.java
src/testcases/org/apache/poi/hssf/record/formula/eval/TestCircularReferences.java
src/testcases/org/apache/poi/hssf/record/formula/eval/TestDivideEval.java [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/record/formula/eval/TestEqualEval.java [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/record/formula/eval/TestPercentEval.java
src/testcases/org/apache/poi/hssf/record/formula/eval/TestUnaryPlusEval.java
src/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java
src/testcases/org/apache/poi/hssf/record/formula/functions/TestDate.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/TestHSSFFormulaEvaluator.java [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFWorkbook.java
src/testcases/org/apache/poi/hssf/util/TestAreaReference.java

index cd86842409f5e6d9585eee5bfe1f907630b20632..a78c832e5af0407028abfe152ecaf94ba2696dc2 100644 (file)
@@ -65,6 +65,8 @@
            <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
         </release>
         <release version="3.1.1-alpha1" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="fix">45720 Fixed HSSFWorkbook.cloneSheet to correctly clone sheets with drawings</action>
+           <action dev="POI-DEVELOPERS" type="fix">45728 Fix for SlideShow.reorderSlide in HSLF</action>
            <action dev="POI-DEVELOPERS" type="add">Initial support for embedded movies and controls in HSLF</action>
            <action dev="POI-DEVELOPERS" type="fix">45358 - signed/unsigned error when parsing 3-d area refs, performance problem evaluating area refs, and ClassCastExcecption in IF()</action>
            <action dev="POI-DEVELOPERS" type="add">Support for HPBF Publisher hyperlinks, including during text extraction</action>
index 5c7198bce557668f2e23ccc06be91f4e3ba87968..85bda8b74fe3a82208183d521bc41559b34880e5 100644 (file)
@@ -62,6 +62,8 @@
            <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
         </release>
         <release version="3.1.1-alpha1" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="fix">45720 Fixed HSSFWorkbook.cloneSheet to correctly clone sheets with drawings</action>
+           <action dev="POI-DEVELOPERS" type="fix">45728 Fix for SlideShow.reorderSlide in HSLF</action>
            <action dev="POI-DEVELOPERS" type="add">Initial support for embedded movies and controls in HSLF</action>
            <action dev="POI-DEVELOPERS" type="fix">45358 - signed/unsigned error when parsing 3-d area refs, performance problem evaluating area refs, and ClassCastExcecption in IF()</action>
            <action dev="POI-DEVELOPERS" type="add">Support for HPBF Publisher hyperlinks, including during text extraction</action>
index 28b4a976c3f89a41cf6c30f8e3cd2038b4712c08..bb188d5387be8dc16b32b2e6c3a5f052b1f0315f 100644 (file)
@@ -235,4 +235,21 @@ public class EscherContainerRecord extends EscherRecord
         return null;
     }
 
+    /**
+     * Recursively find records with the specified record ID
+     *
+     * @param out - list to store found records
+     */
+    public void getRecordsById(short recordId, List out){
+        for(Iterator it = childRecords.iterator(); it.hasNext();) {
+            Object er = it.next();
+            if(er instanceof EscherContainerRecord) {
+                EscherContainerRecord c = (EscherContainerRecord)er;
+                c.getRecordsById(recordId, out );
+            } else if (er instanceof EscherSpRecord){
+                out.add(er);
+            }
+        }
+    }
+
 }
index dee95fb4fcb1cb99af482a5fd42cbec951324c40..c9993c2418732b4110fa8e5524ab85a9a76eb639 100644 (file)
@@ -67,6 +67,17 @@ public class DrawingManager2
      * @return a new shape id.
      */
     public int allocateShapeId(short drawingGroupId)
+    {
+        EscherDgRecord dg = getDrawingGroup(drawingGroupId);
+        return allocateShapeId(drawingGroupId, dg);
+    }
+
+    /**
+     * Allocates new shape id for the new drawing group id.
+     *
+     * @return a new shape id.
+     */
+    public int allocateShapeId(short drawingGroupId, EscherDgRecord dg)
     {
         dgg.setNumShapesSaved( dgg.getNumShapesSaved() + 1 );
 
@@ -78,7 +89,6 @@ public class DrawingManager2
             {
                 int result = c.getNumShapeIdsUsed() + (1024 * (i+1));
                 c.incrementShapeId();
-                EscherDgRecord dg = getDrawingGroup(drawingGroupId);
                 dg.setNumShapes( dg.getNumShapes() + 1 );
                 dg.setLastMSOSPID( result );
                 if (result >= dgg.getShapeIdMax())
@@ -90,7 +100,6 @@ public class DrawingManager2
         // Create new cluster
         dgg.addCluster( drawingGroupId, 0 );
         dgg.getFileIdClusters()[dgg.getFileIdClusters().length-1].incrementShapeId();
-        EscherDgRecord dg = getDrawingGroup(drawingGroupId);
         dg.setNumShapes( dg.getNumShapes() + 1 );
         int result = (1024 * dgg.getFileIdClusters().length);
         dg.setLastMSOSPID( result );
@@ -98,7 +107,6 @@ public class DrawingManager2
             dgg.setShapeIdMax( result + 1 );
         return result;
     }
-
     ////////////  Non-public methods /////////////
     
     /**
index 5ad1ef28d3cc4d725fa6ee3ab33a1ebe52ec3d98..f093feef663e5bb7427194a4eb35f29cbfcf7b40 100644 (file)
@@ -22,57 +22,8 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 
-import org.apache.poi.ddf.EscherBSERecord;
-import org.apache.poi.ddf.EscherBoolProperty;
-import org.apache.poi.ddf.EscherContainerRecord;
-import org.apache.poi.ddf.EscherDggRecord;
-import org.apache.poi.ddf.EscherOptRecord;
-import org.apache.poi.ddf.EscherProperties;
-import org.apache.poi.ddf.EscherRGBProperty;
-import org.apache.poi.ddf.EscherRecord;
-import org.apache.poi.ddf.EscherSplitMenuColorsRecord;
-import org.apache.poi.hssf.record.BOFRecord;
-import org.apache.poi.hssf.record.BackupRecord;
-import org.apache.poi.hssf.record.BookBoolRecord;
-import org.apache.poi.hssf.record.BoundSheetRecord;
-import org.apache.poi.hssf.record.CodepageRecord;
-import org.apache.poi.hssf.record.CountryRecord;
-import org.apache.poi.hssf.record.DSFRecord;
-import org.apache.poi.hssf.record.DateWindow1904Record;
-import org.apache.poi.hssf.record.DrawingGroupRecord;
-import org.apache.poi.hssf.record.EOFRecord;
-import org.apache.poi.hssf.record.ExtSSTRecord;
-import org.apache.poi.hssf.record.ExtendedFormatRecord;
-import org.apache.poi.hssf.record.ExternSheetRecord;
-import org.apache.poi.hssf.record.FileSharingRecord;
-import org.apache.poi.hssf.record.FnGroupCountRecord;
-import org.apache.poi.hssf.record.FontRecord;
-import org.apache.poi.hssf.record.FormatRecord;
-import org.apache.poi.hssf.record.HideObjRecord;
-import org.apache.poi.hssf.record.HyperlinkRecord;
-import org.apache.poi.hssf.record.InterfaceEndRecord;
-import org.apache.poi.hssf.record.InterfaceHdrRecord;
-import org.apache.poi.hssf.record.MMSRecord;
-import org.apache.poi.hssf.record.NameRecord;
-import org.apache.poi.hssf.record.PaletteRecord;
-import org.apache.poi.hssf.record.PasswordRecord;
-import org.apache.poi.hssf.record.PasswordRev4Record;
-import org.apache.poi.hssf.record.PrecisionRecord;
-import org.apache.poi.hssf.record.ProtectRecord;
-import org.apache.poi.hssf.record.ProtectionRev4Record;
-import org.apache.poi.hssf.record.RecalcIdRecord;
-import org.apache.poi.hssf.record.Record;
-import org.apache.poi.hssf.record.RefreshAllRecord;
-import org.apache.poi.hssf.record.SSTRecord;
-import org.apache.poi.hssf.record.StyleRecord;
-import org.apache.poi.hssf.record.SupBookRecord;
-import org.apache.poi.hssf.record.TabIdRecord;
-import org.apache.poi.hssf.record.UnicodeString;
-import org.apache.poi.hssf.record.UseSelFSRecord;
-import org.apache.poi.hssf.record.WindowOneRecord;
-import org.apache.poi.hssf.record.WindowProtectRecord;
-import org.apache.poi.hssf.record.WriteAccessRecord;
-import org.apache.poi.hssf.record.WriteProtectRecord;
+import org.apache.poi.ddf.*;
+import org.apache.poi.hssf.record.*;
 import org.apache.poi.hssf.record.formula.NameXPtg;
 import org.apache.poi.hssf.util.HSSFColor;
 import org.apache.poi.hssf.util.SheetReferences;
@@ -2206,17 +2157,17 @@ public final class Workbook implements Model {
         //  contains a EscherDggRecord
         for(Iterator rit = records.iterator(); rit.hasNext();) {
             Record r = (Record)rit.next();
-            
+
             if(r instanceof DrawingGroupRecord) {
                 DrawingGroupRecord dg =    (DrawingGroupRecord)r;
                 dg.processChildRecords();
-                
+
                 EscherContainerRecord cr =
                     dg.getEscherContainer();
                 if(cr == null) {
                     continue;
                 }
-                
+
                 EscherDggRecord dgg = null;
                 for(Iterator it = cr.getChildRecords().iterator(); it.hasNext();) {
                     Object er = it.next();
@@ -2224,7 +2175,7 @@ public final class Workbook implements Model {
                         dgg = (EscherDggRecord)er;
                     }
                 }
-                
+
                 if(dgg != null) {
                     drawingManager = new DrawingManager2(dgg);
                     return;
@@ -2234,7 +2185,7 @@ public final class Workbook implements Model {
 
         // Look for the DrawingGroup record
         int dgLoc = findFirstRecordLocBySid(DrawingGroupRecord.sid);
-        
+
         // If there is one, does it have a EscherDggRecord?
         if(dgLoc != -1) {
             DrawingGroupRecord dg =
@@ -2246,7 +2197,7 @@ public final class Workbook implements Model {
                     dgg = (EscherDggRecord)er;
                 }
             }
-            
+
             if(dgg != null) {
                 drawingManager = new DrawingManager2(dgg);
             }
@@ -2455,4 +2406,54 @@ public final class Workbook implements Model {
     public NameXPtg getNameXPtg(String name) {
         return getOrCreateLinkTable().getNameXPtg(name);
     }
+
+    /**
+     * Check if the cloned sheet has drawings. If yes, then allocate a new drawing group ID and
+     * re-generate shape IDs
+     *
+     * @param sheet the cloned sheet
+     */
+    public void cloneDrawings(Sheet sheet){
+
+        findDrawingGroup();
+
+        if(drawingManager == null) {
+            //this workbook does not have drawings
+            return;
+        }
+
+        //check if the cloned sheet has drawings
+        int aggLoc = sheet.aggregateDrawingRecords(drawingManager, false);
+        if(aggLoc != -1) {
+            EscherAggregate agg = (EscherAggregate) sheet.findFirstRecordBySid(EscherAggregate.sid);
+
+            EscherDggRecord dgg = drawingManager.getDgg();
+
+            //register a new drawing group for the cloned sheet
+            int dgId = drawingManager.findNewDrawingGroupId();
+            dgg.addCluster( dgId, 0 );
+            dgg.setDrawingsSaved(dgg.getDrawingsSaved() + 1);
+
+            EscherDgRecord dg = null;
+            for(Iterator it = agg.getEscherContainer().getChildRecords().iterator(); it.hasNext();) {
+                Object er = it.next();
+                if(er instanceof EscherDgRecord) {
+                    dg = (EscherDgRecord)er;
+                    //update id of the drawing in the cloned sheet
+                    dg.setOptions( (short) ( dgId << 4 ) );
+                } else if (er instanceof EscherContainerRecord){
+                    //recursively find shape records and re-generate shapeId
+                    ArrayList spRecords = new ArrayList();
+                    EscherContainerRecord cp = (EscherContainerRecord)er;
+                    cp.getRecordsById(EscherSpRecord.RECORD_ID,  spRecords);
+                    for(Iterator spIt = spRecords.iterator(); spIt.hasNext();) {
+                        EscherSpRecord sp = (EscherSpRecord)spIt.next();
+                        int shapeId = drawingManager.allocateShapeId((short)dgId, dg);
+                        sp.setShapeId(shapeId);
+                    }
+                }
+            }
+
+        }
+    }
 }
index 71f3bf6fb7d471e1d6fa9a9638ce2c94a1d98a46..77296becd8529942eb288bb19b4c872d3c34aecd 100644 (file)
@@ -17,9 +17,8 @@
 
 package org.apache.poi.hssf.record;
 
-import java.util.Iterator;
+import java.util.ArrayList;
 import java.util.List;
-import java.util.Stack;
 
 import org.apache.poi.hssf.model.FormulaParser;
 import org.apache.poi.hssf.record.formula.Area3DPtg;
@@ -44,51 +43,33 @@ import org.apache.poi.util.StringUtil;
  */
 public final class NameRecord extends Record {
     public final static short sid = 0x0018;
-       /**Included for completeness sake, not implemented
-          */
-       public final static byte  BUILTIN_CONSOLIDATE_AREA      = (byte)1;
-       
-       /**Included for completeness sake, not implemented
-        */
-       public final static byte  BUILTIN_AUTO_OPEN             = (byte)2;
-
-       /**Included for completeness sake, not implemented
-        */
-       public final static byte  BUILTIN_AUTO_CLOSE            = (byte)3;
-
-       /**Included for completeness sake, not implemented
-        */
-       public final static byte  BUILTIN_DATABASE              = (byte)4;
-
-       /**Included for completeness sake, not implemented
-        */
-       public final static byte  BUILTIN_CRITERIA              = (byte)5;
-       
-       public final static byte  BUILTIN_PRINT_AREA            = (byte)6;
-       public final static byte  BUILTIN_PRINT_TITLE           = (byte)7;
-       
-       /**Included for completeness sake, not implemented
-        */
-       public final static byte  BUILTIN_RECORDER              = (byte)8;
-       
-       /**Included for completeness sake, not implemented
-        */
-       public final static byte  BUILTIN_DATA_FORM             = (byte)9;
-       
-       /**Included for completeness sake, not implemented
-        */
-
-       public final static byte  BUILTIN_AUTO_ACTIVATE         = (byte)10;
-       
-       /**Included for completeness sake, not implemented
-        */
+       /**Included for completeness sake, not implemented */
+       public final static byte  BUILTIN_CONSOLIDATE_AREA      = 1;
+       /**Included for completeness sake, not implemented */
+       public final static byte  BUILTIN_AUTO_OPEN             = 2;
+       /**Included for completeness sake, not implemented */
+       public final static byte  BUILTIN_AUTO_CLOSE            = 3;
+       /**Included for completeness sake, not implemented */
+       public final static byte  BUILTIN_DATABASE              = 4;
+       /**Included for completeness sake, not implemented */
+       public final static byte  BUILTIN_CRITERIA              = 5;
+
+       public final static byte  BUILTIN_PRINT_AREA            = 6;
+       public final static byte  BUILTIN_PRINT_TITLE           = 7;
+
+       /**Included for completeness sake, not implemented */
+       public final static byte  BUILTIN_RECORDER              = 8;
+       /**Included for completeness sake, not implemented */
+       public final static byte  BUILTIN_DATA_FORM             = 9;
+       /**Included for completeness sake, not implemented */
+       public final static byte  BUILTIN_AUTO_ACTIVATE         = 10;
+       /**Included for completeness sake, not implemented */
+       public final static byte  BUILTIN_AUTO_DEACTIVATE       = 11;
+       /**Included for completeness sake, not implemented */
+       public final static byte  BUILTIN_SHEET_TITLE           = 12;
+
+       public final static byte  BUILTIN_FILTER_DB             = 13;
 
-       public final static byte  BUILTIN_AUTO_DEACTIVATE       = (byte)11;
-       
-       /**Included for completeness sake, not implemented
-        */
-       public final static byte  BUILTIN_SHEET_TITLE           = (byte)12;
-       
        private static final class Option {
                public static final int OPT_HIDDEN_NAME =   0x0001;
                public static final int OPT_FUNCTION_NAME = 0x0002;
@@ -98,22 +79,16 @@ public final class NameRecord extends Record {
                public static final int OPT_BUILTIN =       0x0020;
                public static final int OPT_BINDATA =       0x1000;
        }
-       
+
        private short             field_1_option_flag;
        private byte              field_2_keyboard_shortcut;
-       private byte              field_3_length_name_text;
-       private short             field_4_length_name_definition;
        private short             field_5_index_to_sheet;     // unused: see field_6
        /** the one based sheet number.  Zero if this is a global name */
        private int               field_6_sheetNumber;
-       private byte              field_7_length_custom_menu;
-       private byte              field_8_length_description_text;
-       private byte              field_9_length_help_topic_text;
-       private byte              field_10_length_status_bar_text;
-       private byte              field_11_compressed_unicode_flag;   // not documented
-       private byte              field_12_builtIn_name;
+       private boolean           field_11_nameIsMultibyte;
+       private byte              field_12_built_in_code;
        private String            field_12_name_text;
-       private Stack             field_13_name_definition;
+       private Ptg[]             field_13_name_definition;
        private String            field_14_custom_menu_text;
        private String            field_15_description_text;
        private String            field_16_help_topic_text;
@@ -122,13 +97,13 @@ public final class NameRecord extends Record {
 
        /** Creates new NameRecord */
        public NameRecord() {
-               field_13_name_definition = new Stack();
+               field_13_name_definition = Ptg.EMPTY_PTG_ARRAY;
 
-               field_12_name_text = new String();
-               field_14_custom_menu_text = new String();
-               field_15_description_text = new String();
-               field_16_help_topic_text = new String();
-               field_17_status_bar_text = new String();
+               field_12_name_text = "";
+               field_14_custom_menu_text = "";
+               field_15_description_text = "";
+               field_16_help_topic_text = "";
+               field_17_status_bar_text = "";
        }
 
        /**
@@ -146,19 +121,10 @@ public final class NameRecord extends Record {
         */
        public NameRecord(byte builtin, int sheetNumber)
        {
-               this();         
-               this.field_12_builtIn_name = builtin;
-               this.setOptionFlag((short)(this.field_1_option_flag | Option.OPT_BUILTIN));
-               this.setNameTextLength((byte)1);
+               this();
+               field_12_built_in_code = builtin;
+               setOptionFlag((short)(field_1_option_flag | Option.OPT_BUILTIN));
                field_6_sheetNumber = sheetNumber; //the extern sheets are set through references
-               
-               //clearing these because they are not used with builtin records
-               this.setCustomMenuLength((byte)0);
-               this.setDescriptionTextLength((byte)0);
-               this.setHelpTopicLength((byte)0);
-               this.setStatusBarLength((byte)0);
-
-               
        }
 
        /** sets the option flag for the named range
@@ -176,34 +142,9 @@ public final class NameRecord extends Record {
                field_2_keyboard_shortcut = shortcut;
        }
 
-       /** sets the name of the named range length
-        * @param length name length
-        */
-       public void setNameTextLength(byte length){
-               field_3_length_name_text = length;
-       }
-
-       /** sets the definition (reference - formula) length
-        * @param length defenition length
-        */
-       public void setDefinitionTextLength(short length){
-               field_4_length_name_definition = length;
-       }
-
-       /** sets the index number to the extern sheet (thats is what writen in documentation
-        *  but as i saw , it works differently)
-        * @param index extern sheet index
-        */
-       public void setUnused(short index){
-               field_5_index_to_sheet = index;
-
-               // field_6_equals_to_index_to_sheet is equal to field_5_index_to_sheet
-//        field_6_equals_to_index_to_sheet = index;
-       }
-
        /**
         * For named ranges, and built-in names
-        * @return the 1-based sheet number.  Zero if this is a global name 
+        * @return the 1-based sheet number.  Zero if this is a global name
         */
        public int getSheetNumber()
        {
@@ -226,49 +167,12 @@ public final class NameRecord extends Record {
        }
 
 
-       /** sets the custom menu length
-        * @param length custom menu length
-        */
-       public void setCustomMenuLength(byte length){
-               field_7_length_custom_menu = length;
-       }
-
-       /** sets the length of named range description
-        * @param length description length
-        */
-       public void setDescriptionTextLength(byte length){
-               field_8_length_description_text = length;
-       }
-
-       /** sets the help topic length
-        * @param length help topic length
-        */
-       public void setHelpTopicLength(byte length){
-               field_9_length_help_topic_text = length;
-       }
-
-       /** sets the length of the status bar text
-        * @param length status bar text length
-        */
-       public void setStatusBarLength(byte length){
-               field_10_length_status_bar_text = length;
-       }
-
-       /** sets the compressed unicode flag
-        * @param flag unicode flag
-        */
-       public void setCompressedUnicodeFlag(byte flag) {
-               field_11_compressed_unicode_flag = flag;
-       }
-
        /** sets the name of the named range
         * @param name named range name
         */
        public void setNameText(String name){
                field_12_name_text = name;
-               setCompressedUnicodeFlag(
-                       StringUtil.hasMultibyte(name) ? (byte)1 : (byte)0
-               );
+               field_11_nameIsMultibyte = StringUtil.hasMultibyte(name);
        }
 
        /** sets the custom menu text
@@ -313,72 +217,15 @@ public final class NameRecord extends Record {
                return field_2_keyboard_shortcut ;
        }
 
-       /** 
+       /**
         * gets the name length, in characters
         * @return name length
         */
-       public byte getNameTextLength(){
-               return field_3_length_name_text;
-       }
-       
-       /** 
-        * gets the name length, in bytes
-        * @return raw name length
-        */
-       public byte getRawNameTextLength(){
-               if( (field_11_compressed_unicode_flag & 0x01) == 1 ) {
-                       return (byte)(2 * field_3_length_name_text);
+       private int getNameTextLength(){
+               if (isBuiltInName()) {
+                       return 1;
                }
-               return field_3_length_name_text;
-       }
-
-       /** get the definition length
-        * @return definition length
-        */
-       public short getDefinitionLength(){
-               return field_4_length_name_definition;
-       }
-
-       /** gets the index to extern sheet
-        * @return index to extern sheet
-        */
-       public short getUnused(){
-               return field_5_index_to_sheet;
-       }
-
-       /** gets the custom menu length
-        * @return custom menu length
-        */
-       public byte getCustomMenuLength(){
-               return field_7_length_custom_menu;
-       }
-
-       /** gets the description text length
-        * @return description text length
-        */
-       public byte getDescriptionTextLength(){
-               return field_8_length_description_text;
-       }
-
-       /** gets the help topic length
-        * @return help topic length
-        */
-       public byte getHelpTopicLength(){
-               return field_9_length_help_topic_text;
-       }
-
-       /** get the status bar text length
-        * @return satus bar length
-        */
-       public byte getStatusBarLength(){
-               return field_10_length_status_bar_text;
-       }
-
-       /** gets the name compressed Unicode flag
-        * @return compressed unicode flag
-        */
-       public byte getCompressedUnicodeFlag() {
-               return field_11_compressed_unicode_flag;
+               return field_12_name_text.length();
        }
 
 
@@ -388,13 +235,26 @@ public final class NameRecord extends Record {
        public boolean isHiddenName() {
                return (field_1_option_flag & Option.OPT_HIDDEN_NAME) != 0;
        }
+       public void setHidden(boolean b) {
+               if (b) {
+                       field_1_option_flag |= Option.OPT_HIDDEN_NAME;
+               } else {
+                       field_1_option_flag &= (~Option.OPT_HIDDEN_NAME);
+               }
+       }
        /**
-        * @return true if name is a function
+        * @return <code>true</code> if name is a function
         */
        public boolean isFunctionName() {
                return (field_1_option_flag & Option.OPT_FUNCTION_NAME) != 0;
        }
 
+       /**
+        * @return <code>true</code> if name has a formula (named range or defined value)
+        */
+       public boolean hasFormula() {
+               return field_1_option_flag == 0 && field_13_name_definition.length > 0;
+       }
 
        /**
         * @return true if name is a command
@@ -419,7 +279,7 @@ public final class NameRecord extends Record {
         */
        public boolean isBuiltInName()
        {
-               return ((this.field_1_option_flag & Option.OPT_BUILTIN) != 0);
+               return ((field_1_option_flag & Option.OPT_BUILTIN) != 0);
        }
 
 
@@ -428,7 +288,7 @@ public final class NameRecord extends Record {
         */
        public String getNameText(){
 
-               return this.isBuiltInName() ? this.translateBuiltInName(this.getBuiltInName()) : field_12_name_text;
+               return isBuiltInName() ? translateBuiltInName(getBuiltInName()) : field_12_name_text;
        }
 
        /** Gets the Built In Name
@@ -436,19 +296,19 @@ public final class NameRecord extends Record {
         */
        public byte getBuiltInName()
        {
-               return this.field_12_builtIn_name;
+               return field_12_built_in_code;
        }
 
 
        /** gets the definition, reference (Formula)
-        * @return definition -- can be null if we cant parse ptgs
+        * @return the name formula. never <code>null</code>
         */
-       public List getNameDefinition() {
-               return field_13_name_definition;
+       public Ptg[] getNameDefinition() {
+               return (Ptg[]) field_13_name_definition.clone();
        }
 
-       public void setNameDefinition(Stack nameDefinition) {
-               field_13_name_definition = nameDefinition;
+       public void setNameDefinition(Ptg[] ptgs) {
+               field_13_name_definition = (Ptg[]) ptgs.clone();
        }
 
        /** get the custom menu text
@@ -490,7 +350,8 @@ public final class NameRecord extends Record {
                        throw new RecordFormatException("NOT A valid Name RECORD");
                }
        }
-       
+
+
        /**
         * called by the class that is responsible for writing this sucker.
         * Subclasses should implement this so that their data is passed back in a
@@ -498,109 +359,107 @@ public final class NameRecord extends Record {
         * @param data byte array containing instance data
         * @return number of bytes written
         */
-       public int serialize( int offset, byte[] data )
-       {
-               LittleEndian.putShort( data, 0 + offset, sid );
-               short size = (short)( 15 + getTextsLength() + getNameDefinitionSize());
-               LittleEndian.putShort( data, 2 + offset, size );
-               // size defined below
-               LittleEndian.putShort( data, 4 + offset, getOptionFlag() );
-               data[6 + offset] = getKeyboardShortcut();
-               data[7 + offset] = getNameTextLength();
-               LittleEndian.putShort( data, 8 + offset, getDefinitionLength() );
-               LittleEndian.putShort( data, 10 + offset, getUnused() );
-               LittleEndian.putUShort( data, 12 + offset, field_6_sheetNumber);
-               data[14 + offset] = getCustomMenuLength();
-               data[15 + offset] = getDescriptionTextLength();
-               data[16 + offset] = getHelpTopicLength();
-               data[17 + offset] = getStatusBarLength();
-               data[18 + offset] = getCompressedUnicodeFlag();
-
-                       int start_of_name_definition = 19 + field_3_length_name_text;
-
-                       if (this.isBuiltInName()) {
-                               //can send the builtin name directly in
-                               data [19 + offset] =  this.getBuiltInName();
-                       } else if ((this.getCompressedUnicodeFlag() & 0x01) == 1) {
-                               StringUtil.putUnicodeLE( getNameText(), data, 19 + offset );
-                               start_of_name_definition = 19 + (2 * field_3_length_name_text);
-                       } else {
-                               StringUtil.putCompressedUnicode( getNameText(), data, 19 + offset );
-                       }
-
-
-                       Ptg.serializePtgStack(field_13_name_definition,  data, start_of_name_definition + offset );
-
-
-                       int start_of_custom_menu_text = start_of_name_definition + field_4_length_name_definition;
-                       StringUtil.putCompressedUnicode( getCustomMenuText(), data, start_of_custom_menu_text + offset );
-
-                       int start_of_description_text = start_of_custom_menu_text + field_7_length_custom_menu;
-                       StringUtil.putCompressedUnicode( getDescriptionText(), data, start_of_description_text + offset );
-
-                       int start_of_help_topic_text = start_of_description_text + field_8_length_description_text;
-                       StringUtil.putCompressedUnicode( getHelpTopicText(), data, start_of_help_topic_text + offset );
-
-                       int start_of_status_bar_text = start_of_help_topic_text + field_9_length_help_topic_text;
-                       StringUtil.putCompressedUnicode( getStatusBarText(), data, start_of_status_bar_text + offset );
-
-                       return getRecordSize();
-               /* } */
-       }
-
-       /** 
-        * Gets the length of all texts, in bytes
-        * @return total length
-        */
-       public int getTextsLength(){
-               int result;
-
-               result = getRawNameTextLength() + getDescriptionTextLength() +
-                       getHelpTopicLength() + getStatusBarLength();
+       public int serialize( int offset, byte[] data ) {
 
-               return result;
-       }
-       
-       private int getNameDefinitionSize() {
-               int result = 0;
-               List list   = field_13_name_definition;
+               int field_7_length_custom_menu = field_14_custom_menu_text.length();
+               int field_8_length_description_text = field_15_description_text.length();
+               int field_9_length_help_topic_text = field_16_help_topic_text.length();
+               int field_10_length_status_bar_text = field_17_status_bar_text.length();
+               int rawNameSize = getNameRawSize();
                
-               for (int k = 0; k < list.size(); k++)
-               {
-                       Ptg ptg = ( Ptg ) list.get(k);
-                       
-                       result += ptg.getSize();
+               int formulaTotalSize = Ptg.getEncodedSize(field_13_name_definition);
+               int dataSize = 15 // 4 shorts + 7 bytes
+                       + rawNameSize
+                       + field_7_length_custom_menu
+                       + field_8_length_description_text
+                       + field_9_length_help_topic_text
+                       + field_10_length_status_bar_text
+                       + formulaTotalSize;
+               
+               LittleEndian.putShort(data, 0 + offset, sid);
+               LittleEndian.putUShort(data, 2 + offset, dataSize);
+               // size defined below
+               LittleEndian.putShort(data, 4 + offset, getOptionFlag());
+               LittleEndian.putByte(data, 6 + offset, getKeyboardShortcut());
+               LittleEndian.putByte(data, 7 + offset, getNameTextLength());
+               // Note -
+               LittleEndian.putUShort(data, 8 + offset, Ptg.getEncodedSizeWithoutArrayData(field_13_name_definition));
+               LittleEndian.putUShort(data, 10 + offset, field_5_index_to_sheet);
+               LittleEndian.putUShort(data, 12 + offset, field_6_sheetNumber);
+               LittleEndian.putByte(data, 14 + offset, field_7_length_custom_menu);
+               LittleEndian.putByte(data, 15 + offset, field_8_length_description_text);
+               LittleEndian.putByte(data, 16 + offset, field_9_length_help_topic_text);
+               LittleEndian.putByte(data, 17 + offset, field_10_length_status_bar_text);
+               LittleEndian.putByte(data, 18 + offset, field_11_nameIsMultibyte ? 1 : 0);
+               int pos = 19 + offset;
+
+               if (isBuiltInName()) {
+                       //can send the builtin name directly in
+                       LittleEndian.putByte(data, pos,  field_12_built_in_code);
+               } else {
+                       String nameText = field_12_name_text;
+                       if (field_11_nameIsMultibyte) {
+                       StringUtil.putUnicodeLE(nameText, data, pos);
+               } else {
+                       StringUtil.putCompressedUnicode(nameText, data, pos);
+               }
                }
-               return result;    
+               pos += rawNameSize;
+
+               Ptg.serializePtgs(field_13_name_definition,  data, pos);
+               pos += formulaTotalSize;
+               
+               StringUtil.putCompressedUnicode( getCustomMenuText(), data, pos);
+               pos += field_7_length_custom_menu;
+               StringUtil.putCompressedUnicode( getDescriptionText(), data, pos);
+               pos += field_8_length_description_text;
+               StringUtil.putCompressedUnicode( getHelpTopicText(), data, pos);
+               pos += field_9_length_help_topic_text;
+               StringUtil.putCompressedUnicode( getStatusBarText(), data, pos);
+
+               return 4 + dataSize;
+       }
+       private int getNameRawSize() {
+               if (isBuiltInName()) {
+                       return 1;
+               } 
+               int nChars = field_12_name_text.length();
+               if(field_11_nameIsMultibyte) {
+                       return 2 * nChars;
+               } 
+               return nChars;
        }
 
        /** returns the record size
         */
        public int getRecordSize(){
-               int result;
-
-               result = 19 + getTextsLength() + getNameDefinitionSize();
-               
-
-               return result;
+               return 4 // sid + size
+                       + 15 // 4 shorts + 7 bytes
+                       + getNameRawSize()
+                       + field_14_custom_menu_text.length()
+                       + field_15_description_text.length()
+                       + field_16_help_topic_text.length()
+                       + field_17_status_bar_text.length()
+                       + Ptg.getEncodedSize(field_13_name_definition);
        }
 
        /** gets the extern sheet number
         * @return extern sheet index
         */
        public short getExternSheetNumber(){
-               if (field_13_name_definition == null || field_13_name_definition.isEmpty()) return 0;
-               Ptg ptg = (Ptg) field_13_name_definition.peek();
-               short result = 0;
+               if (field_13_name_definition.length < 1) {
+                       return 0;
+               }
+               Ptg ptg = field_13_name_definition[0];
 
                if (ptg.getClass() == Area3DPtg.class){
-                       result = ((Area3DPtg) ptg).getExternSheetIndex();
+                       return ((Area3DPtg) ptg).getExternSheetIndex();
 
-               } else if (ptg.getClass() == Ref3DPtg.class){
-                       result = ((Ref3DPtg) ptg).getExternSheetIndex();
                }
-
-               return result;
+               if (ptg.getClass() == Ref3DPtg.class){
+                       return ((Ref3DPtg) ptg).getExternSheetIndex();
+               }
+               return 0;
        }
 
        /** sets the extern sheet number
@@ -609,11 +468,13 @@ public final class NameRecord extends Record {
        public void setExternSheetNumber(short externSheetNumber){
                Ptg ptg;
 
-               if (field_13_name_definition == null || field_13_name_definition.isEmpty()){
-                       field_13_name_definition = new Stack();
+               if (field_13_name_definition.length < 1){
                        ptg = createNewPtg();
+                       field_13_name_definition = new Ptg[] {
+                               ptg,
+                       };
                } else {
-                       ptg = (Ptg) field_13_name_definition.peek();
+                       ptg = field_13_name_definition[0];
                }
 
                if (ptg.getClass() == Area3DPtg.class){
@@ -625,11 +486,8 @@ public final class NameRecord extends Record {
 
        }
 
-       private Ptg createNewPtg(){
-               Ptg ptg = new Area3DPtg("A1", 0); // TODO - change to not be partially initialised
-               field_13_name_definition.push(ptg);
-
-               return ptg;
+       private static Ptg createNewPtg(){
+               return new Area3DPtg("A1:A1", 0); // TODO - change to not be partially initialised
        }
 
        /** gets the reference , the area only (range)
@@ -646,16 +504,14 @@ public final class NameRecord extends Record {
                //Trying to find if what ptg do we need
                RangeAddress ra = new RangeAddress(ref);
                Ptg oldPtg;
-               Ptg ptg;
 
-               if (field_13_name_definition==null ||field_13_name_definition.isEmpty()){
-                       field_13_name_definition = new Stack();
+               if (field_13_name_definition.length < 1){
                        oldPtg = createNewPtg();
                } else {
                        //Trying to find extern sheet index
-                       oldPtg = (Ptg) field_13_name_definition.pop();
+                       oldPtg = field_13_name_definition[0];
                }
-
+               List temp = new ArrayList();
                short externSheetIndex = 0;
 
                if (oldPtg.getClass() == Area3DPtg.class){
@@ -667,29 +523,27 @@ public final class NameRecord extends Record {
 
                if (ra.hasRange()) {
                        // Is it contiguous or not?
-                       AreaReference[] refs = 
-                               AreaReference.generateContiguous(ref);
-                       this.setDefinitionTextLength((short)0);
+                       AreaReference[] refs = AreaReference.generateContiguous(ref);
 
-                       // Add the area reference(s) 
+                       // Add the area reference(s)
                        for(int i=0; i<refs.length; i++) {
-                               ptg = new Area3DPtg(refs[i].formatAsString(), externSheetIndex);
-                               field_13_name_definition.push(ptg);
-                               this.setDefinitionTextLength( (short)(getDefinitionLength() + ptg.getSize()) );
+                               Ptg ptg = new Area3DPtg(refs[i].formatAsString(), externSheetIndex);
+                               temp.add(ptg);
                        }
                        // And then a union if we had more than one area
                        if(refs.length > 1) {
-                               ptg = UnionPtg.instance;
-                               field_13_name_definition.push(ptg);
-                               this.setDefinitionTextLength( (short)(getDefinitionLength() + ptg.getSize()) );
+                               Ptg ptg = UnionPtg.instance;
+                               temp.add(ptg);
                        }
                } else {
-                       ptg = new Ref3DPtg();
+                       Ptg ptg = new Ref3DPtg();
                        ((Ref3DPtg) ptg).setExternSheetIndex(externSheetIndex);
                        ((Ref3DPtg) ptg).setArea(ref);
-                       field_13_name_definition.push(ptg);
-                       this.setDefinitionTextLength((short)ptg.getSize());
+                       temp.add(ptg);
                }
+               Ptg[] ptgs = new Ptg[temp.size()];
+               temp.toArray(ptgs);
+               field_13_name_definition = ptgs;
        }
 
        /**
@@ -699,40 +553,36 @@ public final class NameRecord extends Record {
         * @param in the RecordInputstream to read the record from
         */
        protected void fillFields(RecordInputStream in) {
-               field_1_option_flag             = in.readShort();
-               field_2_keyboard_shortcut       = in.readByte();
-               field_3_length_name_text        = in.readByte();
-               field_4_length_name_definition  = in.readShort();
-               field_5_index_to_sheet          = in.readShort();
-               field_6_sheetNumber = in.readUShort();
-               field_7_length_custom_menu      = in.readByte();
-               field_8_length_description_text = in.readByte();
-               field_9_length_help_topic_text  = in.readByte();
-               field_10_length_status_bar_text = in.readByte();
-                       
-               //store the name in byte form if it's a builtin name
-               field_11_compressed_unicode_flag= in.readByte();        
-               if (this.isBuiltInName()) {
-                  field_12_builtIn_name = in.readByte();
-               } else {                
-                  if (field_11_compressed_unicode_flag == 1) {
-                        field_12_name_text = in.readUnicodeLEString(field_3_length_name_text);
-                  } else {
-                        field_12_name_text = in.readCompressedUnicode(field_3_length_name_text);
-                  }
+               field_1_option_flag                 = in.readShort();
+               field_2_keyboard_shortcut           = in.readByte();
+               int field_3_length_name_text        = in.readByte();
+               int field_4_length_name_definition  = in.readShort();
+               field_5_index_to_sheet              = in.readShort();
+               field_6_sheetNumber                 = in.readUShort();
+               int field_7_length_custom_menu      = in.readUByte();
+               int field_8_length_description_text = in.readUByte();
+               int field_9_length_help_topic_text  = in.readUByte();
+               int field_10_length_status_bar_text = in.readUByte();
+
+               //store the name in byte form if it's a built-in name
+               field_11_nameIsMultibyte = (in.readByte() != 0);
+               if (isBuiltInName()) {
+                       field_12_built_in_code = in.readByte();
+               } else {
+                       if (field_11_nameIsMultibyte) {
+                               field_12_name_text = in.readUnicodeLEString(field_3_length_name_text);
+                       } else {
+                               field_12_name_text = in.readCompressedUnicode(field_3_length_name_text);
+                       }
                }
-                       
-               field_13_name_definition = Ptg.createParsedExpressionTokens(field_4_length_name_definition, in);
-       
+
+               field_13_name_definition = Ptg.readTokens(field_4_length_name_definition, in);
+
                //Who says that this can only ever be compressed unicode???
-               field_14_custom_menu_text       = in.readCompressedUnicode(LittleEndian.ubyteToInt(field_7_length_custom_menu));
-       
-               field_15_description_text       = in.readCompressedUnicode(LittleEndian.ubyteToInt(field_8_length_description_text));
-       
-               field_16_help_topic_text        = in.readCompressedUnicode(LittleEndian.ubyteToInt(field_9_length_help_topic_text));
-       
-               field_17_status_bar_text        = in.readCompressedUnicode(LittleEndian.ubyteToInt(field_10_length_status_bar_text));
-               /*} */
+               field_14_custom_menu_text = in.readCompressedUnicode(field_7_length_custom_menu);
+               field_15_description_text = in.readCompressedUnicode(field_8_length_description_text);
+               field_16_help_topic_text  = in.readCompressedUnicode(field_9_length_help_topic_text);
+               field_17_status_bar_text  = in.readCompressedUnicode(field_10_length_status_bar_text);
        }
 
        /**
@@ -742,113 +592,90 @@ public final class NameRecord extends Record {
                return sid;
        }
        /*
-         20 00 
-         00 
-         01 
+         20 00
+         00
+         01
          1A 00 // sz = 0x1A = 26
-         00 00 
-         01 00 
-         00 
-         00 
-         00 
-         00 
+         00 00
+         01 00
+         00
+         00
+         00
+         00
          00 // unicode flag
          07 // name
-         
+
          29 17 00 3B 00 00 00 00 FF FF 00 00 02 00 3B 00 //{ 26
          00 07 00 07 00 00 00 FF 00 10                   //  }
-         
-         
-         
-         20 00 
-         00 
-         01 
+
+
+
+         20 00
+         00
+         01
          0B 00 // sz = 0xB = 11
-         00 00 
-         01 00 
-         00 
-         00 
-         00 
-         00 
+         00 00
+         01 00
+         00
+         00
+         00
+         00
          00 // unicode flag
          07 // name
-         
+
          3B 00 00 07 00 07 00 00 00 FF 00   // { 11 }
   */
        /*
-         18, 00, 
-         1B, 00, 
-         
-         20, 00, 
-         00, 
-         01, 
-         0B, 00, 
-         00, 
-         00, 
-         00, 
-         00, 
-         00, 
-         07, 
-         3B 00 00 07 00 07 00 00 00 FF 00 ]     
+         18, 00,
+         1B, 00,
+
+         20, 00,
+         00,
+         01,
+         0B, 00,
+         00,
+         00,
+         00,
+         00,
+         00,
+         07,
+         3B 00 00 07 00 07 00 00 00 FF 00 ]
         */
 
-       /**
-        * @see Object#toString()
-        */
        public String toString() {
-               StringBuffer buffer = new StringBuffer();
-
-               buffer.append("[NAME]\n");
-               buffer.append("    .option flags         = ").append( HexDump.toHex( field_1_option_flag ) )
-                       .append("\n");
-               buffer.append("    .keyboard shortcut    = ").append( HexDump.toHex( field_2_keyboard_shortcut ) )
-                       .append("\n");
-               buffer.append("    .length of the name   = ").append( field_3_length_name_text )
-                       .append("\n");
-               buffer.append("    .size of the formula data = ").append( field_4_length_name_definition )
-                       .append("\n");
-               buffer.append("    .unused                   = ").append( field_5_index_to_sheet )
-                       .append("\n");
-               buffer.append("    .index to sheet (1-based, 0=Global)           = ").append( field_6_sheetNumber )
-                       .append("\n");
-               buffer.append("    .Length of menu text (character count)        = ").append( field_7_length_custom_menu )
-                       .append("\n");
-               buffer.append("    .Length of description text (character count) = ").append( field_8_length_description_text )
-                       .append("\n");
-               buffer.append("    .Length of help topic text (character count)  = ").append( field_9_length_help_topic_text )
-                       .append("\n");
-               buffer.append("    .Length of status bar text (character count)  = ").append( field_10_length_status_bar_text )
-                       .append("\n");
-               buffer.append("    .Name (Unicode flag)  = ").append( field_11_compressed_unicode_flag )
-                       .append("\n");
-               buffer.append("    .Name (Unicode text)  = ").append( getNameText() )
-                       .append("\n");
-               
-               buffer.append("    .Parts (" + field_13_name_definition.size() +"):")
-                       .append("\n");
-               Iterator it = field_13_name_definition.iterator();
-               while(it.hasNext()) {
-                       Ptg ptg = (Ptg)it.next();
-                       buffer.append("       " + ptg.toString()).append("\n");
+               StringBuffer sb = new StringBuffer();
+
+               sb.append("[NAME]\n");
+               sb.append("    .option flags           = ").append(HexDump.shortToHex(field_1_option_flag)).append("\n");
+               sb.append("    .keyboard shortcut      = ").append(HexDump.byteToHex(field_2_keyboard_shortcut)).append("\n");
+               sb.append("    .length of the name     = ").append(getNameTextLength()).append("\n");
+               sb.append("    .unused                 = ").append( field_5_index_to_sheet ).append("\n");
+               sb.append("    .index to sheet (1-based, 0=Global) = ").append( field_6_sheetNumber ).append("\n");
+               sb.append("    .Menu text length       = ").append(field_14_custom_menu_text.length()).append("\n");
+               sb.append("    .Description text length= ").append(field_15_description_text.length()).append("\n");
+               sb.append("    .Help topic text length = ").append(field_16_help_topic_text.length()).append("\n");
+               sb.append("    .Status bar text length = ").append(field_17_status_bar_text.length()).append("\n");
+               sb.append("    .NameIsMultibyte        = ").append(field_11_nameIsMultibyte).append("\n");
+               sb.append("    .Name (Unicode text)    = ").append( getNameText() ).append("\n");
+               sb.append("    .Formula (nTokens=").append(field_13_name_definition.length).append("):") .append("\n");
+               for (int i = 0; i < field_13_name_definition.length; i++) {
+                       Ptg ptg = field_13_name_definition[i];
+                       sb.append("       " + ptg.toString()).append(ptg.getRVAType()).append("\n");
                }
-               
-               buffer.append("    .Menu text (Unicode string without length field)        = ").append( field_14_custom_menu_text )
-                       .append("\n");
-               buffer.append("    .Description text (Unicode string without length field) = ").append( field_15_description_text )
-                       .append("\n");
-               buffer.append("    .Help topic text (Unicode string without length field)  = ").append( field_16_help_topic_text )
-                       .append("\n");
-               buffer.append("    .Status bar text (Unicode string without length field)  = ").append( field_17_status_bar_text )
-                       .append("\n");
-               buffer.append("[/NAME]\n");
-               
-               return buffer.toString();
+
+               sb.append("    .Menu text       = ").append(field_14_custom_menu_text).append("\n");
+               sb.append("    .Description text= ").append(field_15_description_text).append("\n");
+               sb.append("    .Help topic text = ").append(field_16_help_topic_text).append("\n");
+               sb.append("    .Status bar text = ").append(field_17_status_bar_text).append("\n");
+               sb.append("[/NAME]\n");
+
+               return sb.toString();
        }
 
        /**Creates a human readable name for built in types
         * @return Unknown if the built-in name cannot be translated
         */
-       protected String translateBuiltInName(byte name)
+       private static String translateBuiltInName(byte name)
        {
                switch (name)
                {
@@ -859,14 +686,15 @@ public final class NameRecord extends Record {
                        case NameRecord.BUILTIN_CONSOLIDATE_AREA :  return "Consolidate_Area";
                        case NameRecord.BUILTIN_CRITERIA :          return "Criteria";
                        case NameRecord.BUILTIN_DATABASE :          return "Database";
-                       case NameRecord.BUILTIN_DATA_FORM :         return "Data_Form";            
+                       case NameRecord.BUILTIN_DATA_FORM :         return "Data_Form";
                        case NameRecord.BUILTIN_PRINT_AREA :        return "Print_Area";
                        case NameRecord.BUILTIN_PRINT_TITLE :       return "Print_Titles";
                        case NameRecord.BUILTIN_RECORDER :          return "Recorder";
                        case NameRecord.BUILTIN_SHEET_TITLE :       return "Sheet_Title";
-                       
+                       case NameRecord.BUILTIN_FILTER_DB  :        return "_FilterDatabase";
+
                }
-               
+
                return "Unknown";
        }
 }
index 5ea1061ba7745f0b858f16994133a4d326bde1f2..49994c059752037b79d8b4162a21095e28cc7cb3 100644 (file)
@@ -94,10 +94,14 @@ public final class ArrayPtg extends Ptg {
 
                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"); 
+               if (token_3_arrayValues == null) {
+                       buffer.append("  #values#uninitialised#\n");
+               } else {
+                       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();
index 6b48036fe7e3488b2057cc2c9575894f33a2807d..dbb00c323a74b3b3b4842c2c758efe5dd8790d7d 100644 (file)
@@ -41,7 +41,7 @@ public final class Ref3DPtg extends OperandPtg {
     private static final BitField colRelative = BitFieldFactory.getInstance(0x4000);
 
     private final static int  SIZE = 7; // 6 + 1 for Ptg
-    private short             field_1_index_extern_sheet;
+    private int             field_1_index_extern_sheet;
     /** The row index - zero based unsigned 16 bit value */
     private int            field_2_row;
     /** Field 2 
@@ -93,10 +93,10 @@ public final class Ref3DPtg extends OperandPtg {
     }
 
     public short getExternSheetIndex(){
-        return field_1_index_extern_sheet;
+        return (short)field_1_index_extern_sheet;
     }
 
-    public void setExternSheetIndex(short index){
+    public void setExternSheetIndex(int index){
         field_1_index_extern_sheet = index;
     }
 
index e5ec7fa8341a43d394ed5d85091052e4ad2951ed..87a6462b7deb2477e27f79f60f539cfd46b51690 100644 (file)
@@ -109,8 +109,8 @@ public final class AnalysisToolPak {
                r(m, "IMSUB", null);
                r(m, "IMSUM", null);
                r(m, "INTRATE", null);
-               r(m, "ISEVEN", null);
-               r(m, "ISODD", null);
+               r(m, "ISEVEN", ParityFunction.IS_EVEN);
+               r(m, "ISODD", ParityFunction.IS_ODD);
                r(m, "LCM", null);
                r(m, "MDURATION", null);
                r(m, "MROUND", null);
diff --git a/src/java/org/apache/poi/hssf/record/formula/atp/ParityFunction.java b/src/java/org/apache/poi/hssf/record/formula/atp/ParityFunction.java
new file mode 100644 (file)
index 0000000..9dd6d09
--- /dev/null
@@ -0,0 +1,74 @@
+/* ====================================================================
+   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.atp;
+
+import org.apache.poi.hssf.record.formula.eval.BlankEval;
+import org.apache.poi.hssf.record.formula.eval.BoolEval;
+import org.apache.poi.hssf.record.formula.eval.ErrorEval;
+import org.apache.poi.hssf.record.formula.eval.Eval;
+import org.apache.poi.hssf.record.formula.eval.EvaluationException;
+import org.apache.poi.hssf.record.formula.eval.OperandResolver;
+import org.apache.poi.hssf.record.formula.eval.ValueEval;
+import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+/**
+ * Implementation of Excel 'Analysis ToolPak' function ISEVEN() ISODD()<br/>
+ * 
+ * @author Josh Micich
+ */
+final class ParityFunction implements FreeRefFunction {
+
+       public static final FreeRefFunction IS_EVEN = new ParityFunction(0);
+       public static final FreeRefFunction IS_ODD = new ParityFunction(1);
+       private final int _desiredParity;
+       
+       private ParityFunction(int desiredParity) {
+               _desiredParity = desiredParity;
+       }
+
+       public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, Workbook workbook,
+                       Sheet sheet) {
+               if (args.length != 1) {
+                       return ErrorEval.VALUE_INVALID;  
+               }
+               
+               int val;
+               try {
+                       val = evaluateArgParity(args[0], srcCellRow, srcCellCol);
+               } catch (EvaluationException e) {
+                       return e.getErrorEval();
+               }
+
+               return BoolEval.valueOf(val == _desiredParity);
+       }
+
+       private static int evaluateArgParity(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException {
+               ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
+               
+               if (ve == BlankEval.INSTANCE) {
+                       return 0;
+               }
+               double d = OperandResolver.coerceValueToDouble(ve);
+               if (d < 0) {
+                       d = -d;
+               }
+               long v = (long) Math.floor(d);
+               return (int) (v & 0x0001);
+       }
+}
index 517189d407601863776abeffaf92ce012f66aebd..96718ef4ecd717934c9930e0c611f4945797caea 100644 (file)
@@ -21,6 +21,7 @@ import java.util.Calendar;
 import java.util.GregorianCalendar;
 import java.util.regex.Pattern;
 
+import org.apache.poi.hssf.record.formula.eval.BlankEval;
 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;
@@ -96,6 +97,9 @@ final class YearFrac implements FreeRefFunction {
                        Calendar date = parseDate(strVal);
                        return DateUtil.getExcelDate(date, false);
                }
+               if (ve instanceof BlankEval) {
+                       return 0.0;
+               }
                return OperandResolver.coerceValueToDouble(ve);
        }
 
@@ -120,7 +124,7 @@ final class YearFrac implements FreeRefFunction {
                } catch (NumberFormatException e) {
                        throw new EvaluationException(ErrorEval.VALUE_INVALID);
                }
-               if (f0<0 || f1<0 || f2<0 || f0>12 || f1>12 || f2>12) {
+               if (f0<0 || f1<0 || f2<0 || (f0>12 && f1>12 && f2>12)) {
                        // easy to see this cannot be a valid date
                        throw new EvaluationException(ErrorEval.VALUE_INVALID);
                }
@@ -150,6 +154,7 @@ final class YearFrac implements FreeRefFunction {
                if (day <1 || day>cal.getActualMaximum(Calendar.DAY_OF_MONTH)) {
                        throw new EvaluationException(ErrorEval.VALUE_INVALID);
                }
+               cal.set(Calendar.DAY_OF_MONTH, day);
                return cal;
        }
 
index 6562263d5474b8acda1fc5322e707fedc3c2ce10..bf1b424214369b5f8265a4d9f3519e369b57327f 100644 (file)
@@ -1,27 +1,22 @@
-/*
-* 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 8, 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.eval;
 
-import org.apache.poi.hssf.record.formula.AddPtg;
-import org.apache.poi.hssf.record.formula.Ptg;
 
 /**
  * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
@@ -37,58 +32,14 @@ import org.apache.poi.hssf.record.formula.Ptg;
  * <li> 1+A1 = 2 if A1 contains TRUE or =TRUE
  * <li> 1+A1 = #VALUE! if A1 contains "TRUE" or ="TRUE"
  */
-public class AddEval extends NumericOperationEval {
-
-    private AddPtg delegate;
-    private static final ValueEvalToNumericXlator NUM_XLATOR = 
-        new ValueEvalToNumericXlator((short)
-                ( ValueEvalToNumericXlator.BOOL_IS_PARSED 
-                | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
-                | ValueEvalToNumericXlator.STRING_IS_PARSED
-                | ValueEvalToNumericXlator.REF_STRING_IS_PARSED
-                ));
-
-    public AddEval(Ptg ptg) {
-        delegate = (AddPtg) ptg;
-    }
+public final class AddEval extends TwoOperandNumericOperation {
 
-    public ValueEvalToNumericXlator getXlator() {
-        return NUM_XLATOR;
-    }
-    
-    
-    public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
-       if(args.length != 2) {
-               return ErrorEval.VALUE_INVALID;
-       }
-       
-        double d = 0;
-        for (int i = 0; i < 2; i++) {
-            ValueEval ve = singleOperandEvaluate(args[i], srcRow, srcCol);
-            if(ve instanceof ErrorEval) {
-                               return ve;
-            }
-            if (ve instanceof NumericValueEval) {
-                d += ((NumericValueEval) ve).getNumberValue();
-            }
-            else if (ve instanceof BlankEval) {
-                // do nothing
-            }
-            else {
-                return ErrorEval.VALUE_INVALID;
-            }
-        }
-        if(Double.isNaN(d) || Double.isInfinite(d)) {
-               return ErrorEval.NUM_ERROR;
-        }
-        return new NumberEval(d);
-    }
+       public static final OperationEval instance = new AddEval();
 
-    public int getNumberOfOperands() {
-        return delegate.getNumberOfOperands();
-    }
+       private AddEval() {
+       }
 
-    public int getType() {
-        return delegate.getType();
-    }
+       protected double evaluate(double d0, double d1) {
+               return d0 + d1;
+       }
 }
index 021168ad7907edbd771dfb5a367893cf36c8aa2b..ba0b07e7e4a71a1be63e5adb9fb2137c82bc2a4c 100644 (file)
@@ -1,95 +1,36 @@
-/*
-* 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.
-*/
+/* ====================================================================
+   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.eval;
 
-import org.apache.poi.hssf.record.formula.Ptg;
-import org.apache.poi.hssf.record.formula.DividePtg;
-
 /**
  * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *  
  */
-public final class DivideEval extends NumericOperationEval {
-
-    private DividePtg delegate;
-
-    private static final ValueEvalToNumericXlator NUM_XLATOR = 
-        new ValueEvalToNumericXlator((short)
-                ( ValueEvalToNumericXlator.BOOL_IS_PARSED 
-                | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
-                | ValueEvalToNumericXlator.STRING_IS_PARSED
-                | ValueEvalToNumericXlator.REF_STRING_IS_PARSED
-                ));
-
-    public DivideEval(Ptg ptg) {
-        delegate = (DividePtg) ptg;
-    }
-    
-    protected ValueEvalToNumericXlator getXlator() {
-        return NUM_XLATOR;
-    }
-
-    public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
-       if(args.length != 2) {
-               return ErrorEval.VALUE_INVALID;
-       }
-        Eval retval = null;
-        double d0 = 0;
-        double d1 = 0;
-        ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol);
-        if (ve instanceof NumericValueEval) {
-            d0 = ((NumericValueEval) ve).getNumberValue();
-        }
-        else if (ve instanceof BlankEval) {
-            // do nothing
-        }
-        else {
-            retval = ErrorEval.VALUE_INVALID;
-        }
-        
-        if (retval == null) { // no error yet
-            ve = singleOperandEvaluate(args[1], srcRow, srcCol);
-            if (ve instanceof NumericValueEval) {
-                d1 = ((NumericValueEval) ve).getNumberValue();
-            }
-            else if (ve instanceof BlankEval) {
-                // do nothing
-            }
-            else {
-                retval = ErrorEval.VALUE_INVALID;
-            }
-        }
+public final class DivideEval extends TwoOperandNumericOperation {
 
-        if (retval == null) {
-            retval = (d1 == 0) 
-                ? ErrorEval.DIV_ZERO
-                : (Double.isNaN(d0) || Double.isNaN(d1)) 
-                    ? (ValueEval) ErrorEval.VALUE_INVALID 
-                    : new NumberEval(d0 / d1);
-        }
-        return retval;
-    }
+       public static final OperationEval instance = new DivideEval();
 
-    public int getNumberOfOperands() {
-        return delegate.getNumberOfOperands();
-    }
+       private DivideEval() {
+       }
 
-    public int getType() {
-        return delegate.getType();
-    }
+       protected double evaluate(double d0, double d1) throws EvaluationException {
+               if (d1 == 0.0) {
+                       throw new EvaluationException(ErrorEval.DIV_ZERO);
+               }
+               return d0 / d1;
+       }
 }
index e7b169294e8ceb25dd1a20de257a615a6dcc6405..8dca6090665eba07d08acf38c73ac343b3ad785b 100644 (file)
@@ -1,67 +1,34 @@
-/*
-* 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 8, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.eval;
+/* ====================================================================
+   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.
+==================================================================== */
 
-import org.apache.poi.hssf.record.formula.EqualPtg;
-import org.apache.poi.hssf.record.formula.Ptg;
+package org.apache.poi.hssf.record.formula.eval;
 
 /**
  * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
  *  
  */
-public class EqualEval extends RelationalOperationEval {
-
-    private EqualPtg delegate;
-
-    public EqualEval(Ptg ptg) {
-        this.delegate = (EqualPtg) ptg;
-    }
-
-    
-    public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
-        ValueEval retval = null;
-        
-        RelationalValues rvs = super.doEvaluate(operands, srcRow, srcCol);
-        retval = rvs.ee;
-        int result = 0;
-        if (retval == null) {
-            result = doComparison(rvs.bs);
-            if (result == 0) {
-                result = doComparison(rvs.ss);
-            }
-            if (result == 0) {
-                result = doComparison(rvs.ds);
-            }
-
-            retval = (result == 0) ? BoolEval.TRUE : BoolEval.FALSE;
-        }
-
-        return retval;
-    }
+public final class EqualEval extends RelationalOperationEval {
 
-    public int getNumberOfOperands() {
-        return delegate.getNumberOfOperands();
-    }
+       public static final OperationEval instance = new EqualEval();
+       
+       private EqualEval() {
+       }
 
-    public int getType() {
-        return delegate.getType();
-    }
+       protected boolean convertComparisonResult(int cmpResult) {
+               return cmpResult == 0;
+       }
 }
index ffaf780ab65092b95a484a20145a3c7bf7217bc1..95dd0b545b11f6ed9a9010e243c88a173bb05f02 100755 (executable)
@@ -42,7 +42,7 @@ final class ExternalFunction implements FreeRefFunction {
                FreeRefFunction targetFunc;
                try {
                        if (nameArg instanceof NameEval) {
-                               targetFunc = findInternalUserDefinedFunction(workbook, (NameEval) nameArg);
+                               targetFunc = findInternalUserDefinedFunction((NameEval) nameArg);
                        } else if (nameArg instanceof NameXEval) {
                                targetFunc = findExternalUserDefinedFunction(workbook, (NameXEval) nameArg);
                        } else {
@@ -65,7 +65,7 @@ final class ExternalFunction implements FreeRefFunction {
                if(false) {
                        System.out.println("received call to external user defined function (" + functionName + ")");
                }
-               // currently only looking for functions from the 'Analysis TookPak'
+               // currently only looking for functions from the 'Analysis TookPak'  e.g. "YEARFRAC" or "ISEVEN"
                // not sure how much this logic would need to change to support other or multiple add-ins.
                FreeRefFunction result = AnalysisToolPak.findFunction(functionName);
                if (result != null) {
@@ -74,24 +74,12 @@ final class ExternalFunction implements FreeRefFunction {
                throw new EvaluationException(ErrorEval.FUNCTION_NOT_IMPLEMENTED);
        }
 
-       private FreeRefFunction findInternalUserDefinedFunction(Workbook workbook, NameEval functionNameEval) throws EvaluationException {
-
-               int numberOfNames = workbook.getNumberOfNames();
-               
-               int nameIndex = functionNameEval.getIndex();
-               if(nameIndex < 0 || nameIndex >= numberOfNames) {
-                       throw new RuntimeException("Bad name index (" + nameIndex 
-                                       + "). Allowed range is (0.." + (numberOfNames-1) + ")");
-               }
-               
-               String functionName = workbook.getNameName(nameIndex);
+       private FreeRefFunction findInternalUserDefinedFunction(NameEval functionNameEval) throws EvaluationException {
+               String functionName = functionNameEval.getFunctionName();
                if(false) {
                        System.out.println("received call to internal user defined function  (" + functionName + ")");
                }
-               // TODO - detect if the NameRecord corresponds to a named range, function, or something undefined
-               // throw the right errors in these cases
-               
-               // TODO find the implementation for the external function e.g. "YEARFRAC" or "ISEVEN"
+               // TODO find the implementation for the user defined function
                
                throw new EvaluationException(ErrorEval.FUNCTION_NOT_IMPLEMENTED);
        }
index 6a9a23217bf70ca92838d42d20d5e04c8182d896..16dd48846eeb378d1929ec84af3f39cf058fbfc7 100644 (file)
@@ -1,67 +1,34 @@
-/*
-* 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 8, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.eval;
+/* ====================================================================
+   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.
+==================================================================== */
 
-import org.apache.poi.hssf.record.formula.GreaterEqualPtg;
-import org.apache.poi.hssf.record.formula.Ptg;
+package org.apache.poi.hssf.record.formula.eval;
 
 /**
  * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
  *  
  */
-public class GreaterEqualEval extends RelationalOperationEval {
-
-    private GreaterEqualPtg delegate;
-
-    public GreaterEqualEval(Ptg ptg) {
-        this.delegate = (GreaterEqualPtg) ptg;
-    }
-
-    public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
-        ValueEval retval = null;
-        
-        RelationalValues rvs = super.doEvaluate(operands, srcRow, srcCol);
-        retval = rvs.ee;
-        int result = 0;
-        if (retval == null) {
-            result = doComparison(rvs.bs);
-            if (result == 0) {
-                result = doComparison(rvs.ss);
-            }
-            if (result == 0) {
-                result = doComparison(rvs.ds);
-            }
-
-            retval = (result >= 0) ? BoolEval.TRUE : BoolEval.FALSE;
-        }
-
-        return retval;
-    }
-
-    public int getNumberOfOperands() {
-        return delegate.getNumberOfOperands();
-    }
+public final class GreaterEqualEval extends RelationalOperationEval {
 
-    public int getType() {
-        return delegate.getType();
-    }
+       public static final OperationEval instance = new GreaterEqualEval();
+       
+       private GreaterEqualEval() {
+       }
 
+       protected boolean convertComparisonResult(int cmpResult) {
+               return cmpResult >= 0;
+       }
 }
index 27da54a0460a62e5e7be70193f5fd12830e13a6f..45605a41c08801bcd61d3c9aeb0266515e6a5bb8 100644 (file)
@@ -1,67 +1,34 @@
-/*
-* 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 8, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.eval;
+/* ====================================================================
+   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.
+==================================================================== */
 
-import org.apache.poi.hssf.record.formula.GreaterThanPtg;
-import org.apache.poi.hssf.record.formula.Ptg;
+package org.apache.poi.hssf.record.formula.eval;
 
 /**
  * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
  *  
  */
-public class GreaterThanEval extends RelationalOperationEval {
-
-    private GreaterThanPtg delegate;
-
-    public GreaterThanEval(Ptg ptg) {
-        this.delegate = (GreaterThanPtg) ptg;
-    }
-
-    public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
-        ValueEval retval = null;
-        
-        RelationalValues rvs = super.doEvaluate(operands, srcRow, srcCol);
-        retval = rvs.ee;
-        int result = 0;
-        if (retval == null) {
-            result = doComparison(rvs.bs);
-            if (result == 0) {
-                result = doComparison(rvs.ss);
-            }
-            if (result == 0) {
-                result = doComparison(rvs.ds);
-            }
-
-            retval = (result > 0) ? BoolEval.TRUE : BoolEval.FALSE;;
-        }
-
-        return retval;
-    }
-
-    public int getNumberOfOperands() {
-        return delegate.getNumberOfOperands();
-    }
+public final class GreaterThanEval extends RelationalOperationEval {
 
-    public int getType() {
-        return delegate.getType();
-    }
+       public static final OperationEval instance = new GreaterThanEval();
+       
+       private GreaterThanEval() {
+       }
 
+       protected boolean convertComparisonResult(int cmpResult) {
+               return cmpResult > 0;
+       }
 }
index b5c633617096b314c971feb016405a35334be943..9f38305545b9d7f9208a31928df8a53166fdeb87 100644 (file)
@@ -1,19 +1,19 @@
-/*
-* 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.
-*/
+/* ====================================================================
+   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.eval;
 
@@ -24,20 +24,21 @@ import org.apache.poi.ss.usermodel.FormulaEvaluator;
 import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.ss.usermodel.Sheet;
 import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.util.CellReference;
 
 /**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- * 
+ *
+ * @author Josh Micich 
  */
 public final class LazyAreaEval extends AreaEvalBase {
 
        private final Sheet _sheet;
-       private Workbook _workbook;
+       private FormulaEvaluator _evaluator;
 
-       public LazyAreaEval(AreaI ptg, Sheet sheet, Workbook workbook) {
+       public LazyAreaEval(AreaI ptg, Sheet sheet, FormulaEvaluator evaluator) {
                super(ptg);
                _sheet = sheet;
-               _workbook = workbook;
+               _evaluator = evaluator;
        }
 
        public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) { 
@@ -53,13 +54,27 @@ public final class LazyAreaEval extends AreaEvalBase {
                if (cell == null) {
                        return BlankEval.INSTANCE;
                }
-               return FormulaEvaluator.getEvalForCell(cell, _sheet, _workbook);
+               return _evaluator.getEvalForCell(cell, _sheet);
        }
 
        public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) {
                AreaI area = new OffsetArea(getFirstRow(), getFirstColumn(),
                                relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);
 
-               return new LazyAreaEval(area, _sheet, _workbook);
+               return new LazyAreaEval(area, _sheet, _evaluator);
+       }
+       public String toString() {
+               CellReference crA = new CellReference(getFirstRow(), getFirstColumn());
+               CellReference crB = new CellReference(getLastRow(), getLastColumn());
+               StringBuffer sb = new StringBuffer();
+               sb.append(getClass().getName()).append("[");
+               String sheetName = _evaluator.getSheetName(_sheet);
+               sb.append(sheetName);
+               sb.append('!');
+               sb.append(crA.formatAsString());
+               sb.append(':');
+               sb.append(crB.formatAsString());
+               sb.append("]");
+               return sb.toString();
        }
 }
index 7daf964240346cc92bc53a373b6f91115a4cfd6b..b458011972dc4467ccf8fefe581bbded571acc45 100644 (file)
@@ -1,30 +1,51 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
 package org.apache.poi.hssf.record.formula.eval;\r
 \r
 import org.apache.poi.hssf.record.formula.AreaI;\r
-import org.apache.poi.hssf.record.formula.AreaI.OffsetArea;\r
 import org.apache.poi.hssf.record.formula.Ref3DPtg;\r
 import org.apache.poi.hssf.record.formula.RefPtg;\r
+import org.apache.poi.hssf.record.formula.AreaI.OffsetArea;\r
 import org.apache.poi.ss.usermodel.Cell;\r
 import org.apache.poi.ss.usermodel.FormulaEvaluator;\r
 import org.apache.poi.ss.usermodel.Row;\r
 import org.apache.poi.ss.usermodel.Sheet;\r
 import org.apache.poi.ss.usermodel.Workbook;\r
+import org.apache.poi.ss.util.CellReference;\r
 \r
+/**\r
+*\r
+* @author Josh Micich \r
+*/\r
 public final class LazyRefEval extends RefEvalBase {\r
-\r
        private final Sheet _sheet;\r
-       private final Workbook _workbook;\r
+       private final FormulaEvaluator _evaluator;\r
 \r
 \r
-       public LazyRefEval(RefPtg ptg, Sheet sheet, Workbook workbook) {\r
+       public LazyRefEval(RefPtg ptg, Sheet sheet, FormulaEvaluator evaluator) {\r
                super(ptg.getRow(), ptg.getColumn());\r
                _sheet = sheet;\r
-               _workbook = workbook;\r
+               _evaluator = evaluator;\r
        }\r
-       public LazyRefEval(Ref3DPtg ptg, Sheet sheet, Workbook workbook) {\r
+       public LazyRefEval(Ref3DPtg ptg, Sheet sheet, FormulaEvaluator evaluator) {\r
                super(ptg.getRow(), ptg.getColumn());\r
                _sheet = sheet;\r
-               _workbook = workbook;\r
+               _evaluator = evaluator;\r
        }\r
 \r
        public ValueEval getInnerValueEval() {\r
@@ -39,7 +60,7 @@ public final class LazyRefEval extends RefEvalBase {
                if (cell == null) {\r
                        return BlankEval.INSTANCE;\r
                }\r
-               return FormulaEvaluator.getEvalForCell(cell, _sheet, _workbook);\r
+               return _evaluator.getEvalForCell(cell, _sheet);\r
        }\r
        \r
        public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) {\r
@@ -47,6 +68,18 @@ public final class LazyRefEval extends RefEvalBase {
                AreaI area = new OffsetArea(getRow(), getColumn(),\r
                                relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);\r
 \r
-               return new LazyAreaEval(area, _sheet, _workbook);\r
+               return new LazyAreaEval(area, _sheet, _evaluator);\r
+       }\r
+       \r
+       public String toString() {\r
+               CellReference cr = new CellReference(getRow(), getColumn());\r
+               StringBuffer sb = new StringBuffer();\r
+               sb.append(getClass().getName()).append("[");\r
+               String sheetName = _evaluator.getSheetName(_sheet);\r
+               sb.append(sheetName);\r
+               sb.append('!');\r
+               sb.append(cr.formatAsString());\r
+               sb.append("]");\r
+               return sb.toString();\r
        }\r
 }\r
index e45bf9e6bb110b628dd878b8449bf9421b5d94f2..e62be04d0cf97bf5062f500e8abbc7838f4dd75e 100644 (file)
@@ -1,67 +1,34 @@
-/*
-* 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 8, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.eval;
+/* ====================================================================
+   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.
+==================================================================== */
 
-import org.apache.poi.hssf.record.formula.LessEqualPtg;
-import org.apache.poi.hssf.record.formula.Ptg;
+package org.apache.poi.hssf.record.formula.eval;
 
 /**
  * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
  *  
  */
-public class LessEqualEval extends RelationalOperationEval {
-
-    private LessEqualPtg delegate;
-
-    public LessEqualEval(Ptg ptg) {
-        this.delegate = (LessEqualPtg) ptg;
-    }
-
-    public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
-        ValueEval retval = null;
-        
-        RelationalValues rvs = super.doEvaluate(operands, srcRow, srcCol);
-        retval = rvs.ee;
-        int result = 0;
-        if (retval == null) {
-            result = doComparison(rvs.bs);
-            if (result == 0) {
-                result = doComparison(rvs.ss);
-            }
-            if (result == 0) {
-                result = doComparison(rvs.ds);
-            }
-
-            retval = (result <= 0) ? BoolEval.TRUE : BoolEval.FALSE;;
-        }
-
-        return retval;
-    }
-
-    public int getNumberOfOperands() {
-        return delegate.getNumberOfOperands();
-    }
+public final class LessEqualEval extends RelationalOperationEval {
 
-    public int getType() {
-        return delegate.getType();
-    }
+       public static final OperationEval instance = new LessEqualEval();
+       
+       private LessEqualEval() {
+       }
 
+       protected boolean convertComparisonResult(int cmpResult) {
+               return cmpResult <= 0;
+       }
 }
index 1aac6f73bfa9c425753b05685bdcb84c198f489b..cf1fc1befca06110097b19d1fe4fe1c3dd2c67a9 100644 (file)
@@ -1,68 +1,34 @@
-/*
-* 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 8, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.eval;
+/* ====================================================================
+   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.
+==================================================================== */
 
-import org.apache.poi.hssf.record.formula.LessThanPtg;
-import org.apache.poi.hssf.record.formula.Ptg;
+package org.apache.poi.hssf.record.formula.eval;
 
 /**
  * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
  *  
  */
-public class LessThanEval extends RelationalOperationEval {
-
-    private LessThanPtg delegate;
-
-    public LessThanEval(Ptg ptg) {
-        this.delegate = (LessThanPtg) ptg;
-    }
-
-
-    public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
-        ValueEval retval = null;
-        
-        RelationalValues rvs = super.doEvaluate(operands, srcRow, srcCol);
-        retval = rvs.ee;
-        int result = 0;
-        if (retval == null) {
-            result = doComparison(rvs.bs);
-            if (result == 0) {
-                result = doComparison(rvs.ss);
-            }
-            if (result == 0) {
-                result = doComparison(rvs.ds);
-            }
-
-            retval = (result < 0) ? BoolEval.TRUE : BoolEval.FALSE;;
-        }
-
-        return retval;
-    }
-
-    public int getNumberOfOperands() {
-        return delegate.getNumberOfOperands();
-    }
+public final class LessThanEval extends RelationalOperationEval {
 
-    public int getType() {
-        return delegate.getType();
-    }
+       public static final OperationEval instance = new LessThanEval();
+       
+       private LessThanEval() {
+       }
 
+       protected boolean convertComparisonResult(int cmpResult) {
+               return cmpResult < 0;
+       }
 }
index 22d87b7e4d4f2dccec54eb8680c450127f9e9513..83b829c7111cafde460268cd18abe0fca9add97e 100644 (file)
@@ -1,89 +1,33 @@
-/*
-* 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.
-*/
+/* ====================================================================
+   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.eval;
 
-import org.apache.poi.hssf.record.formula.Ptg;
-import org.apache.poi.hssf.record.formula.MultiplyPtg;
-
 /**
  * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *  
  */
-public final class MultiplyEval extends NumericOperationEval {
-
-    private MultiplyPtg delegate;
-
-    private static final ValueEvalToNumericXlator NUM_XLATOR = 
-        new ValueEvalToNumericXlator((short)
-                ( ValueEvalToNumericXlator.BOOL_IS_PARSED 
-                | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
-                | ValueEvalToNumericXlator.STRING_IS_PARSED
-                | ValueEvalToNumericXlator.REF_STRING_IS_PARSED
-                ));
-
-    public MultiplyEval(Ptg ptg) {
-        delegate = (MultiplyPtg) ptg;
-    }
-    
-    protected ValueEvalToNumericXlator getXlator() {
-        return NUM_XLATOR;
-    }
+public final class MultiplyEval extends TwoOperandNumericOperation {
 
-    public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
-       if(args.length != 2) {
-               return ErrorEval.VALUE_INVALID;
-       }
-       
-        double d0 = 0;
-        double d1 = 0;
-        ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol);
-        if (ve instanceof NumericValueEval) {
-            d0 = ((NumericValueEval) ve).getNumberValue();
-        }
-        else if (ve instanceof BlankEval) {
-            // do nothing
-        }
-        else {
-            return ErrorEval.VALUE_INVALID;
-        }
-        
-        ve = singleOperandEvaluate(args[1], srcRow, srcCol);
-        if (ve instanceof NumericValueEval) {
-            d1 = ((NumericValueEval) ve).getNumberValue();
-        }
-        else if (ve instanceof BlankEval) {
-        // do nothing
-        }
-        else {
-               return ErrorEval.VALUE_INVALID;
-        }
-        if (Double.isNaN(d0) || Double.isNaN(d1)) {
-                       return ErrorEval.NUM_ERROR;
-               }
-               return new NumberEval(d0 * d1);
-    }
+       public static final OperationEval instance = new MultiplyEval();
 
-    public int getNumberOfOperands() {
-        return delegate.getNumberOfOperands();
-    }
+       private MultiplyEval() {
+       }
 
-    public int getType() {
-        return delegate.getType();
-    }
+       protected double evaluate(double d0, double d1) {
+               return d0 * d1;
+       }
 }
index 682394b3c25636122d4d006d09e672bc7813f124..1f77deec2aad3922c5e5484bcba2d5e6ca91986f 100755 (executable)
@@ -22,26 +22,24 @@ package org.apache.poi.hssf.record.formula.eval;
  */
 public final class NameEval implements Eval {
 
-       private final int _index;
+       private final String _functionName;
 
        /**
-        * @param index zero based index to a defined name record
+        * Creates a NameEval representing a function name
         */
-       public NameEval(int index) {
-               _index = index;
+       public NameEval(String functionName) {
+               _functionName = functionName;
        }
 
-       /**
-        * @return zero based index to a defined name record
-        */
-       public int getIndex() {
-               return _index;
+
+       public String getFunctionName() {
+               return _functionName;
        }
 
        public String toString() {
                StringBuffer sb = new StringBuffer(64);
                sb.append(getClass().getName()).append(" [");
-               sb.append(_index);
+               sb.append(_functionName);
                sb.append("]");
                return sb.toString();
        }
index c5388f520f8d75242a0e9ede1d091d611b210e77..da8f315490966fc20e17aa53cab2d8c240e106e3 100644 (file)
@@ -1,68 +1,34 @@
-/*
-* 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 8, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.eval;
+/* ====================================================================
+   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.
+==================================================================== */
 
-import org.apache.poi.hssf.record.formula.NotEqualPtg;
-import org.apache.poi.hssf.record.formula.Ptg;
+package org.apache.poi.hssf.record.formula.eval;
 
 /**
  * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
  *  
  */
-public class NotEqualEval extends RelationalOperationEval {
-
-    private NotEqualPtg delegate;
-
-    public NotEqualEval(Ptg ptg) {
-        this.delegate = (NotEqualPtg) ptg;
-    }
-
-
-    public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
-        ValueEval retval = null;
-        
-        RelationalValues rvs = super.doEvaluate(operands, srcRow, srcCol);
-        retval = rvs.ee;
-        int result = 0;
-        if (retval == null) {
-            result = doComparison(rvs.bs);
-            if (result == 0) {
-                result = doComparison(rvs.ss);
-            }
-            if (result == 0) {
-                result = doComparison(rvs.ds);
-            }
-
-            retval = (result != 0) ? BoolEval.TRUE : BoolEval.FALSE;
-        }
-
-        return retval;
-    }
-
-    public int getNumberOfOperands() {
-        return delegate.getNumberOfOperands();
-    }
+public final class NotEqualEval extends RelationalOperationEval {
 
-    public int getType() {
-        return delegate.getType();
-    }
+       public static final OperationEval instance = new NotEqualEval();
+       
+       private NotEqualEval() {
+       }
 
+       protected boolean convertComparisonResult(int cmpResult) {
+               return cmpResult != 0;
+       }
 }
diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/NumericOperationEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/NumericOperationEval.java
deleted file mode 100644 (file)
index 6bc1c95..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
-* 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 14, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.eval;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *
- */
-public abstract class NumericOperationEval implements OperationEval {
-
-    protected abstract ValueEvalToNumericXlator getXlator();
-    
-    protected ValueEval singleOperandEvaluate(Eval eval, int srcRow, short srcCol) {
-        ValueEval retval;
-        if (eval instanceof AreaEval) {
-            AreaEval ae = (AreaEval) eval;
-            if (ae.contains(srcRow, srcCol)) { // circular ref!
-                retval = ErrorEval.CIRCULAR_REF_ERROR;
-            }
-            else if (ae.isRow()) {
-                if (ae.containsColumn(srcCol)) {
-                    ValueEval ve = ae.getValueAt(ae.getFirstRow(), srcCol);
-                    ve = getXlator().attemptXlateToNumeric(ve);
-                    retval = getXlator().attemptXlateToNumeric(ve);
-                }
-                else {
-                    retval = ErrorEval.VALUE_INVALID;
-                }
-            }
-            else if (ae.isColumn()) {
-                if (ae.containsRow(srcRow)) {
-                    ValueEval ve = ae.getValueAt(srcRow, ae.getFirstColumn());
-                    retval = getXlator().attemptXlateToNumeric(ve);
-                }
-                else {
-                    retval = ErrorEval.VALUE_INVALID;
-                }
-            }
-            else {
-                retval = ErrorEval.VALUE_INVALID;
-            }
-        }
-        else {
-            retval = getXlator().attemptXlateToNumeric((ValueEval) eval);
-        }
-        return retval;
-    }
-}
index c698a4e5020a8440d8222d1392f78b633fdbe89d..d03e4474521a08753ae02bc8df58dc6228ae1c5f 100755 (executable)
 
 package org.apache.poi.hssf.record.formula.eval;
 
-import org.apache.poi.hssf.record.formula.PercentPtg;
-import org.apache.poi.hssf.record.formula.Ptg;
 
 /**
  * Implementation of Excel formula token '%'. <p/>
  * @author Josh Micich
  */
-public final class PercentEval extends NumericOperationEval {
+public final class PercentEval implements OperationEval {
 
-       private PercentPtg _delegate;
+       public static final OperationEval instance = new PercentEval();
 
-       private static final ValueEvalToNumericXlator NUM_XLATOR = new ValueEvalToNumericXlator(
-                       (short) (ValueEvalToNumericXlator.BOOL_IS_PARSED
-                                       | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
-                                       | ValueEvalToNumericXlator.STRING_IS_PARSED | ValueEvalToNumericXlator.REF_STRING_IS_PARSED));
-
-       public PercentEval(Ptg ptg) {
-               _delegate = (PercentPtg) ptg;
-       }
-
-       protected ValueEvalToNumericXlator getXlator() {
-               return NUM_XLATOR;
+       private PercentEval() {
        }
 
        public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
                if (args.length != 1) {
                        return ErrorEval.VALUE_INVALID;
                }
-
-               ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol);
-               if (ve instanceof NumericValueEval) {
-                       double d0 = ((NumericValueEval) ve).getNumberValue();
-                       return new NumberEval(d0 / 100);
+       double d0;
+               try {
+                       ValueEval ve = OperandResolver.getSingleValue(args[0], srcRow, srcCol);
+                       if (ve instanceof BlankEval) {
+                               return NumberEval.ZERO;
+                       }
+                       d0 = OperandResolver.coerceValueToDouble(ve);
+               } catch (EvaluationException e) {
+                       return e.getErrorEval();
                }
-
-               if (ve instanceof BlankEval) {
-                       return NumberEval.ZERO;
-               }
-               if (ve instanceof ErrorEval) {
-                       return ve;
-               }
-               return ErrorEval.VALUE_INVALID;
+               return new NumberEval(d0 / 100);
        }
 
        public int getNumberOfOperands() {
-               return _delegate.getNumberOfOperands();
-       }
-
-       public int getType() {
-               return _delegate.getType();
+               return 1;
        }
+       public final int getType() {
+       // TODO - remove
+        throw new RuntimeException("obsolete code should not be called");
+    }
 }
index 651c5d2aa2c829b87984b7f25fc0ef227b6e960c..685332a053aaa6ed8116ce7dc2159fd08c9e77a5 100644 (file)
@@ -1,90 +1,33 @@
-/*
-* 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.
-*/
+/* ====================================================================
+   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.eval;
 
-import org.apache.poi.hssf.record.formula.Ptg;
-import org.apache.poi.hssf.record.formula.PowerPtg;
-
 /**
  * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *  
  */
-public final class PowerEval extends NumericOperationEval {
-
-    private PowerPtg delegate;
-
-    private static final ValueEvalToNumericXlator NUM_XLATOR = 
-        new ValueEvalToNumericXlator((short)
-                ( ValueEvalToNumericXlator.BOOL_IS_PARSED 
-                | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
-                | ValueEvalToNumericXlator.STRING_IS_PARSED
-                | ValueEvalToNumericXlator.REF_STRING_IS_PARSED
-                ));
-
-    public PowerEval(Ptg ptg) {
-        delegate = (PowerPtg) ptg;
-    }
-    
-    protected ValueEvalToNumericXlator getXlator() {
-        return NUM_XLATOR;
-    }
-
-    public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
-       if(args.length != 2) {
-               return ErrorEval.VALUE_INVALID;
-       }
-        double d0 = 0;
-        double d1 = 0;
-        
-        ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol);
-        if (ve instanceof NumericValueEval) {
-            d0 = ((NumericValueEval) ve).getNumberValue();
-        }
-        else if (ve instanceof BlankEval) {
-            // do nothing
-        }
-        else {
-            return ErrorEval.VALUE_INVALID;
-        }
-        
-        ve = singleOperandEvaluate(args[1], srcRow, srcCol);
-        if (ve instanceof NumericValueEval) {
-            d1 = ((NumericValueEval) ve).getNumberValue();
-        }
-        else if (ve instanceof BlankEval) {
-        // do nothing
-        }
-        else {
-            return ErrorEval.VALUE_INVALID;
-        }
+public final class PowerEval extends TwoOperandNumericOperation {
 
-        double p = Math.pow(d0, d1);
-        if (Double.isNaN(p)) {
-                       return ErrorEval.VALUE_INVALID;
-               }
-               return new NumberEval(p);
-    }
+       public static final OperationEval instance = new PowerEval();
 
-    public int getNumberOfOperands() {
-        return delegate.getNumberOfOperands();
-    }
+       private PowerEval() {
+       }
 
-    public int getType() {
-        return delegate.getType();
-    }
+       protected double evaluate(double d0, double d1) {
+               return Math.pow(d0, d1);
+       }
 }
index 9b1a2ece4ff678a5db9e5b198d0ae0d3fdcf2692..058b00e1ad10500c9f48537b94c534ab5388e0bc 100644 (file)
-/*
-* 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 10, 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.eval;
 
 /**
+ * Base class for all comparison operator evaluators
+ * 
  * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *
  */
 public abstract class RelationalOperationEval implements OperationEval {
 
-    protected class RelationalValues {
-        public Double[] ds = new Double[2];
-        public Boolean[] bs = new Boolean[2];
-        public String[] ss = new String[3];
-        public ErrorEval ee = null;
-    }
+       /**
+        * Converts a standard compare result (-1, 0, 1) to <code>true</code> or <code>false</code>
+        * according to subclass' comparison type.
+        */
+       protected abstract boolean convertComparisonResult(int cmpResult);
+
+       /**
+        * This is a description of how the relational operators apply in MS Excel.
+        * Use this as a guideline when testing/implementing the evaluate methods
+        * for the relational operators Evals.
+        *
+        * <pre>
+        * Bool.TRUE > any number.
+        * Bool > any string. ALWAYS
+        * Bool.TRUE > Bool.FALSE
+        * Bool.FALSE == Blank
+        *
+        * Strings are never converted to numbers or booleans
+        * String > any number. ALWAYS
+        * Non-empty String > Blank
+        * Empty String == Blank
+        * String are sorted dictionary wise
+        *
+        * Blank > Negative numbers
+        * Blank == 0
+        * Blank < Positive numbers
+        * </pre>
+        */
+       public final Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
+               if (operands.length != 2) {
+                       return ErrorEval.VALUE_INVALID;
+               }
+
+               ValueEval vA;
+               ValueEval vB;
+               try {
+                       vA = OperandResolver.getSingleValue(operands[0], srcRow, srcCol);
+                       vB = OperandResolver.getSingleValue(operands[1], srcRow, srcCol);
+               } catch (EvaluationException e) {
+                       return e.getErrorEval();
+               }
+               int cmpResult = doCompare(vA, vB);
+               boolean result = convertComparisonResult(cmpResult);
+               return BoolEval.valueOf(result);
+       }
+
+       private static int doCompare(ValueEval va, ValueEval vb) {
+               // special cases when one operand is blank
+               if (va == BlankEval.INSTANCE) {
+                       return compareBlank(vb);
+               }
+               if (vb == BlankEval.INSTANCE) {
+                       return -compareBlank(va);
+               }
+
+               if (va instanceof BoolEval) {
+                       if (vb instanceof BoolEval) {
+                               BoolEval bA = (BoolEval) va;
+                               BoolEval bB = (BoolEval) vb;
+                               if (bA.getBooleanValue() == bB.getBooleanValue()) {
+                                       return 0;
+                               }
+                               return bA.getBooleanValue() ? 1 : -1;
+                       }
+                       return 1;
+               }
+               if (vb instanceof BoolEval) {
+                       return -1;
+               }
+               if (va instanceof StringEval) {
+                       if (vb instanceof StringEval) {
+                               StringEval sA = (StringEval) va;
+                               StringEval sB = (StringEval) vb;
+                               return sA.getStringValue().compareTo(sB.getStringValue());
+                       }
+                       return 1;
+               }
+               if (vb instanceof StringEval) {
+                       return -1;
+               }
+               if (va instanceof NumberEval) {
+                       if (vb instanceof NumberEval) {
+                               NumberEval nA = (NumberEval) va;
+                               NumberEval nB = (NumberEval) vb;
+                               return Double.compare(nA.getNumberValue(), nB.getNumberValue());
+                       }
+               }
+               throw new IllegalArgumentException("Bad operand types (" + va.getClass().getName() + "), ("
+                               + vb.getClass().getName() + ")");
+       }
 
-    
-    /*
-     * This is a description of how the relational operators apply in MS Excel.
-     * Use this as a guideline when testing/implementing the evaluate methods 
-     * for the relational operators Evals.
-     * 
-     * Bool > any number. ALWAYS
-     * Bool > any string. ALWAYS
-     * Bool.TRUE > Bool.FALSE
-     * 
-     * String > any number. ALWAYS
-     * String > Blank. ALWAYS
-     * String are sorted dictionary wise
-     * 
-     * Blank == 0 (numeric)
-     */
-    public RelationalValues doEvaluate(Eval[] operands, int srcRow, short srcCol) {
-        RelationalValues retval = new RelationalValues();
-        
-        switch (operands.length) {
-        default:
-            retval.ee = ErrorEval.VALUE_INVALID;
-            break;
-        case 2:
-            internalDoEvaluate(operands, srcRow, srcCol, retval, 0);
-            internalDoEvaluate(operands, srcRow, srcCol, retval, 1);
-        } // end switch
-        return retval;
-    }
-    
-    /**
-     * convenience method to avoid code duplication for multiple operands
-     * @param operands
-     * @param srcRow
-     * @param srcCol
-     * @param retval
-     * @param index
-     */
-    private void internalDoEvaluate(Eval[] operands, int srcRow, short srcCol, RelationalValues retval, int index) {
-        if (operands[index] instanceof BoolEval) {
-            BoolEval be = (BoolEval) operands[index];
-            retval.bs[index] = Boolean.valueOf(be.getBooleanValue());
-        }
-        else if (operands[index] instanceof NumericValueEval) {
-            NumericValueEval ne = (NumericValueEval) operands[index];
-            retval.ds[index] = new Double(ne.getNumberValue());
-        }
-        else if (operands[index] instanceof StringValueEval) {
-            StringValueEval se = (StringValueEval) operands[index];
-            retval.ss[index] = se.getStringValue();
-        }
-        else if (operands[index] instanceof RefEval) {
-            RefEval re = (RefEval) operands[index];
-            ValueEval ve = re.getInnerValueEval();
-            if (ve instanceof BoolEval) {
-                BoolEval be = (BoolEval) ve;
-                retval.bs[index] = Boolean.valueOf(be.getBooleanValue());
-            }
-            else if (ve instanceof BlankEval) {
-                retval.ds[index] = new Double(0);
-            }
-            else if (ve instanceof NumericValueEval) {
-                NumericValueEval ne = (NumericValueEval) ve;
-                retval.ds[index] = new Double(ne.getNumberValue());
-            }
-            else if (ve instanceof StringValueEval) {
-                StringValueEval se = (StringValueEval) ve;
-                retval.ss[index] = se.getStringValue();
-            }
-        }
-        else if (operands[index] instanceof AreaEval) {
-            AreaEval ae = (AreaEval) operands[index];
-            if (ae.isRow()) {
-                if (ae.containsColumn(srcCol)) {
-                    ValueEval ve = ae.getValueAt(ae.getFirstRow(), srcCol);
-                    if (ve instanceof BoolEval) {
-                        BoolEval be = (BoolEval) ve;
-                        retval.bs[index] = Boolean.valueOf(be.getBooleanValue());
-                    }
-                    else if (ve instanceof BlankEval) {
-                        retval.ds[index] = new Double(0);
-                    }
-                    else if (ve instanceof NumericValueEval) {
-                        NumericValueEval ne = (NumericValueEval) ve;
-                        retval.ds[index] = new Double(ne.getNumberValue());
-                    }
-                    else if (ve instanceof StringValueEval) {
-                        StringValueEval se = (StringValueEval) ve;
-                        retval.ss[index] = se.getStringValue();
-                    }
-                    else {
-                        retval.ee = ErrorEval.VALUE_INVALID;
-                    }
-                }
-                else {
-                    retval.ee = ErrorEval.VALUE_INVALID;
-                }
-            }
-            else if (ae.isColumn()) {
-                if (ae.containsRow(srcRow)) {
-                    ValueEval ve = ae.getValueAt(srcRow, ae.getFirstColumn());
-                    if (ve instanceof BoolEval) {
-                        BoolEval be = (BoolEval) ve;
-                        retval.bs[index] = Boolean.valueOf(be.getBooleanValue());
-                    }
-                    else if (ve instanceof BlankEval) {
-                        retval.ds[index] = new Double(0);
-                    }
-                    else if (ve instanceof NumericValueEval) {
-                        NumericValueEval ne = (NumericValueEval) ve;
-                        retval.ds[index] = new Double(ne.getNumberValue());
-                    }
-                    else if (ve instanceof StringValueEval) {
-                        StringValueEval se = (StringValueEval) ve;
-                        retval.ss[index] = se.getStringValue();
-                    }
-                    else {
-                        retval.ee = ErrorEval.VALUE_INVALID;
-                    }
-                }
-                else {
-                    retval.ee = ErrorEval.VALUE_INVALID;
-                }
-            }
-            else {
-                retval.ee = ErrorEval.VALUE_INVALID;
-            }
-        }
-    }
-    
-    // if both null return 0, else non null wins, else TRUE wins
-    protected int doComparison(Boolean[] bs) {
-        int retval = 0;
-        if (bs[0] != null || bs[1] != null) {
-            retval = bs[0] != null
-                    ? bs[1] != null
-                            ? bs[0].booleanValue()
-                                    ? bs[1].booleanValue()
-                                            ? 0
-                                            : 1
-                                    : bs[1].booleanValue()
-                                            ? -1
-                                            : 0
-                            : 1
-                    : bs[1] != null
-                            ? -1
-                            : 0;
-        }
-        return retval;
-    }
+       private static int compareBlank(ValueEval v) {
+               if (v == BlankEval.INSTANCE) {
+                       return 0;
+               }
+               if (v instanceof BoolEval) {
+                       BoolEval boolEval = (BoolEval) v;
+                       return boolEval.getBooleanValue() ? -1 : 0;
+               }
+               if (v instanceof NumberEval) {
+                       NumberEval ne = (NumberEval) v;
+                       return Double.compare(0, ne.getNumberValue());
+               }
+               if (v instanceof StringEval) {
+                       StringEval se = (StringEval) v;
+                       return se.getStringValue().length() < 1 ? 0 : -1;
+               }
+               throw new IllegalArgumentException("bad value class (" + v.getClass().getName() + ")");
+       }
 
-    // if both null return 0, else non null wins, else string compare
-    protected int doComparison(String[] ss) {
-        int retval = 0;
-        if (ss[0] != null || ss[1] != null) {
-            retval = ss[0] != null
-                    ? ss[1] != null
-                            ? ss[0].compareTo(ss[1])
-                            : 1
-                    : ss[1] != null
-                            ? -1
-                            : 0;
-        }
-        return retval;
-    }
+       public final int getNumberOfOperands() {
+               return 2;
+       }
 
-    // if both null return 0, else non null wins, else doublevalue compare
-    protected int doComparison(Double[] ds) {
-        int retval = 0;
-        if (ds[0] != null || ds[1] != null) {
-            retval = ds[0] != null
-                    ? ds[1] != null
-                            ? ds[0].compareTo(ds[1])
-                            : 1
-                    : ds[1] != null
-                            ? -1
-                            : 0;
-        }
-        return retval;
-    }
+       public final int getType() {
+               // TODO - get rid of this method
+               throw new RuntimeException("Obsolete code - should not be called");
+       }
 }
index 85a3845299a49686b8308b5832a601875d675cf7..cbd6dc5eec476ec605eeb374d34616af51f381e4 100644 (file)
@@ -1,93 +1,33 @@
-/*
-* 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.
-*/
+/* ====================================================================
+   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.eval;
 
-import org.apache.poi.hssf.record.formula.Ptg;
-import org.apache.poi.hssf.record.formula.SubtractPtg;
-
 /**
  * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *  
  */
-public final class SubtractEval extends NumericOperationEval {
-
-    private SubtractPtg delegate;
-
-    private static final ValueEvalToNumericXlator NUM_XLATOR = 
-        new ValueEvalToNumericXlator((short)
-                ( ValueEvalToNumericXlator.BOOL_IS_PARSED 
-                | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
-                | ValueEvalToNumericXlator.STRING_IS_PARSED
-                | ValueEvalToNumericXlator.REF_STRING_IS_PARSED
-                ));
-
-    public SubtractEval(Ptg ptg) {
-        delegate = (SubtractPtg) ptg;
-    }
-    
-    protected ValueEvalToNumericXlator getXlator() {
-        return NUM_XLATOR;
-    }
+public final class SubtractEval extends TwoOperandNumericOperation {
 
-    public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
-       if(args.length != 2) {
-               return ErrorEval.VALUE_INVALID;
-       }
-        Eval retval = null;
-        double d0 = 0;
-        double d1 = 0;
-        ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol);
-        if (ve instanceof NumericValueEval) {
-            d0 = ((NumericValueEval) ve).getNumberValue();
-        }
-        else if (ve instanceof BlankEval) {
-            // do nothing
-        }
-        else {
-            retval = ErrorEval.VALUE_INVALID;
-        }
-        
-        if (retval == null) { // no error yet
-            ve = singleOperandEvaluate(args[1], srcRow, srcCol);
-            if (ve instanceof NumericValueEval) {
-                d1 = ((NumericValueEval) ve).getNumberValue();
-            }
-            else if (ve instanceof BlankEval) {
-                // do nothing
-            }
-            else {
-                retval = ErrorEval.VALUE_INVALID;
-            }
-        }
-        if (retval == null) {
-            retval = (Double.isNaN(d0) || Double.isNaN(d1)) 
-                    ? (ValueEval) ErrorEval.VALUE_INVALID 
-                    : new NumberEval(d0 - d1);
-        }
-        return retval;
-    }
+       public static final OperationEval instance = new SubtractEval();
 
-    public int getNumberOfOperands() {
-        return delegate.getNumberOfOperands();
-    }
+       private SubtractEval() {
+       }
 
-    public int getType() {
-        return delegate.getType();
-    }
+       protected double evaluate(double d0, double d1) {
+               return d0 - d1;
+       }
 }
diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/TwoOperandNumericOperation.java b/src/java/org/apache/poi/hssf/record/formula/eval/TwoOperandNumericOperation.java
new file mode 100644 (file)
index 0000000..665ba4b
--- /dev/null
@@ -0,0 +1,55 @@
+/* ====================================================================
+   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.eval;
+
+/**
+ * @author Josh Micich
+ */
+abstract class TwoOperandNumericOperation implements OperationEval {
+
+       public final int getType() {
+       // TODO - remove
+        throw new RuntimeException("obsolete code should not be called");
+    }
+    protected final double singleOperandEvaluate(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException {
+       ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
+               if (ve instanceof BlankEval) {
+                       return 0.0;
+               }
+               return OperandResolver.coerceValueToDouble(ve);
+    }
+    
+    public final Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
+               double result;
+               try {
+                       double d0 = singleOperandEvaluate(args[0], srcCellRow, srcCellCol);
+                       double d1 = singleOperandEvaluate(args[1], srcCellRow, srcCellCol);
+                       result = evaluate(d0, d1);
+                       if (Double.isNaN(result) || Double.isInfinite(result)) {
+                               return ErrorEval.NUM_ERROR;
+                       }
+               } catch (EvaluationException e) {
+                       return e.getErrorEval();
+               }
+       return new NumberEval(result);
+    }
+       protected abstract double evaluate(double d0, double d1) throws EvaluationException;
+       public final int getNumberOfOperands() {
+               return 2;
+       }
+}
index ef6f533ea52d3afff1c69ad0549ffffea3662dc5..8174429e01b2745684bdc778868c8a7a09e767bb 100644 (file)
@@ -1,75 +1,56 @@
-/*
-* 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.
-*/
+/* ====================================================================
+   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.eval;
 
-import org.apache.poi.hssf.record.formula.Ptg;
-import org.apache.poi.hssf.record.formula.UnaryMinusPtg;
 
 /**
  * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
  *  
  */
-public final class UnaryMinusEval extends NumericOperationEval {
-
-    private UnaryMinusPtg delegate;
-    private static final ValueEvalToNumericXlator NUM_XLATOR = 
-        new ValueEvalToNumericXlator((short)
-                ( ValueEvalToNumericXlator.BOOL_IS_PARSED 
-                | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
-                | ValueEvalToNumericXlator.STRING_IS_PARSED
-                | ValueEvalToNumericXlator.REF_STRING_IS_PARSED
-                ));
-
-
-    public UnaryMinusEval(Ptg ptg) {
-        this.delegate = (UnaryMinusPtg) ptg;
-    }
-    
-    protected ValueEvalToNumericXlator getXlator() {
-        return NUM_XLATOR;
-    }
-
-    public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
-       if(args.length != 1) {
-               return ErrorEval.VALUE_INVALID;
-       }
-        double d = 0;
-        
-        ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol);
-        if (ve instanceof NumericValueEval) {
-            d = ((NumericValueEval) ve).getNumberValue();
-        }
-        else if (ve instanceof BlankEval) {
-            // do nothing
-        }
-        else if (ve instanceof ErrorEval) {
-            return ve;
-        }
-        
-        return new NumberEval(-d);
+public final class UnaryMinusEval implements OperationEval {
+
+       public static final OperationEval instance = new UnaryMinusEval();
+
+       private UnaryMinusEval() {
+       }
+
+       public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
+               if (args.length != 1) {
+                       return ErrorEval.VALUE_INVALID;
+               }
+       double d;
+               try {
+                       ValueEval ve = OperandResolver.getSingleValue(args[0], srcRow, srcCol);
+                       if (ve instanceof BlankEval) {
+                               return NumberEval.ZERO;
+                       }
+                       d = OperandResolver.coerceValueToDouble(ve);
+               } catch (EvaluationException e) {
+                       return e.getErrorEval();
+               }
+               return new NumberEval(-d);
+       }
+
+       public int getNumberOfOperands() {
+               return 1;
+       }
+       public final int getType() {
+       // TODO - remove
+        throw new RuntimeException("obsolete code should not be called");
     }
-
-    public int getNumberOfOperands() {
-        return delegate.getNumberOfOperands();
-    }
-
-    public int getType() {
-        return delegate.getType();
-    }
-
 }
index edcc7bee790a84cd7dc88a73cdb7fc75a256ba4c..66c5f6801713abf4fada69572cf97b8db5668d97 100644 (file)
@@ -17,8 +17,6 @@
 
 package org.apache.poi.hssf.record.formula.eval;
 
-import org.apache.poi.hssf.record.formula.Ptg;
-import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
 
 /**
  * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
@@ -26,13 +24,9 @@ import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
  */
 public final class UnaryPlusEval implements OperationEval {
 
-    private UnaryPlusPtg delegate;
+    public static final OperationEval instance = new UnaryPlusEval();
     
-    /**
-     * called by reflection
-     */
-    public UnaryPlusEval(Ptg ptg) {
-        this.delegate = (UnaryPlusPtg) ptg;
+    private UnaryPlusEval() {
     }
 
     public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
@@ -59,10 +53,10 @@ public final class UnaryPlusEval implements OperationEval {
     }
 
     public int getNumberOfOperands() {
-        return delegate.getNumberOfOperands();
+        return 1;
     }
 
     public int getType() {
-        return delegate.getType();
+        throw new RuntimeException("obsolete code should not be called");
     }
 }
index 1abcf34d2b90105392de7543390705e4d2a9a836..46aa0ddf6990e7e72dd968bb0cdc2799a13b8e82 100644 (file)
@@ -55,49 +55,40 @@ public final class ValueEvalToNumericXlator {
      * @param eval
      */
     public ValueEval attemptXlateToNumeric(ValueEval eval) {
-        ValueEval retval = null;
         
         if (eval == null) {
-            retval = BlankEval.INSTANCE;
+            throw new IllegalArgumentException("eval must not be null");
         }
         
         // most common case - least worries :)
-        else if (eval instanceof NumberEval) {
-            retval = eval; 
+        if (eval instanceof NumberEval) {
+            return eval; 
         }
         
-        // booleval
-        else if (eval instanceof BoolEval) {
-            retval = ((flags & BOOL_IS_PARSED) > 0)
+        if (eval instanceof BoolEval) {
+            return ((flags & BOOL_IS_PARSED) > 0)
                 ? (NumericValueEval) eval
                 : xlateBlankEval(BLANK_IS_PARSED);
         } 
         
-        // stringeval 
-        else if (eval instanceof StringEval) {
-            retval = xlateStringEval((StringEval) eval); // TODO: recursive call needed
+        if (eval instanceof StringEval) {
+            return xlateStringEval((StringEval) eval); // TODO: recursive call needed
         }
         
-        // refeval
-        else if (eval instanceof RefEval) {
-            retval = xlateRefEval((RefEval) eval);
+        if (eval instanceof RefEval) {
+            return xlateRefEval((RefEval) eval);
         }
         
-        // erroreval
-        else if (eval instanceof ErrorEval) {
-            retval = eval;
+        if (eval instanceof ErrorEval) {
+            return eval;
         }
         
-        else if (eval instanceof BlankEval) {
-            retval = xlateBlankEval(BLANK_IS_PARSED);
+        if (eval instanceof BlankEval) {
+            return xlateBlankEval(BLANK_IS_PARSED);
         }
         
         // probably AreaEval? then not acceptable.
-        else {
-            throw new RuntimeException("Invalid ValueEval type passed for conversion: " + eval.getClass());
-        }
-        
-        return retval;
+        throw new RuntimeException("Invalid ValueEval type passed for conversion: " + eval.getClass());
     }
     
     /**
index 8604eadc3741a12c20e65023c866ef8f89261500..2bbf6f5cd571bbf9d04c12c259886d656ad1a30e 100644 (file)
@@ -60,8 +60,7 @@ public final class Hlookup implements Function {
                        AreaEval tableArray = LookupUtils.resolveTableArrayArg(args[1]);
                        boolean isRangeLookup = LookupUtils.resolveRangeLookupArg(arg3, srcCellRow, srcCellCol);
                        int colIndex = LookupUtils.lookupIndexOfValue(lookupValue, LookupUtils.createRowVector(tableArray, 0), isRangeLookup);
-                       ValueEval veColIndex = OperandResolver.getSingleValue(args[2], srcCellRow, srcCellCol);
-                       int rowIndex = LookupUtils.resolveRowOrColIndexArg(veColIndex);
+                       int rowIndex = LookupUtils.resolveRowOrColIndexArg(args[2], srcCellRow, srcCellCol);
                        ValueVector resultCol = createResultColumnVector(tableArray, rowIndex);
                        return resultCol.getItem(colIndex);
                } catch (EvaluationException e) {
@@ -73,15 +72,14 @@ public final class Hlookup implements Function {
        /**
         * Returns one column from an <tt>AreaEval</tt>
         * 
-        * @throws EvaluationException (#VALUE!) if colIndex is negative, (#REF!) if colIndex is too high
+        * @param rowIndex assumed to be non-negative
+        * 
+        * @throws EvaluationException (#REF!) if colIndex is too high
         */
-       private ValueVector createResultColumnVector(AreaEval tableArray, int colIndex) throws EvaluationException {
-               if(colIndex < 0) {
-                       throw EvaluationException.invalidValue();
-               }
-               if(colIndex >= tableArray.getWidth()) {
+       private ValueVector createResultColumnVector(AreaEval tableArray, int rowIndex) throws EvaluationException {
+               if(rowIndex >= tableArray.getHeight()) {
                        throw EvaluationException.invalidRef();
                }
-               return LookupUtils.createRowVector(tableArray, colIndex);
+               return LookupUtils.createRowVector(tableArray, rowIndex);
        }
 }
index e6a3ec81c2f23edd79186b13d8ccbe0869ba031f..ee67ef8ec59bfe69e5dda72caeb74cf0b046b7a2 100644 (file)
@@ -322,30 +322,45 @@ final class LookupUtils {
         *      <tr><td>&lt;blank&gt;</td><td>&nbsp;</td><td>#VALUE!</td></tr>
         *    </table><br/>
         *
-        *  * Note - out of range errors (both too high and too low) are handled by the caller.
-        * @return column or row index as a zero-based value
-        *
+        * Note - out of range errors (result index too high) are handled by the caller.
+        * @return column or row index as a zero-based value, never negative.
+        * @throws EvaluationException when the specified arg cannot be coerced to a non-negative integer
         */
-       public static int resolveRowOrColIndexArg(ValueEval veRowColIndexArg) throws EvaluationException {
-               if(veRowColIndexArg == null) {
+       public static int resolveRowOrColIndexArg(Eval rowColIndexArg, int srcCellRow, int srcCellCol) throws EvaluationException {
+               if(rowColIndexArg == null) {
                        throw new IllegalArgumentException("argument must not be null");
                }
-               if(veRowColIndexArg instanceof BlankEval) {
-                       throw EvaluationException.invalidValue();
+               
+               ValueEval veRowColIndexArg;
+               try {
+                       veRowColIndexArg = OperandResolver.getSingleValue(rowColIndexArg, srcCellRow, (short)srcCellCol);
+               } catch (EvaluationException e) {
+                       // All errors get translated to #REF!
+                       throw EvaluationException.invalidRef();
                }
-               if(veRowColIndexArg instanceof StringEval) {
-                       StringEval se = (StringEval) veRowColIndexArg;
-                       String strVal = se.getStringValue();
-                       Double dVal = OperandResolver.parseDouble(strVal);
-                       if(dVal == null) {
-                               // String does not resolve to a number. Raise #VALUE! error.
-                               throw EvaluationException.invalidRef();
-                               // This includes text booleans "TRUE" and "FALSE".  They are not valid.
+               int oneBasedIndex;
+               if(veRowColIndexArg instanceof BlankEval) {
+                       oneBasedIndex = 0;
+               } else {
+                       if(veRowColIndexArg instanceof StringEval) {
+                               StringEval se = (StringEval) veRowColIndexArg;
+                               String strVal = se.getStringValue();
+                               Double dVal = OperandResolver.parseDouble(strVal);
+                               if(dVal == null) {
+                                       // String does not resolve to a number. Raise #REF! error.
+                                       throw EvaluationException.invalidRef();
+                                       // This includes text booleans "TRUE" and "FALSE".  They are not valid.
+                               }
+                               // else - numeric value parses OK
                        }
-                       // else - numeric value parses OK
+                       // actual BoolEval values get interpreted as FALSE->0 and TRUE->1
+                       oneBasedIndex = OperandResolver.coerceValueToInt(veRowColIndexArg);
+               }
+               if (oneBasedIndex < 1) {
+                       // note this is asymmetric with the errors when the index is too large (#REF!)  
+                       throw EvaluationException.invalidValue();
                }
-               // actual BoolEval values get interpreted as FALSE->0 and TRUE->1
-               return OperandResolver.coerceValueToInt(veRowColIndexArg) - 1;
+               return oneBasedIndex - 1; // convert to zero based
        }
 
 
@@ -583,11 +598,13 @@ final class LookupUtils {
                return maxIx - 1;
        }
 
-       public static LookupValueComparer createLookupComparer(ValueEval lookupValue) throws EvaluationException {
+       public static LookupValueComparer createLookupComparer(ValueEval lookupValue) {
 
-               if (lookupValue instanceof BlankEval) {
-                       // blank eval can never be found in a lookup array
-                       throw new EvaluationException(ErrorEval.NA);
+               if (lookupValue == BlankEval.INSTANCE) {
+                       // blank eval translates to zero
+                       // Note - a blank eval in the lookup column/row never matches anything
+                       // empty string in the lookup column/row can only be matched by explicit emtpty string
+                       return new NumberLookupComparer(NumberEval.ZERO);
                }
                if (lookupValue instanceof StringEval) {
                        return new StringLookupComparer((StringEval) lookupValue);
index 54f7d465e5de77d75946c61fec8dae63d2a93610..28923c0f374eea8c09707a1589807a41b6b1f459 100644 (file)
@@ -60,8 +60,7 @@ public final class Vlookup implements Function {
                        AreaEval tableArray = LookupUtils.resolveTableArrayArg(args[1]);
                        boolean isRangeLookup = LookupUtils.resolveRangeLookupArg(arg3, srcCellRow, srcCellCol);
                        int rowIndex = LookupUtils.lookupIndexOfValue(lookupValue, LookupUtils.createColumnVector(tableArray, 0), isRangeLookup);
-                       ValueEval veColIndex = OperandResolver.getSingleValue(args[2], srcCellRow, srcCellCol);
-                       int colIndex = LookupUtils.resolveRowOrColIndexArg(veColIndex);
+                       int colIndex = LookupUtils.resolveRowOrColIndexArg(args[2], srcCellRow, srcCellCol);
                        ValueVector resultCol = createResultColumnVector(tableArray, colIndex);
                        return resultCol.getItem(rowIndex);
                } catch (EvaluationException e) {
@@ -73,12 +72,11 @@ public final class Vlookup implements Function {
        /**
         * Returns one column from an <tt>AreaEval</tt>
         * 
-        * @throws EvaluationException (#VALUE!) if colIndex is negative, (#REF!) if colIndex is too high
+        * @param colIndex assumed to be non-negative
+        * 
+        * @throws EvaluationException (#REF!) if colIndex is too high
         */
        private ValueVector createResultColumnVector(AreaEval tableArray, int colIndex) throws EvaluationException {
-               if(colIndex < 0) {
-                       throw EvaluationException.invalidValue();
-               }
                if(colIndex >= tableArray.getWidth()) {
                        throw EvaluationException.invalidRef();
                }
index 8d7c0ada8119efb3ad325c832180342cb2fd651d..4fde9e6cc3879bfe19001c1c6ed8497232697ead 100644 (file)
@@ -23,15 +23,15 @@ import org.apache.poi.hssf.util.RangeAddress;
 import org.apache.poi.ss.usermodel.Name;
 
 /**
- * High Level Representation of a 'defined name' which could be a 'built-in' name, 
+ * High Level Representation of a 'defined name' which could be a 'built-in' name,
  * 'named range' or name of a user defined function.
- * 
+ *
  * @author Libin Roman (Vista Portal LDT. Developer)
  */
 public class HSSFName implements Name {
     private HSSFWorkbook _book;
     private NameRecord _definedNameRec;
-    
+
     /** Creates new HSSFName   - called by HSSFWorkbook to create a sheet from
      * scratch.
      *
@@ -43,58 +43,57 @@ public class HSSFName implements Name {
         _book = book;
         _definedNameRec = name;
     }
-    
+
     /** Get the sheets name which this named range is referenced to
      * @return sheet name, which this named range referred to
-     */    
+     */
     public String getSheetName() {
         short indexToExternSheet = _definedNameRec.getExternSheetNumber();
-        
+
         return _book.getWorkbook().findSheetNameFromExternSheet(indexToExternSheet);
     }
-    
-    /** 
+
+    /**
      * @return text name of this defined name
-     */    
+     */
     public String getNameName(){
         return _definedNameRec.getNameText();
     }
-    
-    /** 
+
+    /**
      * sets the name of the named range
      * @param nameName named range name to set
-     */    
+     */
     public void setNameName(String nameName){
         _definedNameRec.setNameText(nameName);
-        _definedNameRec.setNameTextLength((byte)nameName.length());
         Workbook wb = _book.getWorkbook();
-        
+
         //Check to ensure no other names have the same case-insensitive name
         for ( int i = wb.getNumNames()-1; i >=0; i-- )
         {
-               NameRecord rec = wb.getNameRecord(i);
-               if (rec != _definedNameRec) {
-                       if (rec.getNameText().equalsIgnoreCase(getNameName()))
-                               throw new IllegalArgumentException("The workbook already contains this name (case-insensitive)");
-               }
+            NameRecord rec = wb.getNameRecord(i);
+            if (rec != _definedNameRec) {
+                if (rec.getNameText().equalsIgnoreCase(getNameName()))
+                    throw new IllegalArgumentException("The workbook already contains this name (case-insensitive)");
+            }
         }
     }
 
-    /** 
+    /**
      * Note - this method only applies to named ranges
      * @return the formula text defining the named range
-     */    
+     */
     public String getReference() {
-       if (_definedNameRec.isFunctionName()) {
-               throw new IllegalStateException("Only applicable to named ranges");
-       }
+        if (_definedNameRec.isFunctionName()) {
+            throw new IllegalStateException("Only applicable to named ranges");
+        }
         return _definedNameRec.getAreaReference(_book);
     }
 
-    /** 
+    /**
      * sets the sheet name which this named range referenced to
      * @param sheetName the sheet name of the reference
-     */    
+     */
     private void setSheetName(String sheetName){
         int sheetNumber = _book.getSheetIndex(sheetName);
         short externSheetNumber = (short)
@@ -102,11 +101,11 @@ public class HSSFName implements Name {
         _definedNameRec.setExternSheetNumber(externSheetNumber);
     }
 
-  
-    /** 
+
+    /**
      * sets the reference of this named range
      * @param ref the reference to set
-     */    
+     */
     public void setReference(String ref){
 
         RangeAddress ra = new RangeAddress(ref);
@@ -117,7 +116,7 @@ public class HSSFName implements Name {
             setSheetName(sheetName);
         }
 
-               //allow the poi utilities to parse it out
+        //allow the poi utilities to parse it out
         _definedNameRec.setAreaReference(ref);
     }
 
@@ -131,7 +130,7 @@ public class HSSFName implements Name {
         return "#REF!".endsWith(ref);
     }
     public boolean isFunctionName() {
-       return _definedNameRec.isFunctionName();
+        return _definedNameRec.isFunctionName();
     }
     public String toString() {
         StringBuffer sb = new StringBuffer(64);
index 801b29878323b6dc8bf19fb64db3a02eb6a6a9a9..dd6d5b38324e224fe10fa10f02fe5945de69b8e1 100644 (file)
@@ -27,7 +27,6 @@ import java.util.ArrayList;
 import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Stack;
 
 import org.apache.poi.POIDocument;
 import org.apache.poi.ddf.EscherBSERecord;
@@ -36,6 +35,7 @@ import org.apache.poi.ddf.EscherBlipRecord;
 import org.apache.poi.ddf.EscherRecord;
 import org.apache.poi.hssf.model.Sheet;
 import org.apache.poi.hssf.model.Workbook;
+import org.apache.poi.hssf.model.DrawingManager2;
 import org.apache.poi.hssf.record.AbstractEscherHolderRecord;
 import org.apache.poi.hssf.record.BackupRecord;
 import org.apache.poi.hssf.record.DrawingGroupRecord;
@@ -55,6 +55,8 @@ import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor;
 import org.apache.poi.hssf.record.formula.Area3DPtg;
 import org.apache.poi.hssf.record.formula.MemFuncPtg;
 import org.apache.poi.hssf.record.formula.NameXPtg;
+import org.apache.poi.hssf.record.formula.Ptg;
+import org.apache.poi.hssf.record.formula.Ref3DPtg;
 import org.apache.poi.hssf.record.formula.UnionPtg;
 import org.apache.poi.hssf.util.CellReference;
 import org.apache.poi.hssf.util.SheetReferences;
@@ -667,33 +669,81 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
      * @return HSSFSheet representing the cloned sheet.
      */
 
-    public HSSFSheet cloneSheet(int sheetNum) {
-        validateSheetIndex(sheetNum);
-        HSSFSheet srcSheet = (HSSFSheet) _sheets.get(sheetNum);
-        String srcName = workbook.getSheetName(sheetNum);
+    public HSSFSheet cloneSheet(int sheetIndex) {
+        validateSheetIndex(sheetIndex);
+        HSSFSheet srcSheet = (HSSFSheet) _sheets.get(sheetIndex);
+        String srcName = workbook.getSheetName(sheetIndex);
         HSSFSheet clonedSheet = srcSheet.cloneSheet(this);
         clonedSheet.setSelected(false);
         clonedSheet.setActive(false);
 
+        String name = getUniqueSheetName(srcName);
+        int newSheetIndex = _sheets.size(); 
         _sheets.add(clonedSheet);
-        int i = 1;
+        workbook.setSheetName(newSheetIndex, name);
+        
+        // Check this sheet has an autofilter, (which has a built-in NameRecord at workbook level)
+        int filterDbNameIndex = findExistingBuiltinNameRecordIdx(sheetIndex, NameRecord.BUILTIN_FILTER_DB);
+        if (filterDbNameIndex >=0) {
+            NameRecord origNameRecord = workbook.getNameRecord(filterDbNameIndex);
+            // copy original formula but adjust 3D refs to the new external sheet index
+            int newExtSheetIx = getExternalSheetIndex(newSheetIndex);
+            Ptg[] ptgs = origNameRecord.getNameDefinition();
+            for (int i=0; i< ptgs.length; i++) {
+                Ptg ptg = ptgs[i];
+                ptg = ptg.copy();
+                
+                if (ptg instanceof Area3DPtg) {
+                    Area3DPtg a3p = (Area3DPtg) ptg;
+                    a3p.setExternSheetIndex(newExtSheetIx);
+                } else if (ptg instanceof Ref3DPtg) {
+                    Ref3DPtg r3p = (Ref3DPtg) ptg;
+                    r3p.setExternSheetIndex(newExtSheetIx);
+                }
+                ptgs[i] = ptg;
+            }
+            NameRecord newNameRecord = workbook.createBuiltInName(NameRecord.BUILTIN_FILTER_DB, newSheetIndex+1);
+            newNameRecord.setNameDefinition(ptgs);
+            newNameRecord.setHidden(true);
+            HSSFName newName = new HSSFName(this, newNameRecord);
+            names.add(newName);
+
+            workbook.cloneDrawings(clonedSheet.getSheet());
+        }
+        // TODO - maybe same logic required for other/all built-in name records
+        
+        return clonedSheet;
+    }
+
+    private String getUniqueSheetName(String srcName) {
+        int uniqueIndex = 2;
+        String baseName = srcName;
+        int bracketPos = srcName.lastIndexOf('(');
+        if (bracketPos > 0 && srcName.endsWith(")")) {
+            String suffix = srcName.substring(bracketPos + 1, srcName.length() - ")".length());
+            try {
+                uniqueIndex = Integer.parseInt(suffix.trim());
+                uniqueIndex++;
+                baseName=srcName.substring(0, bracketPos).trim();
+            } catch (NumberFormatException e) {
+                // contents of brackets not numeric
+            }
+        }
         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 + ")";
+            String index = Integer.toString(uniqueIndex++);
+            String name;
+            if (baseName.length() + index.length() + 2 < 31) {
+                name = baseName + " (" + index + ")";
             } else {
-                name = name.substring(0, 31 - index.length() - 2) + "(" + index + ")";
+                name = baseName.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 name;
             }
         }
-        return clonedSheet;
     }
 
     /**
@@ -907,7 +957,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
         boolean removingRange =
                 startColumn == -1 && endColumn == -1 && startRow == -1 && endRow == -1;
 
-        int rowColHeaderNameIndex = findExistingRowColHeaderNameRecordIdx(sheetIndex);
+        int rowColHeaderNameIndex = findExistingBuiltinNameRecordIdx(sheetIndex, NameRecord.BUILTIN_PRINT_TITLE);
         if (removingRange) {
             if (rowColHeaderNameIndex >= 0) {
                 workbook.removeName(rowColHeaderNameIndex);
@@ -925,29 +975,27 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
             isNewRecord = false;
         }
 
-        short definitionTextLength = settingRowAndColumn ? (short)0x001a : (short)0x000b;
-        nameRecord.setDefinitionTextLength(definitionTextLength); // TODO - remove
-
-        Stack ptgs = new Stack();
+        List temp = new ArrayList();
 
         if (settingRowAndColumn) {
             final int exprsSize = 2 * 11 + 1; // 2 * Area3DPtg.SIZE + UnionPtg.SIZE
-            ptgs.add(new MemFuncPtg(exprsSize));
+            temp.add(new MemFuncPtg(exprsSize));
         }
         if (startColumn >= 0) {
             Area3DPtg colArea = new Area3DPtg(0, MAX_ROW, startColumn, endColumn, 
                     false, false, false, false, externSheetIndex);
-            ptgs.add(colArea);
+            temp.add(colArea);
         }
         if (startRow >= 0) {
             Area3DPtg rowArea = new Area3DPtg(startRow, endRow, 0, MAX_COLUMN, 
                     false, false, false, false, externSheetIndex);
-            ptgs.add(rowArea);
+            temp.add(rowArea);
         }
-        if (settingRowAndColumn)
-        {
-            ptgs.add(UnionPtg.instance);
+        if (settingRowAndColumn) {
+            temp.add(UnionPtg.instance);
         }
+        Ptg[] ptgs = new Ptg[temp.size()];
+        temp.toArray(ptgs);
         nameRecord.setNameDefinition(ptgs);
 
         if (isNewRecord)
@@ -963,13 +1011,13 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
     }
 
 
-    private int findExistingRowColHeaderNameRecordIdx(int sheetIndex) {
+    private int findExistingBuiltinNameRecordIdx(int sheetIndex, byte builtinCode) {
         for(int defNameIndex =0; defNameIndex<names.size(); defNameIndex++) {
             NameRecord r = workbook.getNameRecord(defNameIndex);
             if (r == null) {
                 throw new RuntimeException("Unable to find all defined names to iterate over");
             }
-            if (!isRowColHeaderRecord( r )) {
+            if (!r.isBuiltInName() || r.getBuiltInName() != builtinCode) {
                 continue;
             }
             if(r.getSheetNumber() == 0) {
@@ -985,10 +1033,6 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
         return -1;
     }
 
-    private static boolean isRowColHeaderRecord(NameRecord r) {
-        return r.isBuiltInName() && r.getBuiltInName() == NameRecord.BUILTIN_PRINT_TITLE;
-    }
-
     /**
      * create a new Font and add it to the workbook's font table
      * @return new font object
@@ -1288,6 +1332,9 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
 
         return result;
     }
+       public NameRecord getNameRecord(int nameIndex) {
+               return getWorkbook().getNameRecord(nameIndex);
+       }
 
     /** gets the named range name
      * @param index the named range index (0 based)
diff --git a/src/java/org/apache/poi/hssf/usermodel/OperationEvaluatorFactory.java b/src/java/org/apache/poi/hssf/usermodel/OperationEvaluatorFactory.java
new file mode 100755 (executable)
index 0000000..21dc067
--- /dev/null
@@ -0,0 +1,184 @@
+/* ====================================================================
+   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.usermodel;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.poi.hssf.record.formula.AddPtg;
+import org.apache.poi.hssf.record.formula.ConcatPtg;
+import org.apache.poi.hssf.record.formula.DividePtg;
+import org.apache.poi.hssf.record.formula.EqualPtg;
+import org.apache.poi.hssf.record.formula.ExpPtg;
+import org.apache.poi.hssf.record.formula.FuncPtg;
+import org.apache.poi.hssf.record.formula.FuncVarPtg;
+import org.apache.poi.hssf.record.formula.GreaterEqualPtg;
+import org.apache.poi.hssf.record.formula.GreaterThanPtg;
+import org.apache.poi.hssf.record.formula.LessEqualPtg;
+import org.apache.poi.hssf.record.formula.LessThanPtg;
+import org.apache.poi.hssf.record.formula.MultiplyPtg;
+import org.apache.poi.hssf.record.formula.NotEqualPtg;
+import org.apache.poi.hssf.record.formula.OperationPtg;
+import org.apache.poi.hssf.record.formula.PercentPtg;
+import org.apache.poi.hssf.record.formula.PowerPtg;
+import org.apache.poi.hssf.record.formula.Ptg;
+import org.apache.poi.hssf.record.formula.SubtractPtg;
+import org.apache.poi.hssf.record.formula.UnaryMinusPtg;
+import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
+import org.apache.poi.hssf.record.formula.eval.AddEval;
+import org.apache.poi.hssf.record.formula.eval.ConcatEval;
+import org.apache.poi.hssf.record.formula.eval.DivideEval;
+import org.apache.poi.hssf.record.formula.eval.EqualEval;
+import org.apache.poi.hssf.record.formula.eval.FuncVarEval;
+import org.apache.poi.hssf.record.formula.eval.GreaterEqualEval;
+import org.apache.poi.hssf.record.formula.eval.GreaterThanEval;
+import org.apache.poi.hssf.record.formula.eval.LessEqualEval;
+import org.apache.poi.hssf.record.formula.eval.LessThanEval;
+import org.apache.poi.hssf.record.formula.eval.MultiplyEval;
+import org.apache.poi.hssf.record.formula.eval.NotEqualEval;
+import org.apache.poi.hssf.record.formula.eval.OperationEval;
+import org.apache.poi.hssf.record.formula.eval.PercentEval;
+import org.apache.poi.hssf.record.formula.eval.PowerEval;
+import org.apache.poi.hssf.record.formula.eval.SubtractEval;
+import org.apache.poi.hssf.record.formula.eval.UnaryMinusEval;
+import org.apache.poi.hssf.record.formula.eval.UnaryPlusEval;
+
+/**
+ * This class creates <tt>OperationEval</tt> instances to help evaluate <tt>OperationPtg</tt>
+ * formula tokens.
+ * 
+ * @author Josh Micich
+ */
+final class OperationEvaluatorFactory {
+       private static final Class[] OPERATION_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class };
+       // TODO - use singleton instances directly instead of reflection
+       private static final Map _constructorsByPtgClass = initialiseConstructorsMap();
+       private static final Map _instancesByPtgClass = initialiseInstancesMap();
+       
+       private OperationEvaluatorFactory() {
+               // no instances of this class
+       }
+       
+       private static Map initialiseConstructorsMap() {
+               Map m = new HashMap(32);
+               add(m, ConcatPtg.class, ConcatEval.class);
+               add(m, FuncPtg.class, FuncVarEval.class);
+               add(m, FuncVarPtg.class, FuncVarEval.class);
+               return m;
+       }
+       private static Map initialiseInstancesMap() {
+               Map m = new HashMap(32);
+               add(m, EqualPtg.class, EqualEval.instance);
+               add(m, GreaterEqualPtg.class, GreaterEqualEval.instance);
+               add(m, GreaterThanPtg.class, GreaterThanEval.instance);
+               add(m, LessEqualPtg.class, LessEqualEval.instance);
+               add(m, LessThanPtg.class, LessThanEval.instance);
+               add(m, NotEqualPtg.class, NotEqualEval.instance);
+
+               add(m, AddPtg.class, AddEval.instance);
+               add(m, DividePtg.class, DivideEval.instance);
+               add(m, MultiplyPtg.class, MultiplyEval.instance);
+               add(m, PercentPtg.class, PercentEval.instance);
+               add(m, PowerPtg.class, PowerEval.instance);
+               add(m, SubtractPtg.class, SubtractEval.instance);
+               add(m, UnaryMinusPtg.class, UnaryMinusEval.instance);
+               add(m, UnaryPlusPtg.class, UnaryPlusEval.instance);
+               return m;
+       }
+
+       private static void add(Map m, Class ptgClass, OperationEval evalInstance) {
+               if(!Ptg.class.isAssignableFrom(ptgClass)) {
+                       throw new IllegalArgumentException("Expected Ptg subclass");
+               }
+               m.put(ptgClass, evalInstance);
+       }
+
+       private static void add(Map m, Class ptgClass, Class evalClass) {
+               // perform some validation now, to keep later exception handlers simple
+               if(!Ptg.class.isAssignableFrom(ptgClass)) {
+                       throw new IllegalArgumentException("Expected Ptg subclass");
+               }
+               
+               if(!OperationEval.class.isAssignableFrom(evalClass)) {
+                       throw new IllegalArgumentException("Expected OperationEval subclass");
+               }
+               if (!Modifier.isPublic(evalClass.getModifiers())) {
+                       throw new RuntimeException("Eval class must be public");
+               }
+               if (Modifier.isAbstract(evalClass.getModifiers())) {
+                       throw new RuntimeException("Eval class must not be abstract");
+               }
+               
+               Constructor constructor;
+               try {
+                       constructor = evalClass.getDeclaredConstructor(OPERATION_CONSTRUCTOR_CLASS_ARRAY);
+               } catch (NoSuchMethodException e) {
+                       throw new RuntimeException("Missing constructor");
+               }
+               if (!Modifier.isPublic(constructor.getModifiers())) {
+                       throw new RuntimeException("Eval constructor must be public");
+               }
+               m.put(ptgClass, constructor);
+       }
+
+       /**
+        * returns the OperationEval concrete impl instance corresponding
+        * to the supplied operationPtg
+        */
+       public static OperationEval create(OperationPtg ptg) {
+               if(ptg == null) {
+                       throw new IllegalArgumentException("ptg must not be null");
+               }
+               Object result;
+               
+               Class ptgClass = ptg.getClass();
+               
+               result = _instancesByPtgClass.get(ptgClass);
+               if (result != null) {
+                       return (OperationEval) result;
+               }
+               
+               
+               Constructor constructor = (Constructor) _constructorsByPtgClass.get(ptgClass);
+               if(constructor == null) {
+                       if(ptgClass == ExpPtg.class) {
+                               // ExpPtg is used for array formulas and shared formulas.
+                               // it is currently unsupported, and may not even get implemented here
+                               throw new RuntimeException("ExpPtg currently not supported");
+                       }
+                       throw new RuntimeException("Unexpected operation ptg class (" + ptgClass.getName() + ")");
+               }
+               
+               Object[] initargs = { ptg };
+               try {
+                       result = constructor.newInstance(initargs);
+               } catch (IllegalArgumentException e) {
+                       throw new RuntimeException(e);
+               } catch (InstantiationException e) {
+                       throw new RuntimeException(e);
+               } catch (IllegalAccessException e) {
+                       throw new RuntimeException(e);
+               } catch (InvocationTargetException e) {
+                       throw new RuntimeException(e);
+               }
+               return (OperationEval) result;
+       }
+}
diff --git a/src/java/org/apache/poi/ss/usermodel/EvaluationCache.java b/src/java/org/apache/poi/ss/usermodel/EvaluationCache.java
new file mode 100644 (file)
index 0000000..75eda02
--- /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.ss.usermodel;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.poi.hssf.record.formula.eval.ValueEval;
+
+/**
+ * Performance optimisation for {@link HSSFFormulaEvaluator}. This class stores previously
+ * calculated values of already visited cells, to avoid unnecessary re-calculation when the 
+ * same cells are referenced multiple times
+ * 
+ * 
+ * @author Josh Micich
+ */
+final class EvaluationCache {
+       private static final class Key {
+
+               private final int _sheetIndex;
+               private final int _srcRowNum;
+               private final int _srcColNum;
+               private final int _hashCode;
+
+               public Key(int sheetIndex, int srcRowNum, int srcColNum) {
+                       _sheetIndex = sheetIndex;
+                       _srcRowNum = srcRowNum;
+                       _srcColNum = srcColNum;
+                       _hashCode = sheetIndex + srcRowNum + srcColNum;
+               }
+
+               public int hashCode() {
+                       return _hashCode;
+               }
+
+               public boolean equals(Object obj) {
+                       Key other = (Key) obj;
+                       if (_hashCode != other._hashCode) {
+                               return false;
+                       }
+                       if (_sheetIndex != other._sheetIndex) {
+                               return false;
+                       }
+                       if (_srcRowNum != other._srcRowNum) {
+                               return false;
+                       }
+                       if (_srcColNum != other._srcColNum) {
+                               return false;
+                       }
+                       return true;
+               }
+       }
+
+       private final Map _valuesByKey;
+
+       /* package */EvaluationCache() {
+               _valuesByKey = new HashMap();
+       }
+
+       public ValueEval getValue(int sheetIndex, int srcRowNum, int srcColNum) {
+               Key key = new Key(sheetIndex, srcRowNum, srcColNum);
+               return (ValueEval) _valuesByKey.get(key);
+       }
+
+       public void setValue(int sheetIndex, int srcRowNum, int srcColNum, ValueEval value) {
+               Key key = new Key(sheetIndex, srcRowNum, srcColNum);
+               if (_valuesByKey.containsKey(key)) {
+                       throw new RuntimeException("Already have cached value for this cell");
+               }
+               _valuesByKey.put(key, value);
+       }
+
+       /**
+        * Should be called whenever there are changes to input cells in the evaluated workbook.
+        */
+       public void clear() {
+               _valuesByKey.clear();
+       }
+}
index aa71c68c54877cbf901d3119d38985256415c7f7..30e78c33e7fd44b54b0e31f82efc4ed07cf5a251 100644 (file)
@@ -20,7 +20,11 @@ package org.apache.poi.ss.usermodel;
 import java.util.Iterator;
 import java.util.Stack;
 
+import org.apache.poi.ss.util.AreaReference;
+import org.apache.poi.ss.util.CellReference;
+
 import org.apache.poi.hssf.model.FormulaParser;
+import org.apache.poi.hssf.record.NameRecord;
 import org.apache.poi.hssf.record.formula.Area3DPtg;
 import org.apache.poi.hssf.record.formula.AreaPtg;
 import org.apache.poi.hssf.record.formula.BoolPtg;
@@ -56,17 +60,64 @@ import org.apache.poi.hssf.record.formula.eval.StringEval;
 import org.apache.poi.hssf.record.formula.eval.ValueEval;
 
 /**
+ * Evaluates formula cells.<p/>
+ * 
+ * For performance reasons, this class keeps a cache of all previously calculated intermediate
+ * cell values.  Be sure to call {@link #clearCache()} if any workbook cells are changed between
+ * calls to evaluate~ methods on this class.
+ * 
  * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *
+ * @author Josh Micich
  */
 public class FormulaEvaluator {
+       /**
+        * used to track the number of evaluations
+        */
+    private static final class Counter {
+        public int value;
+        public Counter() {
+            value = 0;
+        }
+    }
 
     protected Sheet _sheet;
     protected Workbook _workbook;
+    private final EvaluationCache _cache;
+
+    private Counter _evaluationCounter;
+
 
     public FormulaEvaluator(Sheet sheet, Workbook workbook) {
+        this(sheet, workbook, new EvaluationCache(), new Counter());
+    }
+    
+    private FormulaEvaluator(Sheet sheet, Workbook workbook, EvaluationCache cache, Counter evaluationCounter) {
         _sheet = sheet;
         _workbook = workbook;
+        _cache = cache;
+        _evaluationCounter = evaluationCounter;
+    }
+
+    /**
+     * for debug use. Used in toString methods
+     */
+    public String getSheetName(Sheet sheet) {
+        return _workbook.getSheetName(_workbook.getSheetIndex(sheet));
+    }
+    /**
+     * for debug/test use
+     */
+    public int getEvaluationCount() {
+        return _evaluationCounter.value;
+    }
+
+    private static boolean isDebugLogEnabled() {
+        return false;
+    }
+    private static void logDebug(String s) {
+        if (isDebugLogEnabled()) {
+            System.out.println(s);
+        }
     }
 
     /**
@@ -75,6 +126,18 @@ public class FormulaEvaluator {
      */
     public void setCurrentRow(Row row) {
         // do nothing
+        if (false) {
+            row.getClass(); // suppress unused parameter compiler warning
+        }
+    }
+
+    /**
+     * Should be called whenever there are changes to input cells in the evaluated workbook.
+     * Failure to call this method after changing cell values will cause incorrect behaviour
+     * of the evaluate~ methods of this class
+     */
+    public void clearCache() {
+        _cache.clear();
     }
 
 
@@ -102,7 +165,7 @@ public class FormulaEvaluator {
                 retval.setErrorValue(cell.getErrorCellValue());
                 break;
             case Cell.CELL_TYPE_FORMULA:
-                retval = getCellValueForEval(internalEvaluate(cell, _sheet, _workbook), _workbook.getCreationHelper());
+                retval = getCellValueForEval(internalEvaluate(cell, _sheet), _workbook.getCreationHelper());
                 break;
             case Cell.CELL_TYPE_NUMERIC:
                 retval = new CellValue(Cell.CELL_TYPE_NUMERIC, _workbook.getCreationHelper());
@@ -140,7 +203,7 @@ public class FormulaEvaluator {
         if (cell != null) {
             switch (cell.getCellType()) {
             case Cell.CELL_TYPE_FORMULA:
-                CellValue cv = getCellValueForEval(internalEvaluate(cell, _sheet, _workbook), _workbook.getCreationHelper());
+                CellValue cv = getCellValueForEval(internalEvaluate(cell, _sheet), _workbook.getCreationHelper());
                 switch (cv.getCellType()) {
                 case Cell.CELL_TYPE_BOOLEAN:
                     cell.setCellValue(cv.getBooleanValue());
@@ -185,7 +248,7 @@ public class FormulaEvaluator {
         if (cell != null) {
             switch (cell.getCellType()) {
             case Cell.CELL_TYPE_FORMULA:
-                CellValue cv = getCellValueForEval(internalEvaluate(cell, _sheet, _workbook), _workbook.getCreationHelper());
+                CellValue cv = getCellValueForEval(internalEvaluate(cell, _sheet), _workbook.getCreationHelper());
                 switch (cv.getCellType()) {
                 case Cell.CELL_TYPE_BOOLEAN:
                     cell.setCellType(Cell.CELL_TYPE_BOOLEAN);
@@ -283,26 +346,40 @@ public class FormulaEvaluator {
      * else a runtime exception will be thrown somewhere inside the method.
      * (Hence this is a private method.)
      */
-    private static ValueEval internalEvaluate(Cell srcCell, Sheet sheet, Workbook workbook) {
+    private ValueEval internalEvaluate(Cell srcCell, Sheet sheet) {
         int srcRowNum = srcCell.getRowIndex();
-        short srcColNum = srcCell.getCellNum();
+        int srcColNum = srcCell.getCellNum();
 
+        ValueEval result;
+
+        int sheetIndex = _workbook.getSheetIndex(sheet);
+        result = _cache.getValue(sheetIndex, srcRowNum, srcColNum);
+        if (result != null) {
+           return result;
+        }
+        _evaluationCounter.value++;
 
         EvaluationCycleDetector tracker = EvaluationCycleDetectorManager.getTracker();
 
-        if(!tracker.startEvaluate(workbook, sheet, srcRowNum, srcColNum)) {
+        if(!tracker.startEvaluate(_workbook, sheet, srcRowNum, srcColNum)) {
             return ErrorEval.CIRCULAR_REF_ERROR;
         }
         try {
-            return evaluateCell(workbook, sheet, srcRowNum, srcColNum, srcCell.getCellFormula());
+            result = evaluateCell(srcRowNum, (short)srcColNum, srcCell.getCellFormula());
         } finally {
-            tracker.endEvaluate(workbook, sheet, srcRowNum, srcColNum);
+            tracker.endEvaluate(_workbook, sheet, srcRowNum, srcColNum);
+            _cache.setValue(sheetIndex, srcRowNum, srcColNum, result);
         }
+        if (isDebugLogEnabled()) {
+            String sheetName = _workbook.getSheetName(sheetIndex);
+            CellReference cr = new CellReference(srcRowNum, srcColNum);
+            logDebug("Evaluated " + sheetName + "!" + cr.formatAsString() + " to " + result.toString());
+        }
+        return result;
     }
-    private static ValueEval evaluateCell(Workbook workbook, Sheet sheet,
-            int srcRowNum, short srcColNum, String cellFormulaText) {
+    private ValueEval evaluateCell(int srcRowNum, short srcColNum, String cellFormulaText) {
 
-        Ptg[] ptgs = FormulaParser.parse(cellFormulaText, workbook);
+        Ptg[] ptgs = FormulaParser.parse(cellFormulaText, _workbook);
 
         Stack stack = new Stack();
         for (int i = 0, iSize = ptgs.length; i < iSize; i++) {
@@ -314,19 +391,10 @@ public class FormulaEvaluator {
                 continue;
             }
             if (ptg instanceof MemErrPtg) { continue; }
-            if (ptg instanceof MissingArgPtg) { continue; }
-            if (ptg instanceof NamePtg) {
-                // named ranges, macro functions
-                NamePtg namePtg = (NamePtg) ptg;
-                stack.push(new NameEval(namePtg.getIndex()));
-                continue;
+            if (ptg instanceof MissingArgPtg) {
+               // TODO - might need to push BlankEval or MissingArgEval
+               continue;
             }
-            if (ptg instanceof NameXPtg) {
-                NameXPtg nameXPtg = (NameXPtg) ptg;
-                stack.push(new NameXEval(nameXPtg.getSheetRefIndex(), nameXPtg.getNameIndex()));
-                continue;
-            }
-            if (ptg instanceof UnknownPtg) { continue; }
             Eval opResult;
             if (ptg instanceof OperationPtg) {
                 OperationPtg optg = (OperationPtg) ptg;
@@ -343,10 +411,15 @@ public class FormulaEvaluator {
                     Eval p = (Eval) stack.pop();
                     ops[j] = p;
                 }
-                opResult = invokeOperation(operation, ops, srcRowNum, srcColNum, workbook, sheet);
+                logDebug("invoke " + operation + " (nAgs=" + numops + ")");
+                opResult = invokeOperation(operation, ops, srcRowNum, srcColNum, _workbook, _sheet);
             } else {
-                opResult = getEvalForPtg(ptg, sheet, workbook);
+                opResult = getEvalForPtg(ptg, _sheet);
+            }
+            if (opResult == null) {
+                throw new RuntimeException("Evaluation result must not be null");
             }
+            logDebug("push " + opResult);
             stack.push(opResult);
         }
 
@@ -403,28 +476,63 @@ public class FormulaEvaluator {
         return operation.evaluate(ops, srcRowNum, srcColNum);
     }
 
+    private Sheet getOtherSheet(int externSheetIndex) {
+        return _workbook.getSheetAt(_workbook.getSheetIndexFromExternSheetIndex(externSheetIndex));
+    }
+    private FormulaEvaluator createEvaluatorForAnotherSheet(Sheet sheet) {
+        return new FormulaEvaluator(sheet, _workbook, _cache, _evaluationCounter);
+    }
+
     /**
      * returns an appropriate Eval impl instance for the Ptg. The Ptg must be
      * one of: Area3DPtg, AreaPtg, ReferencePtg, Ref3DPtg, IntPtg, NumberPtg,
      * StringPtg, BoolPtg <br/>special Note: OperationPtg subtypes cannot be
      * passed here!
      */
-    private static Eval getEvalForPtg(Ptg ptg, Sheet sheet, Workbook workbook) {
+    private Eval getEvalForPtg(Ptg ptg, Sheet sheet) {
+        if (ptg instanceof NamePtg) {
+            // named ranges, macro functions
+            NamePtg namePtg = (NamePtg) ptg;
+            int numberOfNames = _workbook.getNumberOfNames();
+            int nameIndex = namePtg.getIndex();
+            if(nameIndex < 0 || nameIndex >= numberOfNames) {
+                throw new RuntimeException("Bad name index (" + nameIndex
+                        + "). Allowed range is (0.." + (numberOfNames-1) + ")");
+            }
+            if(_workbook instanceof org.apache.poi.hssf.usermodel.HSSFWorkbook) { 
+                               org.apache.poi.hssf.usermodel.HSSFWorkbook hssfWb = 
+                                       (org.apache.poi.hssf.usermodel.HSSFWorkbook)_workbook;
+                               NameRecord nameRecord = hssfWb.getNameRecord(nameIndex);
+                               if (nameRecord.isFunctionName()) {
+                                       return new NameEval(nameRecord.getNameText());
+                               }
+                               if (nameRecord.hasFormula()) {
+                                       return evaluateNameFormula(nameRecord.getNameDefinition(), sheet);
+                               }
+                               throw new RuntimeException("Don't know how to evalate name '" + nameRecord.getNameText() + "'");
+                       } else {
+                               throw new RuntimeException("Don't know how to evaluate name records for XSSF");
+                       }
+        }
+        if (ptg instanceof NameXPtg) {
+            NameXPtg nameXPtg = (NameXPtg) ptg;
+            return new NameXEval(nameXPtg.getSheetRefIndex(), nameXPtg.getNameIndex());
+        }
         if (ptg instanceof RefPtg) {
-            return new LazyRefEval(((RefPtg) ptg), sheet, workbook);
+            return new LazyRefEval(((RefPtg) ptg), sheet, this);
         }
         if (ptg instanceof Ref3DPtg) {
             Ref3DPtg refPtg = (Ref3DPtg) ptg;
-            Sheet xsheet = workbook.getSheetAt(workbook.getSheetIndexFromExternSheetIndex(refPtg.getExternSheetIndex()));
-            return new LazyRefEval(refPtg, xsheet, workbook);
+            Sheet xsheet = getOtherSheet(refPtg.getExternSheetIndex());
+            return new LazyRefEval(refPtg, xsheet, createEvaluatorForAnotherSheet(xsheet));
         }
         if (ptg instanceof AreaPtg) {
-            return new LazyAreaEval(((AreaPtg) ptg), sheet, workbook);
+            return new LazyAreaEval(((AreaPtg) ptg), sheet, this);
         }
         if (ptg instanceof Area3DPtg) {
             Area3DPtg a3dp = (Area3DPtg) ptg;
-            Sheet xsheet = workbook.getSheetAt(workbook.getSheetIndexFromExternSheetIndex(a3dp.getExternSheetIndex()));
-            return new LazyAreaEval(a3dp, xsheet, workbook);
+            Sheet xsheet = getOtherSheet(a3dp.getExternSheetIndex());
+            return new LazyAreaEval(a3dp, xsheet, createEvaluatorForAnotherSheet(xsheet));
         }
 
         if (ptg instanceof IntPtg) {
@@ -442,8 +550,19 @@ public class FormulaEvaluator {
         if (ptg instanceof ErrPtg) {
             return ErrorEval.valueOf(((ErrPtg) ptg).getErrorCode());
         }
+        if (ptg instanceof UnknownPtg) {
+            // TODO - remove UnknownPtg
+            throw new RuntimeException("UnknownPtg not allowed");
+        }
         throw new RuntimeException("Unexpected ptg class (" + ptg.getClass().getName() + ")");
     }
+    private Eval evaluateNameFormula(Ptg[] ptgs, Sheet sheet) {
+        if (ptgs.length > 1) {
+            throw new RuntimeException("Complex name formulas not supported yet");
+        }
+        return getEvalForPtg(ptgs[0], sheet);
+    }
+
     /**
      * Given a cell, find its type and from that create an appropriate ValueEval
      * impl instance and return that. Since the cell could be an external
@@ -453,7 +572,7 @@ public class FormulaEvaluator {
      * @param sheet
      * @param workbook
      */
-    public static ValueEval getEvalForCell(Cell cell, Sheet sheet, Workbook workbook) {
+    public ValueEval getEvalForCell(Cell cell, Sheet sheet) {
 
         if (cell == null) {
             return BlankEval.INSTANCE;
@@ -464,7 +583,7 @@ public class FormulaEvaluator {
             case Cell.CELL_TYPE_STRING:
                 return new StringEval(cell.getRichStringCellValue().getString());
             case Cell.CELL_TYPE_FORMULA:
-                return internalEvaluate(cell, sheet, workbook);
+                return internalEvaluate(cell, sheet);
             case Cell.CELL_TYPE_BOOLEAN:
                 return BoolEval.valueOf(cell.getBooleanCellValue());
             case Cell.CELL_TYPE_BLANK:
index ed136e03790f7fd54391c0790302218e890ec41b..ca8a92312a1af55df81037bc2155f49a57ca4835 100755 (executable)
@@ -69,8 +69,9 @@ import org.apache.poi.hssf.record.formula.eval.UnaryPlusEval;
  */
 final class OperationEvaluatorFactory {
        private static final Class[] OPERATION_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class };
-       
+       // TODO - use singleton instances directly instead of reflection
        private static final Map _constructorsByPtgClass = initialiseConstructorsMap();
+       private static final Map _instancesByPtgClass = initialiseInstancesMap();
        
        private OperationEvaluatorFactory() {
                // no instances of this class
@@ -78,32 +79,44 @@ final class OperationEvaluatorFactory {
        
        private static Map initialiseConstructorsMap() {
                Map m = new HashMap(32);
-               add(m, AddPtg.class, AddEval.class);
                add(m, ConcatPtg.class, ConcatEval.class);
-               add(m, DividePtg.class, DivideEval.class);
-               add(m, EqualPtg.class, EqualEval.class);
                add(m, FuncPtg.class, FuncVarEval.class);
                add(m, FuncVarPtg.class, FuncVarEval.class);
-               add(m, GreaterEqualPtg.class, GreaterEqualEval.class);
-               add(m, GreaterThanPtg.class, GreaterThanEval.class);
-               add(m, LessEqualPtg.class, LessEqualEval.class);
-               add(m, LessThanPtg.class, LessThanEval.class);
-               add(m, MultiplyPtg.class, MultiplyEval.class);
-               add(m, NotEqualPtg.class, NotEqualEval.class);
-               add(m, PercentPtg.class, PercentEval.class);
-               add(m, PowerPtg.class, PowerEval.class);
-               add(m, SubtractPtg.class, SubtractEval.class);
-               add(m, UnaryMinusPtg.class, UnaryMinusEval.class);
-               add(m, UnaryPlusPtg.class, UnaryPlusEval.class);
                return m;
        }
+       private static Map initialiseInstancesMap() {
+               Map m = new HashMap(32);
+               add(m, EqualPtg.class, EqualEval.instance);
+               add(m, GreaterEqualPtg.class, GreaterEqualEval.instance);
+               add(m, GreaterThanPtg.class, GreaterThanEval.instance);
+               add(m, LessEqualPtg.class, LessEqualEval.instance);
+               add(m, LessThanPtg.class, LessThanEval.instance);
+               add(m, NotEqualPtg.class, NotEqualEval.instance);
+
+               add(m, AddPtg.class, AddEval.instance);
+               add(m, DividePtg.class, DivideEval.instance);
+               add(m, MultiplyPtg.class, MultiplyEval.instance);
+               add(m, PercentPtg.class, PercentEval.instance);
+               add(m, PowerPtg.class, PowerEval.instance);
+               add(m, SubtractPtg.class, SubtractEval.instance);
+               add(m, UnaryMinusPtg.class, UnaryMinusEval.instance);
+               add(m, UnaryPlusPtg.class, UnaryPlusEval.instance);
+               return m;
+       }
+
+       private static void add(Map m, Class ptgClass, OperationEval evalInstance) {
+               if(!Ptg.class.isAssignableFrom(ptgClass)) {
+                       throw new IllegalArgumentException("Expected Ptg subclass");
+               }
+               m.put(ptgClass, evalInstance);
+       }
 
        private static void add(Map m, Class ptgClass, Class evalClass) {
-               
                // perform some validation now, to keep later exception handlers simple
                if(!Ptg.class.isAssignableFrom(ptgClass)) {
                        throw new IllegalArgumentException("Expected Ptg subclass");
                }
+               
                if(!OperationEval.class.isAssignableFrom(evalClass)) {
                        throw new IllegalArgumentException("Expected OperationEval subclass");
                }
@@ -125,7 +138,7 @@ final class OperationEvaluatorFactory {
                }
                m.put(ptgClass, constructor);
        }
-       
+
        /**
         * returns the OperationEval concrete impl instance corresponding
         * to the supplied operationPtg
@@ -134,9 +147,16 @@ final class OperationEvaluatorFactory {
                if(ptg == null) {
                        throw new IllegalArgumentException("ptg must not be null");
                }
+               Object result;
                
                Class ptgClass = ptg.getClass();
                
+               result = _instancesByPtgClass.get(ptgClass);
+               if (result != null) {
+                       return (OperationEval) result;
+               }
+               
+               
                Constructor constructor = (Constructor) _constructorsByPtgClass.get(ptgClass);
                if(constructor == null) {
                        if(ptgClass == ExpPtg.class) {
@@ -147,7 +167,6 @@ final class OperationEvaluatorFactory {
                        throw new RuntimeException("Unexpected operation ptg class (" + ptgClass.getName() + ")");
                }
                
-               Object result;
                Object[] initargs = { ptg };
                try {
                        result = constructor.newInstance(initargs);
index b5e06b9af915ffc8690cffc2b177aec69e3cad3a..ae2c9192f0d619fdd4760081cb5f326e4b25e29f 100644 (file)
@@ -46,29 +46,59 @@ public class AreaReference {
         }
 
         String[] parts = separateAreaRefs(reference);
+        String part0 = parts[0];
+        if (parts.length == 1) {
+            // TODO - probably shouldn't initialize area ref when text is really a cell ref
+            // Need to fix some named range stuff to get rid of this
+            _firstCell = new CellReference(part0);
+            
+            _lastCell = _firstCell;
+            _isSingleCell = true;
+            return;
+        }
+        if (parts.length != 2) {
+            throw new IllegalArgumentException("Bad area ref '" + reference + "'");
+        }
         
-        // Special handling for whole-column references
-        if(parts.length == 2 && parts[0].length() == 1 &&
-                parts[1].length() == 1 && 
-                parts[0].charAt(0) >= 'A' && parts[0].charAt(0) <= 'Z' &&
-                parts[1].charAt(0) >= 'A' && parts[1].charAt(0) <= 'Z') {
+        String part1 = parts[1];
+        if (isPlainColumn(part0)) {
+            if (!isPlainColumn(part1)) {
+                throw new RuntimeException("Bad area ref '" + reference + "'");
+            }
+            // Special handling for whole-column references
             // Represented internally as x$1 to x$65536
             //  which is the maximum range of rows
-            parts[0] = parts[0] + "$1";
-            parts[1] = parts[1] + "$65536";
-        }
-        
-        _firstCell = new CellReference(parts[0]);
-        
-        if(parts.length == 2) {
-            _lastCell = new CellReference(parts[1]);
+
+            boolean firstIsAbs = CellReference.isPartAbsolute(part0);
+            boolean lastIsAbs = CellReference.isPartAbsolute(part1);
+            
+            int col0 = CellReference.convertColStringToIndex(part0);
+            int col1 = CellReference.convertColStringToIndex(part1);
+            
+            _firstCell = new CellReference(0, col0, true, firstIsAbs);
+            _lastCell = new CellReference(0xFFFF, col1, true, lastIsAbs);
             _isSingleCell = false;
+            // TODO - whole row refs
         } else {
-            _lastCell = _firstCell;
-            _isSingleCell = true;
+            _firstCell = new CellReference(part0);
+            _lastCell = new CellReference(part1);
+            _isSingleCell = part0.equals(part1);
+       }
+     }
+    
+    private boolean isPlainColumn(String refPart) {
+        for(int i=refPart.length()-1; i>=0; i--) {
+            int ch = refPart.charAt(i);
+            if (ch == '$' && i==0) {
+               continue;
+            }
+            if (ch < 'A' || ch > 'Z') {
+                return false;
+            }
         }
+        return true;
     }
-    
+
     /**
      * Creates an area ref from a pair of Cell References.
      */
index b46cd88ccba32365d2e94d2a0059e6514f5eaae0..e5005db5d4d9a88cfa78bf553723b0fce0269ce1 100644 (file)
@@ -80,7 +80,7 @@ public class CellReference {
         if (_isColAbs) {
             colRef=colRef.substring(1);
         }
-        _colIndex = convertColStringToNum(colRef);
+        _colIndex = convertColStringToIndex(colRef);
         
         String rowRef=parts[2];
         if (rowRef.length() < 1) {
@@ -94,7 +94,7 @@ public class CellReference {
     }
 
     public CellReference(int pRow, int pCol) {
-       this(pRow, pCol, false, false);
+       this(pRow, pCol & 0xFFFF, false, false);
     }
     public CellReference(int pRow, short pCol) {
        this(pRow, (int)pCol, false, false);
@@ -130,18 +130,31 @@ public class CellReference {
     public String getSheetName(){
         return _sheetName;
     }
-    
+    public static boolean isPartAbsolute(String part) {
+        return part.charAt(0) == ABSOLUTE_REFERENCE_MARKER;
+    }
+
     /**
      * takes in a column reference portion of a CellRef and converts it from
      * ALPHA-26 number format to 0-based base 10.
+     * 'A' -> 0
+     * 'Z' -> 25
+     * 'AA' -> 26
+     * 'IV' -> 255
+     * @return zero based column index
      */
-    private int convertColStringToNum(String ref) {
-               int lastIx = ref.length()-1;
-               int retval=0;
-               int pos = 0;
-               
-               for (int k = lastIx; k > -1; k--) {
+    protected static int convertColStringToIndex(String ref) {
+        int pos = 0;
+        int retval=0;
+               for (int k = ref.length()-1; k >= 0; k--) {
                        char thechar = ref.charAt(k);
+            if (thechar == ABSOLUTE_REFERENCE_MARKER) {
+               if (k != 0) {
+                  throw new IllegalArgumentException("Bad col ref format '" + ref + "'");
+               }
+               break;
+            }
                        // Character.getNumericValue() returns the values
                        //  10-35 for the letter A-Z
                        int shift = (int)Math.pow(26, pos);
index f6252840ccd40040f7e74f13baa3069e97b12d55..3284153cb132f77b71ab4efc69f7743cc56384e4 100644 (file)
@@ -71,27 +71,44 @@ public class Document extends PositionDependentRecordContainer
         * This will normally return an array of size 2 or 3
         */
        public SlideListWithText[] getSlideListWithTexts() { return slwts; }
-       /**
+
+    /**
         * Returns the SlideListWithText that deals with the
         *  Master Slides 
         */
        public SlideListWithText getMasterSlideListWithText() { 
-               if(slwts.length > 0) { return slwts[0]; }
-               return null; }
+        for (int i = 0; i < slwts.length; i++) {
+            if(slwts[i].getInstance() == SlideListWithText.MASTER) {
+                return slwts[i];
+            }
+        }
+        return null;
+    }
+
        /**
         * Returns the SlideListWithText that deals with the
         *  Slides, or null if there isn't one
         */
-       public SlideListWithText getSlideSlideListWithText() { 
-               if(slwts.length > 1) { return slwts[1]; }
-               return null; }
+       public SlideListWithText getSlideSlideListWithText() {
+        for (int i = 0; i < slwts.length; i++) {
+            if(slwts[i].getInstance() == SlideListWithText.SLIDES) {
+                return slwts[i];
+            }
+        }
+               return null;
+    }
        /**
         * Returns the SlideListWithText that deals with the
         *  notes, or null if there isn't one
         */
        public SlideListWithText getNotesSlideListWithText() {
-               if(slwts.length > 2) { return slwts[2]; }
-               return null; }
+        for (int i = 0; i < slwts.length; i++) {
+            if(slwts[i].getInstance() == SlideListWithText.NOTES) {
+                return slwts[i];
+            }
+        }
+               return null;
+    }
 
 
        /** 
index f5efedc1d315798a07ed6129cbfbf957e3c72738..0772e0312113fd4b178533296e4dac86d3557e3c 100644 (file)
@@ -243,8 +243,16 @@ public abstract class RecordContainer extends Record
                        moveChildRecords(oldLoc, newLoc, number);
                }
        }
-       
-       
+
+    /**
+     * Set child records. 
+     *
+     * @param records   the new child records
+     */
+    public void setChildRecord(Record[] records) {
+        this._children = records;
+    }
+
        /* ===============================================================
         *                 External Serialisation Methods
         * ===============================================================
index 1573c82a166ff1812b2be145a0439eb4857f8161..13ffa707527b4070488e643890603c72aca872c7 100644 (file)
@@ -50,7 +50,24 @@ import java.util.Vector;
 // For now, pretend to be an atom
 public class SlideListWithText extends RecordContainer
 {
-       private byte[] _header;
+
+    /**
+     * Instance filed of the record header indicates that this SlideListWithText stores
+     * references to slides
+     */
+    public static final int SLIDES = 0;
+    /**
+     * Instance filed of the record header indicates that this SlideListWithText stores
+     * references to master slides
+     */
+    public static final int MASTER = 1;
+    /**
+     * Instance filed of the record header indicates that this SlideListWithText stores
+     * references to notes
+     */
+    public static final int NOTES = 2;
+
+    private byte[] _header;
        private static long _type = 4080;
 
        private SlideAtomsSet[] slideAtomsSets;
@@ -123,9 +140,9 @@ public class SlideListWithText extends RecordContainer
        public void addSlidePersistAtom(SlidePersistAtom spa) {
                // Add the new SlidePersistAtom at the end
                appendChildRecord(spa);
-               
+
                SlideAtomsSet newSAS = new SlideAtomsSet(spa, new Record[0]);
-               
+
                // Update our SlideAtomsSets with this
                SlideAtomsSet[] sas = new SlideAtomsSet[slideAtomsSets.length+1];
                System.arraycopy(slideAtomsSets, 0, sas, 0, slideAtomsSets.length);
@@ -133,7 +150,15 @@ public class SlideListWithText extends RecordContainer
                slideAtomsSets = sas;
        }
 
-       /**
+    public int getInstance(){
+        return LittleEndian.getShort(_header, 0) >> 4;
+    }
+
+    public void setInstance(int inst){
+        LittleEndian.putShort(_header, (short)((inst << 4) | 0xF));
+    }
+
+    /**
         * Get access to the SlideAtomsSets of the children of this record
         */
        public SlideAtomsSet[] getSlideAtomsSets() { return slideAtomsSets; }
@@ -152,35 +177,6 @@ public class SlideListWithText extends RecordContainer
        }
 
        /**
-        * Shifts a SlideAtomsSet to a new position.
-        * Works by shifting the child records about, then updating
-        *  the SlideAtomSets array
-        * @param toMove The SlideAtomsSet to move
-        * @param newPosition The new (0 based) position for the SlideAtomsSet
-        */
-       public void repositionSlideAtomsSet(SlideAtomsSet toMove, int newPosition) {
-               // Ensure it's one of ours
-               int curPos = -1;
-               for(int i=0; i<slideAtomsSets.length; i++) {
-                       if(slideAtomsSets[i] == toMove) { curPos = i; }
-               }
-               if(curPos == -1) {
-                       throw new IllegalArgumentException("The supplied SlideAtomsSet didn't belong to this SlideListWithText");
-               }
-               
-               // Ensure the newPosision is valid
-               if(newPosition < 0 || newPosition >= slideAtomsSets.length) {
-                       throw new IllegalArgumentException("The new position must be between 0, and the number of SlideAtomsSets");
-               }
-               
-               // Build the new records list
-               moveChildrenBefore(toMove.getSlidePersistAtom(), toMove.slideRecords.length, slideAtomsSets[newPosition].getSlidePersistAtom());
-               
-               // Build the new SlideAtomsSets list
-               ArrayUtil.arrayMoveWithin(slideAtomsSets, curPos, newPosition, 1);
-       }
-
-       /** 
         * Inner class to wrap up a matching set of records that hold the
         *  text for a given sheet. Contains the leading SlidePersistAtom,
         *  and all of the records until the next SlidePersistAtom. This 
index 80bc2e855aeaa4add319d34c2567c2ef8a356425..7672923ad565222b8ee6f314492aa4433cfd3a25 100644 (file)
@@ -536,32 +536,37 @@ public final class SlideShow {
        
        /**
         * Re-orders a slide, to a new position.
-        * @param oldSlideNumer The old slide number (1 based)
+        * @param oldSlideNumber The old slide number (1 based)
         * @param newSlideNumber The new slide number (1 based)
         */
-       public void reorderSlide(int oldSlideNumer, int newSlideNumber) {
+       public void reorderSlide(int oldSlideNumber, int newSlideNumber) {
                // Ensure these numbers are valid
-               if(oldSlideNumer < 1 || newSlideNumber < 1) {
+               if(oldSlideNumber < 1 || newSlideNumber < 1) {
                        throw new IllegalArgumentException("Old and new slide numbers must be greater than 0");
                }
-               if(oldSlideNumer > _slides.length || newSlideNumber > _slides.length) {
+               if(oldSlideNumber > _slides.length || newSlideNumber > _slides.length) {
                        throw new IllegalArgumentException("Old and new slide numbers must not exceed the number of slides (" + _slides.length + ")");
                }
-               
-               // Shift the SlideAtomsSet
-               SlideListWithText slwt = _documentRecord.getSlideSlideListWithText(); 
-               slwt.repositionSlideAtomsSet( 
-                               slwt.getSlideAtomsSets()[(oldSlideNumer-1)],
-                               (newSlideNumber-1)
-               );
-               
-               // Re-order the slides
-               ArrayUtil.arrayMoveWithin(_slides, (oldSlideNumer-1), (newSlideNumber-1), 1);
-               
-               // Tell the appropriate slides their new numbers
-               for(int i=0; i<_slides.length; i++) {
-                       _slides[i].setSlideNumber( (i+1) );
-               }
+
+        //  The order of slides is defined by the order of slide atom sets in the SlideListWithText container.
+        SlideListWithText slwt = _documentRecord.getSlideSlideListWithText();
+        SlideAtomsSet[] sas = slwt.getSlideAtomsSets();
+
+        SlideAtomsSet tmp = sas[oldSlideNumber-1];
+        sas[oldSlideNumber-1] = sas[newSlideNumber-1];
+        sas[newSlideNumber-1] = tmp;
+
+        ArrayList lst = new ArrayList();
+        for (int i = 0; i < sas.length; i++) {
+            lst.add(sas[i].getSlidePersistAtom());
+            Record[] r = sas[i].getSlideRecords();
+            for (int j = 0; j < r.length; j++) {
+                lst.add(r[j]);
+            }
+            _slides[i].setSlideNumber(i+1);
+        }
+        Record[] r = (Record[])lst.toArray(new Record[lst.size()]);
+        slwt.setChildRecord(r);
        }
 
        /* ===============================================================
@@ -585,7 +590,8 @@ public final class SlideShow {
                if(slist == null) {
                        // Need to add a new one
                        slist = new SlideListWithText();
-                       _documentRecord.addSlideListWithText(slist);
+                       slist.setInstance(SlideListWithText.SLIDES);
+            _documentRecord.addSlideListWithText(slist);
                }
 
                // Grab the SlidePersistAtom with the highest Slide Number.
@@ -678,11 +684,11 @@ public final class SlideShow {
                ptr.addSlideLookup(sp.getRefID(), slideOffset);
                logger.log(POILogger.INFO, "New slide ended up at " + slideOffset);
 
-               // All done and added
+        slide.setMasterSheet(_masters[0]);
+          // All done and added
                return slide;
        }
 
-
     /**
      * Adds a picture to this presentation and returns the associated index.
      *
index 590212fb2259dfb6d50f4918a62c0cedff622ae3..e039781cc8968328cc2e4aebc5035e94b935f728 100644 (file)
@@ -279,18 +279,23 @@ public class TestReOrderingSlides extends TestCase {
                assertEquals(3, ss_read.getSlides().length);
                
                // And check it's as expected
-               s1 = ss_read.getSlides()[0];
-               s2 = ss_read.getSlides()[1];
-               s3 = ss_read.getSlides()[2];
-               
-               assertEquals(257, s1._getSheetNumber());
-               assertEquals(4, s1._getSheetRefId());
+               Slide _s1 = ss_read.getSlides()[0];
+               Slide _s2 = ss_read.getSlides()[1];
+               Slide _s3 = ss_read.getSlides()[2];
+
+        // 1 --> 3
+        assertEquals(s1._getSheetNumber(), _s3._getSheetNumber());
+               assertEquals(s1._getSheetRefId(), _s3._getSheetRefId());
                assertEquals(1, s1.getSlideNumber());
-               assertEquals(256, s2._getSheetNumber());
-               assertEquals(3, s2._getSheetRefId());
+
+        // 2nd slide is not updated
+        assertEquals(s2._getSheetNumber(), _s2._getSheetNumber());
+        assertEquals(s2._getSheetRefId(), _s2._getSheetRefId());
                assertEquals(2, s2.getSlideNumber());
-               assertEquals(258, s3._getSheetNumber());
-               assertEquals(5, s3._getSheetRefId());
+
+        // 3 --> 1
+        assertEquals(s3._getSheetNumber(), _s1._getSheetNumber());
+        assertEquals(s3._getSheetRefId(), _s1._getSheetRefId());
                assertEquals(3, s3.getSlideNumber());
        }
 }
diff --git a/src/testcases/org/apache/poi/hssf/data/45376.xls b/src/testcases/org/apache/poi/hssf/data/45376.xls
deleted file mode 100644 (file)
index 74602fd..0000000
Binary files a/src/testcases/org/apache/poi/hssf/data/45376.xls and /dev/null differ
diff --git a/src/testcases/org/apache/poi/hssf/data/45720.xls b/src/testcases/org/apache/poi/hssf/data/45720.xls
new file mode 100755 (executable)
index 0000000..9c6c484
Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/45720.xls differ
index 51e370bfb96d6ba0c5c2429a0b8cdcebadad0b81..99cb61a58811b2246af6cc66651997d897dffe0c 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
index f4b35fb93504540b85455ff37341318cbcd74e1e..94f16e9840536697981a8c31ce59900625c6236f 100755 (executable)
Binary files a/src/testcases/org/apache/poi/hssf/data/LookupFunctionsTestCaseData.xls and b/src/testcases/org/apache/poi/hssf/data/LookupFunctionsTestCaseData.xls differ
index 07eafb414416e98875b4c0a9cd2a78b453a5413a..fc81e60a18397f0a47b49bb77c3e32309d15662d 100755 (executable)
Binary files a/src/testcases/org/apache/poi/hssf/data/externalFunctionExample.xls and b/src/testcases/org/apache/poi/hssf/data/externalFunctionExample.xls differ
index 7ebbb633af8bd6a0a951f1d4335c9242fe2c3f20..c5d43cc038a5d0ee463a66ddb0d1c125c914a750 100644 (file)
Binary files a/src/testcases/org/apache/poi/hssf/data/testNames.xls and b/src/testcases/org/apache/poi/hssf/data/testNames.xls differ
index cc5198acfac3c64122796de45b9a16e719315c49..1f6d0d3c282867f44c8abb9b96d343ba884ce90f 100755 (executable)
@@ -74,9 +74,18 @@ public final class TestExternalFunctionFormulas extends TestCase {
        public void testEvaluate() {
                HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("externalFunctionExample.xls");
                HSSFSheet sheet = wb.getSheetAt(0);
-               HSSFCell cell = sheet.getRow(0).getCell(0);
                HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb);
-               CellValue evalResult = fe.evaluate(cell);
-               evalResult.toString();
+               confirmCellEval(sheet, 0, 0, fe, "YEARFRAC(B1,C1)", 29.0/90.0);
+               confirmCellEval(sheet, 1, 0, fe, "YEARFRAC(B2,C2)", 0.0);
+               confirmCellEval(sheet, 2, 0, fe, "IF(ISEVEN(3),1.2,1.6)", 1.6);
+               confirmCellEval(sheet, 3, 0, fe, "IF(ISODD(3),1.2,1.6)", 1.2);
+       }
+
+       private static void confirmCellEval(HSSFSheet sheet, int rowIx, int colIx, 
+                       HSSFFormulaEvaluator fe, String expectedFormula, double expectedResult) {
+               HSSFCell cell = sheet.getRow(rowIx).getCell(colIx);
+               assertEquals(expectedFormula, cell.getCellFormula());
+               CellValue cv = fe.evaluate(cell);
+               assertEquals(expectedResult, cv.getNumberValue(), 0.0);
        }
 }
index aaf03a238ee6f413b936395ddfd702a31f921016..890a5c2cbd55d8cfdf81550761fc30de86f83cc9 100644 (file)
@@ -38,6 +38,7 @@ public final class TestYearFracCalculator extends TestCase {
                confirm(md(1999, 3, 31), md(1999, 4, 3), 1, 0.008219178);
                confirm(md(1999, 4, 5), md(1999, 4, 8), 1, 0.008219178);
                confirm(md(1999, 4, 4), md(1999, 4, 7), 1, 0.008219178);
+               confirm(md(2000, 2, 5), md(2000, 6, 1), 0, 0.322222222);
        }
 
        private void confirm(double startDate, double endDate, int basis, double expectedValue) {
index 8887445ad3826928e432802eca1639ac32a2e6fb..75510619420583e3cc3b749dc2e47fbd7ad53975 100755 (executable)
@@ -31,6 +31,8 @@ public class AllFormulaEvalTests {
                TestSuite result = new TestSuite(AllFormulaEvalTests.class.getName());
                result.addTestSuite(TestAreaEval.class);
                result.addTestSuite(TestCircularReferences.class);
+               result.addTestSuite(TestDivideEval.class);
+               result.addTestSuite(TestEqualEval.class);
                result.addTestSuite(TestExternalFunction.class);
                result.addTestSuite(TestFormulaBugs.class);
                result.addTestSuite(TestFormulasFromSpreadsheet.class);
index ee204798726e0d74544ee7381e2bc74a492fcdce..07f13a0445bf2601580e7daae58c3ae817d2bf0e 100755 (executable)
@@ -93,7 +93,6 @@ public final class TestCircularReferences extends TestCase {
                HSSFCell testCell = row.createCell(0);
                testCell.setCellFormula("A1");
 
-               HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb);
                CellValue cellValue = evaluateWithCycles(wb, sheet, testCell);
                
                confirmCycleErrorCode(cellValue);
@@ -114,7 +113,6 @@ public final class TestCircularReferences extends TestCase {
                HSSFCell testCell = row.createCell(3);
                testCell.setCellFormula("A1");
 
-               HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb);
                CellValue cellValue = evaluateWithCycles(wb, sheet, testCell);
                
                confirmCycleErrorCode(cellValue);
diff --git a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestDivideEval.java b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestDivideEval.java
new file mode 100644 (file)
index 0000000..71bf03e
--- /dev/null
@@ -0,0 +1,62 @@
+/* ====================================================================
+   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.eval;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.hssf.record.formula.functions.EvalFactory;
+import org.apache.poi.hssf.record.formula.functions.NumericFunctionInvoker;
+
+/**
+ * Test for divide operator evaluator.
+ * 
+ * @author Josh Micich
+ */
+public final class TestDivideEval extends TestCase {
+       
+       private static void confirm(ValueEval arg0, ValueEval arg1, double expectedResult) {
+               Eval[] args = { 
+                       arg0, arg1,      
+               };
+               
+               double result = NumericFunctionInvoker.invoke(DivideEval.instance, args, 0, 0);
+               
+               assertEquals(expectedResult, result, 0);
+       }
+
+       public void testBasic() {
+               confirm(new NumberEval(5), new NumberEval(2), 2.5);
+               confirm(new NumberEval(3), new NumberEval(16), 0.1875);
+               confirm(new NumberEval(-150), new NumberEval(-15), 10.0);
+               confirm(new StringEval("0.2"), new NumberEval(0.05), 4.0);
+               confirm(BoolEval.TRUE, new StringEval("-0.2"), -5.0);
+       }
+
+       public void test1x1Area() {
+               AreaEval ae0 = EvalFactory.createAreaEval("B2:B2", new ValueEval[] { new NumberEval(50), });
+               AreaEval ae1 = EvalFactory.createAreaEval("C2:C2", new ValueEval[] { new NumberEval(10), });
+               confirm(ae0, ae1, 5);
+       }
+       public void testDivZero() {
+               Eval[] args = { 
+                       new NumberEval(5), NumberEval.ZERO,      
+               };
+               Eval result = DivideEval.instance.evaluate(args, 0, (short) 0);
+               assertEquals(ErrorEval.DIV_ZERO, result);
+       }
+}
diff --git a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestEqualEval.java b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestEqualEval.java
new file mode 100644 (file)
index 0000000..d1b1db0
--- /dev/null
@@ -0,0 +1,69 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.hssf.record.formula.eval;\r
+\r
+import junit.framework.AssertionFailedError;\r
+import junit.framework.TestCase;\r
+\r
+import org.apache.poi.hssf.record.formula.functions.EvalFactory;\r
+\r
+/**\r
+ * Test for unary plus operator evaluator.\r
+ *\r
+ * @author Josh Micich\r
+ */\r
+public final class TestEqualEval extends TestCase {\r
+\r
+       /**\r
+        * Test for bug observable at svn revision 692218 (Sep 2008)<br/>\r
+        * The value from a 1x1 area should be taken immediately, regardless of srcRow and srcCol\r
+        */\r
+       public void test1x1AreaOperand() {\r
\r
+               ValueEval[] values = { BoolEval.FALSE, };\r
+               Eval[] args = {\r
+                       EvalFactory.createAreaEval("B1:B1", values),\r
+                       BoolEval.FALSE,\r
+               };\r
+               Eval result = EqualEval.instance.evaluate(args, 10, (short)20);\r
+               if (result instanceof ErrorEval) {\r
+                       if (result == ErrorEval.VALUE_INVALID) {\r
+                               throw new AssertionFailedError("Identified bug in evaluation of 1x1 area");\r
+                       }\r
+               }\r
+               assertEquals(BoolEval.class, result.getClass());\r
+               assertTrue(((BoolEval)result).getBooleanValue());\r
+       }\r
+       /**\r
+        * Empty string is equal to blank\r
+        */\r
+       public void testBlankEqualToEmptyString() {\r
+                \r
+               Eval[] args = {\r
+                       new StringEval(""),\r
+                       BlankEval.INSTANCE,\r
+               };\r
+               Eval result = EqualEval.instance.evaluate(args, 10, (short)20);\r
+               assertEquals(BoolEval.class, result.getClass());\r
+               BoolEval be = (BoolEval) result;\r
+               if (!be.getBooleanValue()) {\r
+                       throw new AssertionFailedError("Identified bug blank/empty string equality");\r
+               }\r
+               assertTrue(be.getBooleanValue());\r
+       }\r
+}\r
index e027605dfa63aa4b499ab6f891f2bc7c706a23fc..ef2340d4a4fe56b153400ac3eac3fe6239a53259 100755 (executable)
@@ -20,7 +20,7 @@ package org.apache.poi.hssf.record.formula.eval;
 import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;
 
-import org.apache.poi.hssf.record.formula.PercentPtg;
+import org.apache.poi.hssf.record.formula.functions.EvalFactory;
 import org.apache.poi.hssf.record.formula.functions.NumericFunctionInvoker;
 import org.apache.poi.hssf.usermodel.HSSFCell;
 import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
@@ -41,8 +41,8 @@ public final class TestPercentEval extends TestCase {
                        arg,    
                };
                
-               PercentEval opEval = new PercentEval(PercentPtg.instance);
-               double result = NumericFunctionInvoker.invoke(opEval, args, -1, (short)-1);
+               OperationEval opEval = PercentEval.instance;
+               double result = NumericFunctionInvoker.invoke(opEval, args, 0, 0);
                
                assertEquals(expectedResult, result, 0);
        }
@@ -55,6 +55,10 @@ public final class TestPercentEval extends TestCase {
                confirm(BoolEval.TRUE, 0.01);
        }
 
+       public void test1x1Area() {
+               AreaEval ae = EvalFactory.createAreaEval("B2:B2", new ValueEval[] { new NumberEval(50), });
+               confirm(ae, 0.5);
+       }
        public void testInSpreadSheet() {
                HSSFWorkbook wb = new HSSFWorkbook();
                HSSFSheet sheet = wb.createSheet("Sheet1");
index 33c38a6c127a8dfa447440e0c6c900584774b60d..726633c40604f0235398c502cb3ea80591766ab5 100755 (executable)
@@ -1,27 +1,25 @@
-/*
-* 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.
-*/
-
+/* ====================================================================
+   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.eval;
 
 import junit.framework.TestCase;
 
 import org.apache.poi.hssf.record.formula.AreaPtg;
-import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
 import org.apache.poi.hssf.record.formula.functions.EvalFactory;
 import org.apache.poi.hssf.record.formula.functions.NumericFunctionInvoker;
 
@@ -53,7 +51,7 @@ public final class TestUnaryPlusEval extends TestCase {
                        EvalFactory.createAreaEval(areaPtg, values),
                };
 
-               double result = NumericFunctionInvoker.invoke(new UnaryPlusEval(UnaryPlusPtg.instance), args, 10, (short)20);
+               double result = NumericFunctionInvoker.invoke(UnaryPlusEval.instance, args, 10, (short)20);
 
                assertEquals(35, result, 0);
        }
index 5973d7cb2d25bc2933d170f1a982a18902aad835..2ec7ad005a2e1df3f43ffc3303de243c7735140e 100755 (executable)
@@ -36,6 +36,7 @@ public final class AllIndividualFunctionEvaluationTests {
                result.addTestSuite(TestIndex.class);
                result.addTestSuite(TestIsBlank.class);
                result.addTestSuite(TestLen.class);
+               result.addTestSuite(TestLookupFunctionsFromSpreadsheet.class);
                result.addTestSuite(TestMid.class);
                result.addTestSuite(TestMathX.class);
                result.addTestSuite(TestMatch.class);
index 83c9fcd34c17cba5ac0968084f2359d875860fd1..68bc43154ac17a1c1266b8413ab1cf212e0fadf6 100644 (file)
@@ -28,29 +28,29 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook;
  * @author Pavel Krupets (pkrupets at palmtreebusiness dot com)
  */
 public final class TestDate extends TestCase {
-    
+
     private HSSFCell cell11;
     private HSSFFormulaEvaluator evaluator;
 
     public void setUp() {
         HSSFWorkbook wb = new HSSFWorkbook();
         HSSFSheet sheet = wb.createSheet("new sheet");
-               cell11 = sheet.createRow(0).createCell(0);
+        cell11 = sheet.createRow(0).createCell(0);
         cell11.setCellType(HSSFCell.CELL_TYPE_FORMULA);
         evaluator = new HSSFFormulaEvaluator(sheet, wb);
     }
-    
-       /**
-        * Test disabled pending a fix in the formula evaluator
-        * TODO - create MissingArgEval and modify the formula evaluator to handle this
-        */
+
+    /**
+     * Test disabled pending a fix in the formula evaluator
+     * TODO - create MissingArgEval and modify the formula evaluator to handle this
+     */
     public void DISABLEDtestSomeArgumentsMissing() {
         confirm("DATE(, 1, 0)", 0.0);
         confirm("DATE(, 1, 1)", 1.0);
     }
-    
+
     public void testValid() {
-        
+
         confirm("DATE(1900, 1, 1)", 1);
         confirm("DATE(1900, 1, 32)", 32);
         confirm("DATE(1900, 222, 1)", 6727);
@@ -58,7 +58,7 @@ public final class TestDate extends TestCase {
         confirm("DATE(2000, 1, 222)", 36747.00);
         confirm("DATE(2007, 1, 1)", 39083);
     }
-    
+
     public void testBugDate() {
         confirm("DATE(1900, 2, 29)", 60);
         confirm("DATE(1900, 2, 30)", 61);
@@ -66,7 +66,7 @@ public final class TestDate extends TestCase {
         confirm("DATE(1900, 1, 2222)", 2222);
         confirm("DATE(1900, 1, 22222)", 22222);
     }
-    
+
     public void testPartYears() {
         confirm("DATE(4, 1, 1)", 1462.00);
         confirm("DATE(14, 1, 1)", 5115.00);
@@ -74,10 +74,11 @@ public final class TestDate extends TestCase {
         confirm("DATE(1004, 1, 1)", 366705.00);
     }
 
-       private void confirm(String formulaText, double expectedResult) {
+    private void confirm(String formulaText, double expectedResult) {
         cell11.setCellFormula(formulaText);
+        evaluator.clearCache();
         double actualValue = evaluator.evaluate(cell11).getNumberValue();
-               assertEquals(expectedResult, actualValue, 0);
-       }
+        assertEquals(expectedResult, actualValue, 0);
+    }
 }
 
index 772a4fdfc781cfc9c88fa71c5d34686928651de3..72d420cb4f8bf60e5b4234540e438ad90dc1bb7e 100755 (executable)
@@ -46,6 +46,7 @@ public class AllUserModelTests {
                result.addTestSuite(TestHSSFConditionalFormatting.class);
                result.addTestSuite(TestHSSFDataFormatter.class);
                result.addTestSuite(TestHSSFDateUtil.class);
+               result.addTestSuite(TestHSSFFormulaEvaluator.class);
                result.addTestSuite(TestHSSFHeaderFooter.class);
                result.addTestSuite(TestHSSFHyperlink.class);
                result.addTestSuite(TestHSSFOptimiser.class);
index e7c79317662733bfe6f9a514947dfe4556d409cb..1b5e4cd5dce710d7d70b3fb0a5c7171fe7cbe49e 100644 (file)
@@ -37,6 +37,7 @@ import org.apache.poi.hssf.record.EmbeddedObjectRefSubRecord;
 import org.apache.poi.hssf.record.NameRecord;
 import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
 import org.apache.poi.hssf.record.formula.DeletedArea3DPtg;
+import org.apache.poi.hssf.record.formula.Ptg;
 import org.apache.poi.util.TempFile;
 
 /**
@@ -1020,9 +1021,9 @@ public final class TestBugs extends TestCase {
             NameRecord r = w.getNameRecord(i);
             assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets());
             
-            List nd = r.getNameDefinition();
-            assertEquals(1, nd.size());
-            assertTrue(nd.get(0) instanceof DeletedArea3DPtg);
+            Ptg[] nd = r.getNameDefinition();
+            assertEquals(1, nd.length);
+            assertTrue(nd[0] instanceof DeletedArea3DPtg);
         }
         
         
@@ -1038,9 +1039,9 @@ public final class TestBugs extends TestCase {
             NameRecord r = w.getNameRecord(i);
             assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets());
             
-            List nd = r.getNameDefinition();
-            assertEquals(1, nd.size());
-            assertTrue(nd.get(0) instanceof DeletedArea3DPtg);
+            Ptg[] nd = r.getNameDefinition();
+            assertEquals(1, nd.length);
+            assertTrue(nd[0] instanceof DeletedArea3DPtg);
         }
         
         
@@ -1055,9 +1056,9 @@ public final class TestBugs extends TestCase {
             NameRecord r = w.getNameRecord(i);
             assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets());
             
-            List nd = r.getNameDefinition();
-            assertEquals(1, nd.size());
-            assertTrue(nd.get(0) instanceof DeletedArea3DPtg);
+            Ptg[] nd = r.getNameDefinition();
+            assertEquals(1, nd.length);
+            assertTrue(nd[0] instanceof DeletedArea3DPtg);
         }
     }
     
index 6ebcf96bb66b194fcc5bab96b16a0f8cd13921bf..f1d838efa398c6ac664c5c37e4716792271694d5 100644 (file)
@@ -19,6 +19,8 @@ package org.apache.poi.hssf.usermodel;
 
 import java.io.File;
 import java.io.FileOutputStream;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
 import java.util.Iterator;
 
 import junit.framework.AssertionFailedError;
@@ -35,6 +37,7 @@ import org.apache.poi.hssf.record.formula.Ptg;
  */
 public final class TestFormulaEvaluatorBugs extends TestCase {
 
+       private static final boolean OUTPUT_TEST_FILES = false;
        private String tmpDirName;
 
        protected void setUp() {
@@ -65,13 +68,15 @@ public final class TestFormulaEvaluatorBugs extends TestCase {
                HSSFFormulaEvaluator.evaluateAllFormulaCells(wb);
                assertEquals(4.2 * 25, row.getCell(3).getNumericCellValue(), 0.0001);
 
-               // Save
-               File existing = new File(tmpDirName, "44636-existing.xls");
-               FileOutputStream out = new FileOutputStream(existing);
-               wb.write(out);
-               out.close();
-               System.err.println("Existing file for bug #44636 written to " + existing.toString());
-
+               FileOutputStream out;
+               if (OUTPUT_TEST_FILES) {
+                       // Save
+                       File existing = new File(tmpDirName, "44636-existing.xls");
+                       out = new FileOutputStream(existing);
+                       wb.write(out);
+                       out.close();
+                       System.err.println("Existing file for bug #44636 written to " + existing.toString());
+               }
                // Now, do a new file from scratch
                wb = new HSSFWorkbook();
                sheet = wb.createSheet();
@@ -86,12 +91,14 @@ public final class TestFormulaEvaluatorBugs extends TestCase {
                HSSFFormulaEvaluator.evaluateAllFormulaCells(wb);
                assertEquals(5.4, row.getCell(0).getNumericCellValue(), 0.0001);
 
-               // Save
-               File scratch = new File(tmpDirName, "44636-scratch.xls");
-               out = new FileOutputStream(scratch);
-               wb.write(out);
-               out.close();
-               System.err.println("New file for bug #44636 written to " + scratch.toString());
+               if (OUTPUT_TEST_FILES) {
+                       // Save
+                       File scratch = new File(tmpDirName, "44636-scratch.xls");
+                       out = new FileOutputStream(scratch);
+                       wb.write(out);
+                       out.close();
+                       System.err.println("New file for bug #44636 written to " + scratch.toString());
+               }
        }
 
        /**
@@ -281,64 +288,39 @@ public final class TestFormulaEvaluatorBugs extends TestCase {
        }
        
        /**
-        * Apparently, each subsequent call takes longer, which is very
-        *  odd.
-        * We think it's because the formulas are recursive and crazy
+        * The HSSFFormula evaluator performance benefits greatly from caching of intermediate cell values
         */
-       public void DISABLEDtestSlowEvaluate45376() throws Exception {
-               HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("45376.xls");
+       public void testSlowEvaluate45376() {
                
-               final String SHEET_NAME = "Eingabe";
-        final int row = 6;
-        final HSSFSheet sheet = wb.getSheet(SHEET_NAME);
-        
-        int firstCol = 4;
-        int lastCol = 14;
-        long[] timings = new long[lastCol-firstCol+1];
-        long[] rtimings = new long[lastCol-firstCol+1];
-        
-        long then, now;
-
-        final HSSFRow excelRow = sheet.getRow(row);
-        for(int i = firstCol; i <= lastCol; i++) {
-             final HSSFCell excelCell = excelRow.getCell(i);
-             final HSSFFormulaEvaluator evaluator = new
-                 HSSFFormulaEvaluator(sheet, wb);
-
-             now = System.currentTimeMillis();
-             evaluator.evaluate(excelCell);
-             then = System.currentTimeMillis();
-             timings[i-firstCol] = (then-now);
-             System.err.println("Col " + i + " took " + (then-now) + "ms");
-        }
-        for(int i = lastCol; i >= firstCol; i--) {
-            final HSSFCell excelCell = excelRow.getCell(i);
-            final HSSFFormulaEvaluator evaluator = new
-                HSSFFormulaEvaluator(sheet, wb);
-
-            now = System.currentTimeMillis();
-            evaluator.evaluate(excelCell);
-            then = System.currentTimeMillis();
-            rtimings[i-firstCol] = (then-now);
-            System.err.println("Col " + i + " took " + (then-now) + "ms");
-       }
-        
-        // The timings for each should be about the same
-        long avg = 0;
-        for(int i=0; i<timings.length; i++) {
-               avg += timings[i];
-        }
-        avg = (long)( ((double)avg) / timings.length );
-        
-        // Warn if any took more then 1.5 the average
-        // TODO - replace with assert or similar
-        for(int i=0; i<timings.length; i++) {
-               if(timings[i] > 1.5*avg) {
-                       System.err.println("Doing col " + (i+firstCol) + 
-                                       " took " + timings[i] + "ms, vs avg " + 
-                                       avg + "ms"
-                       );
-               }
-        }
+               // Firstly set up a sequence of formula cells where each depends on the  previous multiple
+               // times.  Without caching, each subsequent cell take about 4 times longer to evaluate.
+               HSSFWorkbook wb = new HSSFWorkbook();
+               HSSFSheet sheet = wb.createSheet("Sheet1");
+               HSSFRow row = sheet.createRow(0);
+               for(int i=1; i<10; i++) {
+                       HSSFCell cell = row.createCell(i);
+                       char prevCol = (char) ('A' + i-1);
+                       String prevCell = prevCol + "1";
+                       // this formula is inspired by the offending formula of the attachment for bug 45376
+                       String formula = "IF(DATE(YEAR(" + prevCell + "),MONTH(" + prevCell + ")+1,1)<=$D$3," +
+                                       "DATE(YEAR(" + prevCell + "),MONTH(" + prevCell + ")+1,1),NA())";
+                       cell.setCellFormula(formula);
+                       
+               }
+               Calendar cal = new GregorianCalendar(2000, 0, 1, 0, 0, 0);
+               row.createCell(0).setCellValue(cal);
+               
+               // Choose cell A9, so that the failing test case doesn't take too long to execute.
+               HSSFCell cell = row.getCell(8);
+               HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb);
+               evaluator.evaluate(cell);
+               int evalCount = evaluator.getEvaluationCount();
+               // With caching, the evaluationCount is 8 which is a big improvement
+               if (evalCount > 10) {
+                       // Without caching, evaluating cell 'A9' takes 21845 evaluations which consumes
+                       // much time (~3 sec on Core 2 Duo 2.2GHz)
+                       System.err.println("Cell A9 took " + evalCount + " intermediate evaluations");
+                       throw new AssertionFailedError("Identifed bug 45376 - Formula evaluator should cache values");
+               }
        }
 }
\ No newline at end of file
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFFormulaEvaluator.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFFormulaEvaluator.java
new file mode 100644 (file)
index 0000000..970b166
--- /dev/null
@@ -0,0 +1,82 @@
+/* ====================================================================
+   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.usermodel;
+
+import org.apache.poi.hssf.HSSFTestDataSamples;
+import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
+
+import junit.framework.TestCase;
+/**
+ * 
+ * @author Josh Micich
+ */
+public final class TestHSSFFormulaEvaluator extends TestCase {
+
+       /**
+        * Test that the HSSFFormulaEvaluator can evaluate simple named ranges
+        *  (single cells and rectangular areas)
+        */
+       public void testEvaluateSimple() {
+               HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("testNames.xls");
+               HSSFSheet sheet = wb.getSheetAt(0);
+               HSSFCell cell = sheet.getRow(8).getCell(0);
+               HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb);
+               CellValue cv = fe.evaluate(cell);
+               assertEquals(HSSFCell.CELL_TYPE_NUMERIC, cv.getCellType());
+               assertEquals(3.72, cv.getNumberValue(), 0.0);
+       }
+       
+       public void testFullColumnRefs() {
+               HSSFWorkbook wb = new HSSFWorkbook();
+               HSSFSheet sheet = wb.createSheet("Sheet1");
+               HSSFRow row = sheet.createRow(0);
+               HSSFCell cell0 = row.createCell(0);
+               cell0.setCellFormula("sum(D:D)");
+               HSSFCell cell1 = row.createCell(1);
+               cell1.setCellFormula("sum(D:E)");
+
+               // some values in column D
+               setValue(sheet, 1, 3, 5.0);
+               setValue(sheet, 2, 3, 6.0);
+               setValue(sheet, 5, 3, 7.0);
+               setValue(sheet, 50, 3, 8.0);
+               
+               // some values in column E
+               setValue(sheet, 1, 4, 9.0);
+               setValue(sheet, 2, 4, 10.0);
+               setValue(sheet, 30000, 4, 11.0);
+               
+               // some other values 
+               setValue(sheet, 1, 2, 100.0);
+               setValue(sheet, 2, 5, 100.0);
+               setValue(sheet, 3, 6, 100.0);
+               
+               
+               HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb);
+               assertEquals(26.0, fe.evaluate(cell0).getNumberValue(), 0.0);
+               assertEquals(56.0, fe.evaluate(cell1).getNumberValue(), 0.0);
+       }
+
+       private static void setValue(HSSFSheet sheet, int rowIndex, int colIndex, double value) {
+               HSSFRow row = sheet.getRow(rowIndex);
+               if (row == null) {
+                       row = sheet.createRow(rowIndex);
+               }
+               row.createCell(colIndex).setCellValue(value);
+       }
+}
index 4c8c8c5d6acefe51dfec3935f8183a9c0c4f6778..00db13d9101168314bfbc14d6dc3007d84265739 100644 (file)
@@ -28,15 +28,11 @@ import junit.framework.TestCase;
 
 import org.apache.poi.hssf.HSSFTestDataSamples;
 import org.apache.poi.hssf.model.Sheet;
-import org.apache.poi.hssf.record.HCenterRecord;
-import org.apache.poi.hssf.record.PasswordRecord;
-import org.apache.poi.hssf.record.ProtectRecord;
-import org.apache.poi.hssf.record.SCLRecord;
-import org.apache.poi.hssf.record.VCenterRecord;
-import org.apache.poi.hssf.record.WSBoolRecord;
-import org.apache.poi.hssf.record.WindowTwoRecord;
+import org.apache.poi.hssf.model.DrawingManager2;
+import org.apache.poi.hssf.record.*;
 import org.apache.poi.ss.util.Region;
 import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ddf.EscherDgRecord;
 
 /**
  * Tests HSSFSheet.  This test case is very incomplete at the moment.
@@ -47,806 +43,849 @@ import org.apache.poi.ss.util.CellRangeAddress;
  */
 public final class TestHSSFSheet extends TestCase {
 
-       private static HSSFWorkbook openSample(String sampleFileName) {
-               return HSSFTestDataSamples.openSampleWorkbook(sampleFileName);
-       }
-
-       /**
-        * Test the gridset field gets set as expected.
-        */
-       public void testBackupRecord() {
-               HSSFWorkbook wb = new HSSFWorkbook();
-               HSSFSheet s = wb.createSheet();
-               Sheet sheet = s.getSheet();
-
-               assertEquals(true, sheet.getGridsetRecord().getGridset());
-               s.setGridsPrinted(true);
-               assertEquals(false, sheet.getGridsetRecord().getGridset());
-       }
-
-       /**
-        * Test vertically centered output.
-        */
-       public void testVerticallyCenter() {
-               HSSFWorkbook wb = new HSSFWorkbook();
-               HSSFSheet s = wb.createSheet();
-               Sheet sheet = s.getSheet();
-               VCenterRecord record = sheet.getPageSettings().getVCenter();
-
-               assertEquals(false, record.getVCenter());
-               s.setVerticallyCenter(true);
-               assertEquals(true, record.getVCenter());
-
-               // wb.write(new FileOutputStream("c:\\test.xls"));
-       }
-
-       /**
-        * Test horizontally centered output.
-        */
-       public void testHorizontallyCenter() {
-               HSSFWorkbook wb = new HSSFWorkbook();
-               HSSFSheet s = wb.createSheet();
-               Sheet sheet = s.getSheet();
-               HCenterRecord record = sheet.getPageSettings().getHCenter();
-
-               assertEquals(false, record.getHCenter());
-               s.setHorizontallyCenter(true);
-               assertEquals(true, record.getHCenter());
-       }
-
-
-       /**
-        * Test WSBboolRecord fields get set in the user model.
-        */
-       public void testWSBool() {
-               HSSFWorkbook wb = new HSSFWorkbook();
-               HSSFSheet s = wb.createSheet();
-               Sheet sheet = s.getSheet();
-               WSBoolRecord record =
-                               (WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid);
-
-               // Check defaults
-               assertEquals(true, record.getAlternateExpression());
-               assertEquals(true, record.getAlternateFormula());
-               assertEquals(false, record.getAutobreaks());
-               assertEquals(false, record.getDialog());
-               assertEquals(false, record.getDisplayGuts());
-               assertEquals(true, record.getFitToPage());
-               assertEquals(false, record.getRowSumsBelow());
-               assertEquals(false, record.getRowSumsRight());
-
-               // Alter
-               s.setAlternativeExpression(false);
-               s.setAlternativeFormula(false);
-               s.setAutobreaks(true);
-               s.setDialog(true);
-               s.setDisplayGuts(true);
-               s.setFitToPage(false);
-               s.setRowSumsBelow(true);
-               s.setRowSumsRight(true);
-
-               // Check
-               assertEquals(false, record.getAlternateExpression());
-               assertEquals(false, record.getAlternateFormula());
-               assertEquals(true, record.getAutobreaks());
-               assertEquals(true, record.getDialog());
-               assertEquals(true, record.getDisplayGuts());
-               assertEquals(false, record.getFitToPage());
-               assertEquals(true, record.getRowSumsBelow());
-               assertEquals(true, record.getRowSumsRight());
-               assertEquals(false, s.getAlternateExpression());
-               assertEquals(false, s.getAlternateFormula());
-               assertEquals(true, s.getAutobreaks());
-               assertEquals(true, s.getDialog());
-               assertEquals(true, s.getDisplayGuts());
-               assertEquals(false, s.getFitToPage());
-               assertEquals(true, s.getRowSumsBelow());
-               assertEquals(true, s.getRowSumsRight());
-       }
-
-       public void testReadBooleans() {
-               HSSFWorkbook workbook = new HSSFWorkbook();
-               HSSFSheet sheet = workbook.createSheet("Test boolean");
-               HSSFRow row = sheet.createRow(2);
-               HSSFCell cell = row.createCell(9);
-               cell.setCellValue(true);
-               cell = row.createCell(11);
-               cell.setCellValue(true);
-               
-               workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
-
-               sheet = workbook.getSheetAt(0);
-               row = sheet.getRow(2);
-               assertNotNull(row);
-               assertEquals(2, row.getPhysicalNumberOfCells());
-       }
-
-       public void testRemoveRow() {
-               HSSFWorkbook workbook = new HSSFWorkbook();
-               HSSFSheet sheet = workbook.createSheet("Test boolean");
-               HSSFRow row = sheet.createRow(2);
-               sheet.removeRow(row);
-       }
-
-       public void testRemoveZeroRow() {
-               HSSFWorkbook workbook = new HSSFWorkbook();
-               HSSFSheet sheet = workbook.createSheet("Sheet1");
-               HSSFRow row = sheet.createRow(0);
-               try {
-                       sheet.removeRow(row);
-               } catch (IllegalArgumentException e) {
-                       if (e.getMessage().equals("Invalid row number (-1) outside allowable range (0..65535)")) {
-                               throw new AssertionFailedError("Identified bug 45367");
-                       }
-                       throw e;
-               }
-       }
-
-       public void testCloneSheet() {
-               HSSFWorkbook workbook = new HSSFWorkbook();
-               HSSFSheet sheet = workbook.createSheet("Test Clone");
-               HSSFRow row = sheet.createRow(0);
-               HSSFCell cell = row.createCell(0);
-               HSSFCell cell2 = row.createCell(1);
-               cell.setCellValue(new HSSFRichTextString("clone_test"));
-               cell2.setCellFormula("sin(1)");
-
-               HSSFSheet clonedSheet = workbook.cloneSheet(0);
-               HSSFRow clonedRow = clonedSheet.getRow(0);
-
-               //Check for a good clone
-               assertEquals(clonedRow.getCell(0).getRichStringCellValue().getString(), "clone_test");
-
-               //Check that the cells are not somehow linked
-               cell.setCellValue(new HSSFRichTextString("Difference Check"));
-               cell2.setCellFormula("cos(2)");
-               if ("Difference Check".equals(clonedRow.getCell(0).getRichStringCellValue().getString())) {
-                       fail("string cell not properly cloned");
-               }
-               if ("COS(2)".equals(clonedRow.getCell(1).getCellFormula())) {
-                       fail("formula cell not properly cloned");
-               }
-               assertEquals(clonedRow.getCell(0).getRichStringCellValue().getString(), "clone_test");
-               assertEquals(clonedRow.getCell(1).getCellFormula(), "SIN(1)");
-       }
-
-       /** tests that the sheet name for multiple clones of the same sheet is unique
-        * BUG 37416
-        */
-       public void testCloneSheetMultipleTimes() {
-               HSSFWorkbook workbook = new HSSFWorkbook();
-               HSSFSheet sheet = workbook.createSheet("Test Clone");
-               HSSFRow row = sheet.createRow(0);
-               HSSFCell cell = row.createCell(0);
-               cell.setCellValue(new HSSFRichTextString("clone_test"));
-               //Clone the sheet multiple times
-               workbook.cloneSheet(0);
-               workbook.cloneSheet(0);
-
-               assertNotNull(workbook.getSheet("Test Clone"));
-               assertNotNull(workbook.getSheet("Test Clone(1)"));
-               assertNotNull(workbook.getSheet("Test Clone(2)"));
-       }
-
-       /**
-        * Setting landscape and portrait stuff on new sheets
-        */
-       public void testPrintSetupLandscapeNew() throws Exception {
-               HSSFWorkbook workbook = new HSSFWorkbook();
-               HSSFSheet sheetL = workbook.createSheet("LandscapeS");
-               HSSFSheet sheetP = workbook.createSheet("LandscapeP");
-
-               // Check two aspects of the print setup
-               assertFalse(sheetL.getPrintSetup().getLandscape());
-               assertFalse(sheetP.getPrintSetup().getLandscape());
-               assertEquals(0, sheetL.getPrintSetup().getCopies());
-               assertEquals(0, sheetP.getPrintSetup().getCopies());
-
-               // Change one on each
-               sheetL.getPrintSetup().setLandscape(true);
-               sheetP.getPrintSetup().setCopies((short)3);
-
-               // Check taken
-               assertTrue(sheetL.getPrintSetup().getLandscape());
-               assertFalse(sheetP.getPrintSetup().getLandscape());
-               assertEquals(0, sheetL.getPrintSetup().getCopies());
-               assertEquals(3, sheetP.getPrintSetup().getCopies());
-
-               // Save and re-load, and check still there
-               ByteArrayOutputStream baos = new ByteArrayOutputStream();
-               workbook.write(baos);
-               workbook = new HSSFWorkbook(new ByteArrayInputStream(baos.toByteArray()));
-
-               assertTrue(sheetL.getPrintSetup().getLandscape());
-               assertFalse(sheetP.getPrintSetup().getLandscape());
-               assertEquals(0, sheetL.getPrintSetup().getCopies());
-               assertEquals(3, sheetP.getPrintSetup().getCopies());
-       }
-
-       /**
-        * Setting landscape and portrait stuff on existing sheets
-        */
-       public void testPrintSetupLandscapeExisting() {
-               HSSFWorkbook workbook = openSample("SimpleWithPageBreaks.xls");
-
-               assertEquals(3, workbook.getNumberOfSheets());
-
-               HSSFSheet sheetL = workbook.getSheetAt(0);
-               HSSFSheet sheetPM = workbook.getSheetAt(1);
-               HSSFSheet sheetLS = workbook.getSheetAt(2);
-
-               // Check two aspects of the print setup
-               assertFalse(sheetL.getPrintSetup().getLandscape());
-               assertTrue(sheetPM.getPrintSetup().getLandscape());
-               assertTrue(sheetLS.getPrintSetup().getLandscape());
-               assertEquals(1, sheetL.getPrintSetup().getCopies());
-               assertEquals(1, sheetPM.getPrintSetup().getCopies());
-               assertEquals(1, sheetLS.getPrintSetup().getCopies());
-
-               // Change one on each
-               sheetL.getPrintSetup().setLandscape(true);
-               sheetPM.getPrintSetup().setLandscape(false);
-               sheetPM.getPrintSetup().setCopies((short)3);
-
-               // Check taken
-               assertTrue(sheetL.getPrintSetup().getLandscape());
-               assertFalse(sheetPM.getPrintSetup().getLandscape());
-               assertTrue(sheetLS.getPrintSetup().getLandscape());
-               assertEquals(1, sheetL.getPrintSetup().getCopies());
-               assertEquals(3, sheetPM.getPrintSetup().getCopies());
-               assertEquals(1, sheetLS.getPrintSetup().getCopies());
-
-               // Save and re-load, and check still there
-               workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
-
-               assertTrue(sheetL.getPrintSetup().getLandscape());
-               assertFalse(sheetPM.getPrintSetup().getLandscape());
-               assertTrue(sheetLS.getPrintSetup().getLandscape());
-               assertEquals(1, sheetL.getPrintSetup().getCopies());
-               assertEquals(3, sheetPM.getPrintSetup().getCopies());
-               assertEquals(1, sheetLS.getPrintSetup().getCopies());
-       }
-
-       public void testGroupRows() {
-               HSSFWorkbook workbook = new HSSFWorkbook();
-               HSSFSheet s = workbook.createSheet();
-               HSSFRow r1 = s.createRow(0);
-               HSSFRow r2 = s.createRow(1);
-               HSSFRow r3 = s.createRow(2);
-               HSSFRow r4 = s.createRow(3);
-               HSSFRow r5 = s.createRow(4);
-
-               assertEquals(0, r1.getOutlineLevel());
-               assertEquals(0, r2.getOutlineLevel());
-               assertEquals(0, r3.getOutlineLevel());
-               assertEquals(0, r4.getOutlineLevel());
-               assertEquals(0, r5.getOutlineLevel());
-
-               s.groupRow(2,3);
-
-               assertEquals(0, r1.getOutlineLevel());
-               assertEquals(0, r2.getOutlineLevel());
-               assertEquals(1, r3.getOutlineLevel());
-               assertEquals(1, r4.getOutlineLevel());
-               assertEquals(0, r5.getOutlineLevel());
-
-               // Save and re-open
-               workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
-
-               s = workbook.getSheetAt(0);
-               r1 = s.getRow(0);
-               r2 = s.getRow(1);
-               r3 = s.getRow(2);
-               r4 = s.getRow(3);
-               r5 = s.getRow(4);
-
-               assertEquals(0, r1.getOutlineLevel());
-               assertEquals(0, r2.getOutlineLevel());
-               assertEquals(1, r3.getOutlineLevel());
-               assertEquals(1, r4.getOutlineLevel());
-               assertEquals(0, r5.getOutlineLevel());
-       }
-
-       public void testGroupRowsExisting() {
-               HSSFWorkbook workbook = openSample("NoGutsRecords.xls");
-
-               HSSFSheet s = workbook.getSheetAt(0);
-               HSSFRow r1 = s.getRow(0);
-               HSSFRow r2 = s.getRow(1);
-               HSSFRow r3 = s.getRow(2);
-               HSSFRow r4 = s.getRow(3);
-               HSSFRow r5 = s.getRow(4);
-               HSSFRow r6 = s.getRow(5);
-
-               assertEquals(0, r1.getOutlineLevel());
-               assertEquals(0, r2.getOutlineLevel());
-               assertEquals(0, r3.getOutlineLevel());
-               assertEquals(0, r4.getOutlineLevel());
-               assertEquals(0, r5.getOutlineLevel());
-               assertEquals(0, r6.getOutlineLevel());
-
-               // This used to complain about lacking guts records
-               s.groupRow(2, 4);
-
-               assertEquals(0, r1.getOutlineLevel());
-               assertEquals(0, r2.getOutlineLevel());
-               assertEquals(1, r3.getOutlineLevel());
-               assertEquals(1, r4.getOutlineLevel());
-               assertEquals(1, r5.getOutlineLevel());
-               assertEquals(0, r6.getOutlineLevel());
-
-               // Save and re-open
-               try {
-                       workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
-               } catch (OutOfMemoryError e) {
-                       throw new AssertionFailedError("Identified bug 39903");
-               }
-
-               s = workbook.getSheetAt(0);
-               r1 = s.getRow(0);
-               r2 = s.getRow(1);
-               r3 = s.getRow(2);
-               r4 = s.getRow(3);
-               r5 = s.getRow(4);
-               r6 = s.getRow(5);
-
-               assertEquals(0, r1.getOutlineLevel());
-               assertEquals(0, r2.getOutlineLevel());
-               assertEquals(1, r3.getOutlineLevel());
-               assertEquals(1, r4.getOutlineLevel());
-               assertEquals(1, r5.getOutlineLevel());
-               assertEquals(0, r6.getOutlineLevel());
-       }
-
-       public void testGetDrawings() {
-               HSSFWorkbook wb1c = openSample("WithChart.xls");
-               HSSFWorkbook wb2c = openSample("WithTwoCharts.xls");
-
-               // 1 chart sheet -> data on 1st, chart on 2nd
-               assertNotNull(wb1c.getSheetAt(0).getDrawingPatriarch());
-               assertNotNull(wb1c.getSheetAt(1).getDrawingPatriarch());
-               assertFalse(wb1c.getSheetAt(0).getDrawingPatriarch().containsChart());
-               assertTrue(wb1c.getSheetAt(1).getDrawingPatriarch().containsChart());
-
-               // 2 chart sheet -> data on 1st, chart on 2nd+3rd
-               assertNotNull(wb2c.getSheetAt(0).getDrawingPatriarch());
-               assertNotNull(wb2c.getSheetAt(1).getDrawingPatriarch());
-               assertNotNull(wb2c.getSheetAt(2).getDrawingPatriarch());
-               assertFalse(wb2c.getSheetAt(0).getDrawingPatriarch().containsChart());
-               assertTrue(wb2c.getSheetAt(1).getDrawingPatriarch().containsChart());
-               assertTrue(wb2c.getSheetAt(2).getDrawingPatriarch().containsChart());
-       }
-
-       /**
-        * Test that the ProtectRecord is included when creating or cloning a sheet
-        */
-       public void testProtect() {
-               HSSFWorkbook workbook = new HSSFWorkbook();
-               HSSFSheet hssfSheet = workbook.createSheet();
-               Sheet sheet = hssfSheet.getSheet();
-               ProtectRecord protect = sheet.getProtect();
-
-               assertFalse(protect.getProtect());
-
-               // This will tell us that cloneSheet, and by extension,
-               // the list forms of createSheet leave us with an accessible
-               // ProtectRecord.
-               hssfSheet.setProtect(true);
-               Sheet cloned = sheet.cloneSheet();
-               assertNotNull(cloned.getProtect());
-               assertTrue(hssfSheet.getProtect());
-       }
-
-       public void testProtectSheet() {
-               short expected = (short)0xfef1;
-               HSSFWorkbook wb = new HSSFWorkbook();
-               HSSFSheet s = wb.createSheet();
-               s.protectSheet("abcdefghij");
-               Sheet sheet = s.getSheet();
-               ProtectRecord protect = sheet.getProtect();
-               PasswordRecord pass = sheet.getPassword();
-               assertTrue("protection should be on",protect.getProtect());
-               assertTrue("object protection should be on",sheet.isProtected()[1]);
-               assertTrue("scenario protection should be on",sheet.isProtected()[2]);
-               assertEquals("well known value for top secret hash should be "+Integer.toHexString(expected).substring(4),expected,pass.getPassword());
-       }
-
-
-       public void testZoom() {
-               HSSFWorkbook wb = new HSSFWorkbook();
-               HSSFSheet sheet = wb.createSheet();
-               assertEquals(-1, sheet.getSheet().findFirstRecordLocBySid(SCLRecord.sid));
-               sheet.setZoom(3,4);
-               assertTrue(sheet.getSheet().findFirstRecordLocBySid(SCLRecord.sid) > 0);
-               SCLRecord sclRecord = (SCLRecord) sheet.getSheet().findFirstRecordBySid(SCLRecord.sid);
-               assertEquals(3, sclRecord.getNumerator());
-               assertEquals(4, sclRecord.getDenominator());
-
-               int sclLoc = sheet.getSheet().findFirstRecordLocBySid(SCLRecord.sid);
-               int window2Loc = sheet.getSheet().findFirstRecordLocBySid(WindowTwoRecord.sid);
-               assertTrue(sclLoc == window2Loc + 1);
-       }
-
-
-       /**
-        * When removing one merged region, it would break
-        *
-        */
-       public void testRemoveMerged() {
-               HSSFWorkbook wb = new HSSFWorkbook();
-               HSSFSheet sheet = wb.createSheet();
-               CellRangeAddress region = new CellRangeAddress(0, 1, 0, 1);
-               sheet.addMergedRegion(region);
-               region = new CellRangeAddress(1, 2, 0, 1);
-               sheet.addMergedRegion(region);
-
-               sheet.removeMergedRegion(0);
-
-               region = sheet.getMergedRegion(0);
-               assertEquals("Left over region should be starting at row 1", 1, region.getFirstRow());
-
-               sheet.removeMergedRegion(0);
-
-               assertEquals("there should be no merged regions left!", 0, sheet.getNumMergedRegions());
-
-               //an, add, remove, get(0) would null pointer
-               sheet.addMergedRegion(region);
-               assertEquals("there should now be one merged region!", 1, sheet.getNumMergedRegions());
-               sheet.removeMergedRegion(0);
-               assertEquals("there should now be zero merged regions!", 0, sheet.getNumMergedRegions());
-               //add it again!
-               region.setLastRow(4);
-
-               sheet.addMergedRegion(region);
-               assertEquals("there should now be one merged region!", 1, sheet.getNumMergedRegions());
-
-               //should exist now!
-               assertTrue("there isn't more than one merged region in there", 1 <= sheet.getNumMergedRegions());
-               region = sheet.getMergedRegion(0);
-               assertEquals("the merged row to doesnt match the one we put in ", 4, region.getLastRow());
-       }
-
-       public void testShiftMerged() {
-               HSSFWorkbook wb = new HSSFWorkbook();
-               HSSFSheet sheet = wb.createSheet();
-               HSSFRow row = sheet.createRow(0);
-               HSSFCell cell = row.createCell(0);
-               cell.setCellValue(new HSSFRichTextString("first row, first cell"));
-
-               row = sheet.createRow(1);
-               cell = row.createCell(1);
-               cell.setCellValue(new HSSFRichTextString("second row, second cell"));
-
-               CellRangeAddress region = new CellRangeAddress(1, 1, 0, 1);
-               sheet.addMergedRegion(region);
-
-               sheet.shiftRows(1, 1, 1);
-
-               region = sheet.getMergedRegion(0);
-               assertEquals("Merged region not moved over to row 2", 2, region.getFirstRow());
-       }
-
-       /**
-        * Tests the display of gridlines, formulas, and rowcolheadings.
-        * @author Shawn Laubach (slaubach at apache dot org)
-        */
-       public void testDisplayOptions() {
-               HSSFWorkbook wb = new HSSFWorkbook();
-               HSSFSheet sheet = wb.createSheet();
-
-               wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
-               sheet = wb.getSheetAt(0);
-
-               assertEquals(sheet.isDisplayGridlines(), true);
-               assertEquals(sheet.isDisplayRowColHeadings(), true);
-               assertEquals(sheet.isDisplayFormulas(), false);
-
-               sheet.setDisplayGridlines(false);
-               sheet.setDisplayRowColHeadings(false);
-               sheet.setDisplayFormulas(true);
-
-               wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
-               sheet = wb.getSheetAt(0);
-
-               assertEquals(sheet.isDisplayGridlines(), false);
-               assertEquals(sheet.isDisplayRowColHeadings(), false);
-               assertEquals(sheet.isDisplayFormulas(), true);
-       }
-
-
-       /**
-        * Make sure the excel file loads work
-        *
-        */
-       public void testPageBreakFiles() {
-               HSSFWorkbook wb = openSample("SimpleWithPageBreaks.xls");
-
-               HSSFSheet sheet = wb.getSheetAt(0);
-               assertNotNull(sheet);
-
-               assertEquals("1 row page break", 1, sheet.getRowBreaks().length);
-               assertEquals("1 column page break", 1, sheet.getColumnBreaks().length);
-
-               assertTrue("No row page break", sheet.isRowBroken(22));
-               assertTrue("No column page break", sheet.isColumnBroken((short)4));
-
-               sheet.setRowBreak(10);
-               sheet.setColumnBreak((short)13);
-
-               assertEquals("row breaks number", 2, sheet.getRowBreaks().length);
-               assertEquals("column breaks number", 2, sheet.getColumnBreaks().length);
-
-               wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
-               sheet = wb.getSheetAt(0);
-
-               assertTrue("No row page break", sheet.isRowBroken(22));
-               assertTrue("No column page break", sheet.isColumnBroken((short)4));
-
-               assertEquals("row breaks number", 2, sheet.getRowBreaks().length);
-               assertEquals("column breaks number", 2, sheet.getColumnBreaks().length);
-       }
-
-       public void testDBCSName () {
-               HSSFWorkbook wb = openSample("DBCSSheetName.xls");
-               wb.getSheetAt(1);
-               assertEquals ("DBCS Sheet Name 2", wb.getSheetName(1),"\u090f\u0915" );
-               assertEquals("DBCS Sheet Name 1", wb.getSheetName(0),"\u091c\u093e");
-       }
-
-       /**
-        * Testing newly added method that exposes the WINDOW2.toprow
-        * parameter to allow setting the toprow in the visible view
-        * of the sheet when it is first opened.
-        */
-       public void testTopRow() {
-               HSSFWorkbook wb = openSample("SimpleWithPageBreaks.xls");
-
-               HSSFSheet sheet = wb.getSheetAt(0);
-               assertNotNull(sheet);
-
-               short toprow = (short) 100;
-               short leftcol = (short) 50;
-               sheet.showInPane(toprow,leftcol);
-               assertEquals("HSSFSheet.getTopRow()", toprow, sheet.getTopRow());
-               assertEquals("HSSFSheet.getLeftCol()", leftcol, sheet.getLeftCol());
-       }
-
-       /** cell with formula becomes null on cloning a sheet*/
-        public void test35084() {
-
-               HSSFWorkbook wb = new HSSFWorkbook();
-               HSSFSheet s = wb.createSheet("Sheet1");
-               HSSFRow r = s.createRow(0);
-               r.createCell(0).setCellValue(1);
-               r.createCell(1).setCellFormula("A1*2");
-               HSSFSheet s1 = wb.cloneSheet(0);
-               r = s1.getRow(0);
-               assertEquals("double", r.getCell(0).getNumericCellValue(), 1, 0); // sanity check
-               assertNotNull(r.getCell(1));
-               assertEquals("formula", r.getCell(1).getCellFormula(), "A1*2");
-       }
-
-       /** test that new default column styles get applied */
-       public void testDefaultColumnStyle() {
-               HSSFWorkbook wb = new HSSFWorkbook();
-               HSSFCellStyle style = wb.createCellStyle();
-               HSSFSheet s = wb.createSheet();
-               s.setDefaultColumnStyle((short) 0, style);
-               HSSFRow r = s.createRow(0);
-               HSSFCell c = r.createCell(0);
-               assertEquals("style should match", style.getIndex(), c.getCellStyle().getIndex());
-       }
-
-
-       /**
-        *
-        */
-       public void testAddEmptyRow() {
-               //try to add 5 empty rows to a new sheet
-               HSSFWorkbook workbook = new HSSFWorkbook();
-               HSSFSheet sheet = workbook.createSheet();
-               for (int i = 0; i < 5; i++) {
-                       sheet.createRow(i);
-               }
-
-               workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
-
-               //try adding empty rows in an existing worksheet
-               workbook = openSample("Simple.xls");
-
-               sheet = workbook.getSheetAt(0);
-               for (int i = 3; i < 10; i++) sheet.createRow(i);
-
-               workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
-       }
-
-       public void testAutoSizeColumn() {
-               HSSFWorkbook wb = openSample("43902.xls");
-               String sheetName = "my sheet";
-               HSSFSheet sheet = wb.getSheet(sheetName);
-
-               // Can't use literal numbers for column sizes, as
-               //  will come out with different values on different
-               //  machines based on the fonts available.
-               // So, we use ranges, which are pretty large, but
-               //  thankfully don't overlap!
-               int minWithRow1And2 = 6400;
-               int maxWithRow1And2 = 7800;
-               int minWithRow1Only = 2750;
-               int maxWithRow1Only = 3300;
-
-               // autoSize the first column and check its size before the merged region (1,0,1,1) is set:
-               // it has to be based on the 2nd row width
-               sheet.autoSizeColumn((short)0);
-               assertTrue("Column autosized with only one row: wrong width", sheet.getColumnWidth((short)0) >= minWithRow1And2);
-               assertTrue("Column autosized with only one row: wrong width", sheet.getColumnWidth((short)0) <= maxWithRow1And2);
-
-               //create a region over the 2nd row and auto size the first column
-               sheet.addMergedRegion(new CellRangeAddress(1,1,0,1));
-               sheet.autoSizeColumn((short)0);
-               HSSFWorkbook wb2 = HSSFTestDataSamples.writeOutAndReadBack(wb);
-
-               // check that the autoSized column width has ignored the 2nd row
-               // because it is included in a merged region (Excel like behavior)
-               HSSFSheet sheet2 = wb2.getSheet(sheetName);
-               assertTrue(sheet2.getColumnWidth((short)0) >= minWithRow1Only);
-               assertTrue(sheet2.getColumnWidth((short)0) <= maxWithRow1Only);
-
-               // remove the 2nd row merged region and check that the 2nd row value is used to the autoSizeColumn width
-               sheet2.removeMergedRegion(1);
-               sheet2.autoSizeColumn((short)0);
-               HSSFWorkbook wb3 = HSSFTestDataSamples.writeOutAndReadBack(wb2);
-               HSSFSheet sheet3 = wb3.getSheet(sheetName);
-               assertTrue(sheet3.getColumnWidth((short)0) >= minWithRow1And2);
-               assertTrue(sheet3.getColumnWidth((short)0) <= maxWithRow1And2);
-       }
-
-       /**
-        * Setting ForceFormulaRecalculation on sheets
-        */
-       public void testForceRecalculation() throws Exception {
-               HSSFWorkbook workbook = openSample("UncalcedRecord.xls");
-
-               HSSFSheet sheet = workbook.getSheetAt(0);
-               HSSFSheet sheet2 = workbook.getSheetAt(0);
-               HSSFRow row = sheet.getRow(0);
-               row.createCell(0).setCellValue(5);
-               row.createCell(1).setCellValue(8);
-               assertFalse(sheet.getForceFormulaRecalculation());
-               assertFalse(sheet2.getForceFormulaRecalculation());
-
-               // Save and manually verify that on column C we have 0, value in template
-               File tempFile = new File(System.getProperty("java.io.tmpdir")+"/uncalced_err.xls" );
-               tempFile.delete();
-               FileOutputStream fout = new FileOutputStream( tempFile );
-               workbook.write( fout );
-               fout.close();
-               sheet.setForceFormulaRecalculation(true);
-               assertTrue(sheet.getForceFormulaRecalculation());
-
-               // Save and manually verify that on column C we have now 13, calculated value
-               tempFile = new File(System.getProperty("java.io.tmpdir")+"/uncalced_succ.xls" );
-               tempFile.delete();
-               fout = new FileOutputStream( tempFile );
-               workbook.write( fout );
-               fout.close();
-
-               // Try it can be opened
-               HSSFWorkbook wb2 = new HSSFWorkbook(new FileInputStream(tempFile));
-
-               // And check correct sheet settings found
-               sheet = wb2.getSheetAt(0);
-               sheet2 = wb2.getSheetAt(1);
-               assertTrue(sheet.getForceFormulaRecalculation());
-               assertFalse(sheet2.getForceFormulaRecalculation());
-
-               // Now turn if back off again
-               sheet.setForceFormulaRecalculation(false);
-
-               fout = new FileOutputStream( tempFile );
-               wb2.write( fout );
-               fout.close();
-               wb2 = new HSSFWorkbook(new FileInputStream(tempFile));
-
-               assertFalse(wb2.getSheetAt(0).getForceFormulaRecalculation());
-               assertFalse(wb2.getSheetAt(1).getForceFormulaRecalculation());
-               assertFalse(wb2.getSheetAt(2).getForceFormulaRecalculation());
-
-               // Now add a new sheet, and check things work
-               //  with old ones unset, new one set
-               HSSFSheet s4 = wb2.createSheet();
-               s4.setForceFormulaRecalculation(true);
-
-               assertFalse(sheet.getForceFormulaRecalculation());
-               assertFalse(sheet2.getForceFormulaRecalculation());
-               assertTrue(s4.getForceFormulaRecalculation());
-
-               fout = new FileOutputStream( tempFile );
-               wb2.write( fout );
-               fout.close();
-
-               HSSFWorkbook wb3 = new HSSFWorkbook(new FileInputStream(tempFile));
-               assertFalse(wb3.getSheetAt(0).getForceFormulaRecalculation());
-               assertFalse(wb3.getSheetAt(1).getForceFormulaRecalculation());
-               assertFalse(wb3.getSheetAt(2).getForceFormulaRecalculation());
-               assertTrue(wb3.getSheetAt(3).getForceFormulaRecalculation());
-       }
-
-       public void testColumnWidth() {
-               //check we can correctly read column widths from a reference workbook
-               HSSFWorkbook wb = openSample("colwidth.xls");
-
-               //reference values
-               int[] ref = {365, 548, 731, 914, 1097, 1280, 1462, 1645, 1828, 2011, 2194, 2377, 2560, 2742, 2925, 3108, 3291, 3474, 3657};
-
-               HSSFSheet sh = wb.getSheetAt(0);
-               for (char i = 'A'; i <= 'S'; i++) {
-                       int idx = i - 'A';
-                       int w = sh.getColumnWidth((short)idx);
-                       assertEquals(ref[idx], w);
-               }
-
-               //the second sheet doesn't have overridden column widths
-               sh = wb.getSheetAt(1);
-               int def_width = sh.getDefaultColumnWidth();
-               for (char i = 'A'; i <= 'S'; i++) {
-                       int idx = i - 'A';
-                       int w = sh.getColumnWidth((short)idx);
-                       //getDefaultColumnWidth returns width measued in characters
-                       //getColumnWidth returns width measued in 1/256th units
-                       assertEquals(def_width*256, w);
-               }
-
-               //test new workbook
-               wb = new HSSFWorkbook();
-               sh = wb.createSheet();
-               sh.setDefaultColumnWidth((short)10);
-               assertEquals(10, sh.getDefaultColumnWidth());
-               assertEquals(256*10, sh.getColumnWidth((short)0));
-               assertEquals(256*10, sh.getColumnWidth((short)1));
-               assertEquals(256*10, sh.getColumnWidth((short)2));
-               for (char i = 'D'; i <= 'F'; i++) {
-                       short w = (short)(256*12);
-                       sh.setColumnWidth((short)i, w);
-                       assertEquals(w, sh.getColumnWidth((short)i));
-               }
-
-               //serialize and read again
-               wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
-
-               sh = wb.getSheetAt(0);
-               assertEquals(10, sh.getDefaultColumnWidth());
-               //columns A-C have default width
-               assertEquals(256*10, sh.getColumnWidth((short)0));
-               assertEquals(256*10, sh.getColumnWidth((short)1));
-               assertEquals(256*10, sh.getColumnWidth((short)2));
-               //columns D-F have custom width
-               for (char i = 'D'; i <= 'F'; i++) {
-                       short w = (short)(256*12);
-                       assertEquals(w, sh.getColumnWidth((short)i));
-               }
-       }
-       
-       /**
-        * Some utilities write Excel files without the ROW records.
-        * Excel, ooo, and google docs are OK with this.
-        * Now POI is too.
-        */
-       public void testMissingRowRecords_bug41187() {
-               HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("ex41187-19267.xls");
-
-               HSSFSheet sheet = wb.getSheetAt(0);
-               HSSFRow row = sheet.getRow(0);
-               if(row == null) {
-                       throw new AssertionFailedError("Identified bug 41187 a");
-               }
-               if (row.getHeight() == 0) {
-                       throw new AssertionFailedError("Identified bug 41187 b");
-               }
-               assertEquals("Hi Excel!", row.getCell(0).getRichStringCellValue().getString());
-               // check row height for 'default' flag
-               assertEquals((short)0xFF, row.getHeight());
-
-               HSSFTestDataSamples.writeOutAndReadBack(wb);
-       }
+    private static HSSFWorkbook openSample(String sampleFileName) {
+        return HSSFTestDataSamples.openSampleWorkbook(sampleFileName);
+    }
+
+    /**
+     * Test the gridset field gets set as expected.
+     */
+    public void testBackupRecord() {
+        HSSFWorkbook wb = new HSSFWorkbook();
+        HSSFSheet s = wb.createSheet();
+        Sheet sheet = s.getSheet();
+
+        assertEquals(true, sheet.getGridsetRecord().getGridset());
+        s.setGridsPrinted(true);
+        assertEquals(false, sheet.getGridsetRecord().getGridset());
+    }
+
+    /**
+     * Test vertically centered output.
+     */
+    public void testVerticallyCenter() {
+        HSSFWorkbook wb = new HSSFWorkbook();
+        HSSFSheet s = wb.createSheet();
+        Sheet sheet = s.getSheet();
+        VCenterRecord record = sheet.getPageSettings().getVCenter();
+
+        assertEquals(false, record.getVCenter());
+        s.setVerticallyCenter(true);
+        assertEquals(true, record.getVCenter());
+
+        // wb.write(new FileOutputStream("c:\\test.xls"));
+    }
+
+    /**
+     * Test horizontally centered output.
+     */
+    public void testHorizontallyCenter() {
+        HSSFWorkbook wb = new HSSFWorkbook();
+        HSSFSheet s = wb.createSheet();
+        Sheet sheet = s.getSheet();
+        HCenterRecord record = sheet.getPageSettings().getHCenter();
+
+        assertEquals(false, record.getHCenter());
+        s.setHorizontallyCenter(true);
+        assertEquals(true, record.getHCenter());
+    }
+
+
+    /**
+     * Test WSBboolRecord fields get set in the user model.
+     */
+    public void testWSBool() {
+        HSSFWorkbook wb = new HSSFWorkbook();
+        HSSFSheet s = wb.createSheet();
+        Sheet sheet = s.getSheet();
+        WSBoolRecord record =
+                (WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid);
+
+        // Check defaults
+        assertEquals(true, record.getAlternateExpression());
+        assertEquals(true, record.getAlternateFormula());
+        assertEquals(false, record.getAutobreaks());
+        assertEquals(false, record.getDialog());
+        assertEquals(false, record.getDisplayGuts());
+        assertEquals(true, record.getFitToPage());
+        assertEquals(false, record.getRowSumsBelow());
+        assertEquals(false, record.getRowSumsRight());
+
+        // Alter
+        s.setAlternativeExpression(false);
+        s.setAlternativeFormula(false);
+        s.setAutobreaks(true);
+        s.setDialog(true);
+        s.setDisplayGuts(true);
+        s.setFitToPage(false);
+        s.setRowSumsBelow(true);
+        s.setRowSumsRight(true);
+
+        // Check
+        assertEquals(false, record.getAlternateExpression());
+        assertEquals(false, record.getAlternateFormula());
+        assertEquals(true, record.getAutobreaks());
+        assertEquals(true, record.getDialog());
+        assertEquals(true, record.getDisplayGuts());
+        assertEquals(false, record.getFitToPage());
+        assertEquals(true, record.getRowSumsBelow());
+        assertEquals(true, record.getRowSumsRight());
+        assertEquals(false, s.getAlternateExpression());
+        assertEquals(false, s.getAlternateFormula());
+        assertEquals(true, s.getAutobreaks());
+        assertEquals(true, s.getDialog());
+        assertEquals(true, s.getDisplayGuts());
+        assertEquals(false, s.getFitToPage());
+        assertEquals(true, s.getRowSumsBelow());
+        assertEquals(true, s.getRowSumsRight());
+    }
+
+    public void testReadBooleans() {
+        HSSFWorkbook workbook = new HSSFWorkbook();
+        HSSFSheet sheet = workbook.createSheet("Test boolean");
+        HSSFRow row = sheet.createRow(2);
+        HSSFCell cell = row.createCell(9);
+        cell.setCellValue(true);
+        cell = row.createCell(11);
+        cell.setCellValue(true);
+
+        workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
+
+        sheet = workbook.getSheetAt(0);
+        row = sheet.getRow(2);
+        assertNotNull(row);
+        assertEquals(2, row.getPhysicalNumberOfCells());
+    }
+
+    public void testRemoveRow() {
+        HSSFWorkbook workbook = new HSSFWorkbook();
+        HSSFSheet sheet = workbook.createSheet("Test boolean");
+        HSSFRow row = sheet.createRow(2);
+        sheet.removeRow(row);
+    }
+
+    public void testRemoveZeroRow() {
+        HSSFWorkbook workbook = new HSSFWorkbook();
+        HSSFSheet sheet = workbook.createSheet("Sheet1");
+        HSSFRow row = sheet.createRow(0);
+        try {
+            sheet.removeRow(row);
+        } catch (IllegalArgumentException e) {
+            if (e.getMessage().equals("Invalid row number (-1) outside allowable range (0..65535)")) {
+                throw new AssertionFailedError("Identified bug 45367");
+            }
+            throw e;
+        }
+    }
+
+    public void testCloneSheet() {
+        HSSFWorkbook workbook = new HSSFWorkbook();
+        HSSFSheet sheet = workbook.createSheet("Test Clone");
+        HSSFRow row = sheet.createRow(0);
+        HSSFCell cell = row.createCell(0);
+        HSSFCell cell2 = row.createCell(1);
+        cell.setCellValue(new HSSFRichTextString("clone_test"));
+        cell2.setCellFormula("sin(1)");
+
+        HSSFSheet clonedSheet = workbook.cloneSheet(0);
+        HSSFRow clonedRow = clonedSheet.getRow(0);
+
+        //Check for a good clone
+        assertEquals(clonedRow.getCell(0).getRichStringCellValue().getString(), "clone_test");
+
+        //Check that the cells are not somehow linked
+        cell.setCellValue(new HSSFRichTextString("Difference Check"));
+        cell2.setCellFormula("cos(2)");
+        if ("Difference Check".equals(clonedRow.getCell(0).getRichStringCellValue().getString())) {
+            fail("string cell not properly cloned");
+        }
+        if ("COS(2)".equals(clonedRow.getCell(1).getCellFormula())) {
+            fail("formula cell not properly cloned");
+        }
+        assertEquals(clonedRow.getCell(0).getRichStringCellValue().getString(), "clone_test");
+        assertEquals(clonedRow.getCell(1).getCellFormula(), "SIN(1)");
+    }
+
+    /** tests that the sheet name for multiple clones of the same sheet is unique
+     * BUG 37416
+     */
+    public void testCloneSheetMultipleTimes() {
+        HSSFWorkbook workbook = new HSSFWorkbook();
+        HSSFSheet sheet = workbook.createSheet("Test Clone");
+        HSSFRow row = sheet.createRow(0);
+        HSSFCell cell = row.createCell(0);
+        cell.setCellValue(new HSSFRichTextString("clone_test"));
+        //Clone the sheet multiple times
+        workbook.cloneSheet(0);
+        workbook.cloneSheet(0);
+
+        assertNotNull(workbook.getSheet("Test Clone"));
+        assertNotNull(workbook.getSheet("Test Clone (2)"));
+        assertEquals("Test Clone (3)", workbook.getSheetName(2));
+        assertNotNull(workbook.getSheet("Test Clone (3)"));
+
+        workbook.removeSheetAt(0);
+        workbook.removeSheetAt(0);
+        workbook.removeSheetAt(0);
+        workbook.createSheet("abc ( 123)");
+        workbook.cloneSheet(0);
+        assertEquals("abc (124)", workbook.getSheetName(1));
+    }
+
+    /**
+     * Setting landscape and portrait stuff on new sheets
+     */
+    public void testPrintSetupLandscapeNew() throws Exception {
+        HSSFWorkbook workbook = new HSSFWorkbook();
+        HSSFSheet sheetL = workbook.createSheet("LandscapeS");
+        HSSFSheet sheetP = workbook.createSheet("LandscapeP");
+
+        // Check two aspects of the print setup
+        assertFalse(sheetL.getPrintSetup().getLandscape());
+        assertFalse(sheetP.getPrintSetup().getLandscape());
+        assertEquals(0, sheetL.getPrintSetup().getCopies());
+        assertEquals(0, sheetP.getPrintSetup().getCopies());
+
+        // Change one on each
+        sheetL.getPrintSetup().setLandscape(true);
+        sheetP.getPrintSetup().setCopies((short)3);
+
+        // Check taken
+        assertTrue(sheetL.getPrintSetup().getLandscape());
+        assertFalse(sheetP.getPrintSetup().getLandscape());
+        assertEquals(0, sheetL.getPrintSetup().getCopies());
+        assertEquals(3, sheetP.getPrintSetup().getCopies());
+
+        // Save and re-load, and check still there
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        workbook.write(baos);
+        workbook = new HSSFWorkbook(new ByteArrayInputStream(baos.toByteArray()));
+
+        assertTrue(sheetL.getPrintSetup().getLandscape());
+        assertFalse(sheetP.getPrintSetup().getLandscape());
+        assertEquals(0, sheetL.getPrintSetup().getCopies());
+        assertEquals(3, sheetP.getPrintSetup().getCopies());
+    }
+
+    /**
+     * Setting landscape and portrait stuff on existing sheets
+     */
+    public void testPrintSetupLandscapeExisting() {
+        HSSFWorkbook workbook = openSample("SimpleWithPageBreaks.xls");
+
+        assertEquals(3, workbook.getNumberOfSheets());
+
+        HSSFSheet sheetL = workbook.getSheetAt(0);
+        HSSFSheet sheetPM = workbook.getSheetAt(1);
+        HSSFSheet sheetLS = workbook.getSheetAt(2);
+
+        // Check two aspects of the print setup
+        assertFalse(sheetL.getPrintSetup().getLandscape());
+        assertTrue(sheetPM.getPrintSetup().getLandscape());
+        assertTrue(sheetLS.getPrintSetup().getLandscape());
+        assertEquals(1, sheetL.getPrintSetup().getCopies());
+        assertEquals(1, sheetPM.getPrintSetup().getCopies());
+        assertEquals(1, sheetLS.getPrintSetup().getCopies());
+
+        // Change one on each
+        sheetL.getPrintSetup().setLandscape(true);
+        sheetPM.getPrintSetup().setLandscape(false);
+        sheetPM.getPrintSetup().setCopies((short)3);
+
+        // Check taken
+        assertTrue(sheetL.getPrintSetup().getLandscape());
+        assertFalse(sheetPM.getPrintSetup().getLandscape());
+        assertTrue(sheetLS.getPrintSetup().getLandscape());
+        assertEquals(1, sheetL.getPrintSetup().getCopies());
+        assertEquals(3, sheetPM.getPrintSetup().getCopies());
+        assertEquals(1, sheetLS.getPrintSetup().getCopies());
+
+        // Save and re-load, and check still there
+        workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
+
+        assertTrue(sheetL.getPrintSetup().getLandscape());
+        assertFalse(sheetPM.getPrintSetup().getLandscape());
+        assertTrue(sheetLS.getPrintSetup().getLandscape());
+        assertEquals(1, sheetL.getPrintSetup().getCopies());
+        assertEquals(3, sheetPM.getPrintSetup().getCopies());
+        assertEquals(1, sheetLS.getPrintSetup().getCopies());
+    }
+
+    public void testGroupRows() {
+        HSSFWorkbook workbook = new HSSFWorkbook();
+        HSSFSheet s = workbook.createSheet();
+        HSSFRow r1 = s.createRow(0);
+        HSSFRow r2 = s.createRow(1);
+        HSSFRow r3 = s.createRow(2);
+        HSSFRow r4 = s.createRow(3);
+        HSSFRow r5 = s.createRow(4);
+
+        assertEquals(0, r1.getOutlineLevel());
+        assertEquals(0, r2.getOutlineLevel());
+        assertEquals(0, r3.getOutlineLevel());
+        assertEquals(0, r4.getOutlineLevel());
+        assertEquals(0, r5.getOutlineLevel());
+
+        s.groupRow(2,3);
+
+        assertEquals(0, r1.getOutlineLevel());
+        assertEquals(0, r2.getOutlineLevel());
+        assertEquals(1, r3.getOutlineLevel());
+        assertEquals(1, r4.getOutlineLevel());
+        assertEquals(0, r5.getOutlineLevel());
+
+        // Save and re-open
+        workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
+
+        s = workbook.getSheetAt(0);
+        r1 = s.getRow(0);
+        r2 = s.getRow(1);
+        r3 = s.getRow(2);
+        r4 = s.getRow(3);
+        r5 = s.getRow(4);
+
+        assertEquals(0, r1.getOutlineLevel());
+        assertEquals(0, r2.getOutlineLevel());
+        assertEquals(1, r3.getOutlineLevel());
+        assertEquals(1, r4.getOutlineLevel());
+        assertEquals(0, r5.getOutlineLevel());
+    }
+
+    public void testGroupRowsExisting() {
+        HSSFWorkbook workbook = openSample("NoGutsRecords.xls");
+
+        HSSFSheet s = workbook.getSheetAt(0);
+        HSSFRow r1 = s.getRow(0);
+        HSSFRow r2 = s.getRow(1);
+        HSSFRow r3 = s.getRow(2);
+        HSSFRow r4 = s.getRow(3);
+        HSSFRow r5 = s.getRow(4);
+        HSSFRow r6 = s.getRow(5);
+
+        assertEquals(0, r1.getOutlineLevel());
+        assertEquals(0, r2.getOutlineLevel());
+        assertEquals(0, r3.getOutlineLevel());
+        assertEquals(0, r4.getOutlineLevel());
+        assertEquals(0, r5.getOutlineLevel());
+        assertEquals(0, r6.getOutlineLevel());
+
+        // This used to complain about lacking guts records
+        s.groupRow(2, 4);
+
+        assertEquals(0, r1.getOutlineLevel());
+        assertEquals(0, r2.getOutlineLevel());
+        assertEquals(1, r3.getOutlineLevel());
+        assertEquals(1, r4.getOutlineLevel());
+        assertEquals(1, r5.getOutlineLevel());
+        assertEquals(0, r6.getOutlineLevel());
+
+        // Save and re-open
+        try {
+            workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
+        } catch (OutOfMemoryError e) {
+            throw new AssertionFailedError("Identified bug 39903");
+        }
+
+        s = workbook.getSheetAt(0);
+        r1 = s.getRow(0);
+        r2 = s.getRow(1);
+        r3 = s.getRow(2);
+        r4 = s.getRow(3);
+        r5 = s.getRow(4);
+        r6 = s.getRow(5);
+
+        assertEquals(0, r1.getOutlineLevel());
+        assertEquals(0, r2.getOutlineLevel());
+        assertEquals(1, r3.getOutlineLevel());
+        assertEquals(1, r4.getOutlineLevel());
+        assertEquals(1, r5.getOutlineLevel());
+        assertEquals(0, r6.getOutlineLevel());
+    }
+
+    public void testGetDrawings() {
+        HSSFWorkbook wb1c = openSample("WithChart.xls");
+        HSSFWorkbook wb2c = openSample("WithTwoCharts.xls");
+
+        // 1 chart sheet -> data on 1st, chart on 2nd
+        assertNotNull(wb1c.getSheetAt(0).getDrawingPatriarch());
+        assertNotNull(wb1c.getSheetAt(1).getDrawingPatriarch());
+        assertFalse(wb1c.getSheetAt(0).getDrawingPatriarch().containsChart());
+        assertTrue(wb1c.getSheetAt(1).getDrawingPatriarch().containsChart());
+
+        // 2 chart sheet -> data on 1st, chart on 2nd+3rd
+        assertNotNull(wb2c.getSheetAt(0).getDrawingPatriarch());
+        assertNotNull(wb2c.getSheetAt(1).getDrawingPatriarch());
+        assertNotNull(wb2c.getSheetAt(2).getDrawingPatriarch());
+        assertFalse(wb2c.getSheetAt(0).getDrawingPatriarch().containsChart());
+        assertTrue(wb2c.getSheetAt(1).getDrawingPatriarch().containsChart());
+        assertTrue(wb2c.getSheetAt(2).getDrawingPatriarch().containsChart());
+    }
+
+    /**
+     * Test that the ProtectRecord is included when creating or cloning a sheet
+     */
+    public void testProtect() {
+        HSSFWorkbook workbook = new HSSFWorkbook();
+        HSSFSheet hssfSheet = workbook.createSheet();
+        Sheet sheet = hssfSheet.getSheet();
+        ProtectRecord protect = sheet.getProtect();
+
+        assertFalse(protect.getProtect());
+
+        // This will tell us that cloneSheet, and by extension,
+        // the list forms of createSheet leave us with an accessible
+        // ProtectRecord.
+        hssfSheet.setProtect(true);
+        Sheet cloned = sheet.cloneSheet();
+        assertNotNull(cloned.getProtect());
+        assertTrue(hssfSheet.getProtect());
+    }
+
+    public void testProtectSheet() {
+        short expected = (short)0xfef1;
+        HSSFWorkbook wb = new HSSFWorkbook();
+        HSSFSheet s = wb.createSheet();
+        s.protectSheet("abcdefghij");
+        Sheet sheet = s.getSheet();
+        ProtectRecord protect = sheet.getProtect();
+        PasswordRecord pass = sheet.getPassword();
+        assertTrue("protection should be on",protect.getProtect());
+        assertTrue("object protection should be on",sheet.isProtected()[1]);
+        assertTrue("scenario protection should be on",sheet.isProtected()[2]);
+        assertEquals("well known value for top secret hash should be "+Integer.toHexString(expected).substring(4),expected,pass.getPassword());
+    }
+
+
+    public void testZoom() {
+        HSSFWorkbook wb = new HSSFWorkbook();
+        HSSFSheet sheet = wb.createSheet();
+        assertEquals(-1, sheet.getSheet().findFirstRecordLocBySid(SCLRecord.sid));
+        sheet.setZoom(3,4);
+        assertTrue(sheet.getSheet().findFirstRecordLocBySid(SCLRecord.sid) > 0);
+        SCLRecord sclRecord = (SCLRecord) sheet.getSheet().findFirstRecordBySid(SCLRecord.sid);
+        assertEquals(3, sclRecord.getNumerator());
+        assertEquals(4, sclRecord.getDenominator());
+
+        int sclLoc = sheet.getSheet().findFirstRecordLocBySid(SCLRecord.sid);
+        int window2Loc = sheet.getSheet().findFirstRecordLocBySid(WindowTwoRecord.sid);
+        assertTrue(sclLoc == window2Loc + 1);
+    }
+
+
+    /**
+     * When removing one merged region, it would break
+     *
+     */
+    public void testRemoveMerged() {
+        HSSFWorkbook wb = new HSSFWorkbook();
+        HSSFSheet sheet = wb.createSheet();
+        CellRangeAddress region = new CellRangeAddress(0, 1, 0, 1);
+        sheet.addMergedRegion(region);
+        region = new CellRangeAddress(1, 2, 0, 1);
+        sheet.addMergedRegion(region);
+
+        sheet.removeMergedRegion(0);
+
+        region = sheet.getMergedRegion(0);
+        assertEquals("Left over region should be starting at row 1", 1, region.getFirstRow());
+
+        sheet.removeMergedRegion(0);
+
+        assertEquals("there should be no merged regions left!", 0, sheet.getNumMergedRegions());
+
+        //an, add, remove, get(0) would null pointer
+        sheet.addMergedRegion(region);
+        assertEquals("there should now be one merged region!", 1, sheet.getNumMergedRegions());
+        sheet.removeMergedRegion(0);
+        assertEquals("there should now be zero merged regions!", 0, sheet.getNumMergedRegions());
+        //add it again!
+        region.setLastRow(4);
+
+        sheet.addMergedRegion(region);
+        assertEquals("there should now be one merged region!", 1, sheet.getNumMergedRegions());
+
+        //should exist now!
+        assertTrue("there isn't more than one merged region in there", 1 <= sheet.getNumMergedRegions());
+        region = sheet.getMergedRegion(0);
+        assertEquals("the merged row to doesnt match the one we put in ", 4, region.getLastRow());
+    }
+
+    public void testShiftMerged() {
+        HSSFWorkbook wb = new HSSFWorkbook();
+        HSSFSheet sheet = wb.createSheet();
+        HSSFRow row = sheet.createRow(0);
+        HSSFCell cell = row.createCell(0);
+        cell.setCellValue(new HSSFRichTextString("first row, first cell"));
+
+        row = sheet.createRow(1);
+        cell = row.createCell(1);
+        cell.setCellValue(new HSSFRichTextString("second row, second cell"));
+
+        CellRangeAddress region = new CellRangeAddress(1, 1, 0, 1);
+        sheet.addMergedRegion(region);
+
+        sheet.shiftRows(1, 1, 1);
+
+        region = sheet.getMergedRegion(0);
+        assertEquals("Merged region not moved over to row 2", 2, region.getFirstRow());
+    }
+
+    /**
+     * Tests the display of gridlines, formulas, and rowcolheadings.
+     * @author Shawn Laubach (slaubach at apache dot org)
+     */
+    public void testDisplayOptions() {
+        HSSFWorkbook wb = new HSSFWorkbook();
+        HSSFSheet sheet = wb.createSheet();
+
+        wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
+        sheet = wb.getSheetAt(0);
+
+        assertEquals(sheet.isDisplayGridlines(), true);
+        assertEquals(sheet.isDisplayRowColHeadings(), true);
+        assertEquals(sheet.isDisplayFormulas(), false);
+
+        sheet.setDisplayGridlines(false);
+        sheet.setDisplayRowColHeadings(false);
+        sheet.setDisplayFormulas(true);
+
+        wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
+        sheet = wb.getSheetAt(0);
+
+        assertEquals(sheet.isDisplayGridlines(), false);
+        assertEquals(sheet.isDisplayRowColHeadings(), false);
+        assertEquals(sheet.isDisplayFormulas(), true);
+    }
+
+
+    /**
+     * Make sure the excel file loads work
+     *
+     */
+    public void testPageBreakFiles() {
+        HSSFWorkbook wb = openSample("SimpleWithPageBreaks.xls");
+
+        HSSFSheet sheet = wb.getSheetAt(0);
+        assertNotNull(sheet);
+
+        assertEquals("1 row page break", 1, sheet.getRowBreaks().length);
+        assertEquals("1 column page break", 1, sheet.getColumnBreaks().length);
+
+        assertTrue("No row page break", sheet.isRowBroken(22));
+        assertTrue("No column page break", sheet.isColumnBroken((short)4));
+
+        sheet.setRowBreak(10);
+        sheet.setColumnBreak((short)13);
+
+        assertEquals("row breaks number", 2, sheet.getRowBreaks().length);
+        assertEquals("column breaks number", 2, sheet.getColumnBreaks().length);
+
+        wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
+        sheet = wb.getSheetAt(0);
+
+        assertTrue("No row page break", sheet.isRowBroken(22));
+        assertTrue("No column page break", sheet.isColumnBroken((short)4));
+
+        assertEquals("row breaks number", 2, sheet.getRowBreaks().length);
+        assertEquals("column breaks number", 2, sheet.getColumnBreaks().length);
+    }
+
+    public void testDBCSName () {
+        HSSFWorkbook wb = openSample("DBCSSheetName.xls");
+        wb.getSheetAt(1);
+        assertEquals ("DBCS Sheet Name 2", wb.getSheetName(1),"\u090f\u0915" );
+        assertEquals("DBCS Sheet Name 1", wb.getSheetName(0),"\u091c\u093e");
+    }
+
+    /**
+     * Testing newly added method that exposes the WINDOW2.toprow
+     * parameter to allow setting the toprow in the visible view
+     * of the sheet when it is first opened.
+     */
+    public void testTopRow() {
+        HSSFWorkbook wb = openSample("SimpleWithPageBreaks.xls");
+
+        HSSFSheet sheet = wb.getSheetAt(0);
+        assertNotNull(sheet);
+
+        short toprow = (short) 100;
+        short leftcol = (short) 50;
+        sheet.showInPane(toprow,leftcol);
+        assertEquals("HSSFSheet.getTopRow()", toprow, sheet.getTopRow());
+        assertEquals("HSSFSheet.getLeftCol()", leftcol, sheet.getLeftCol());
+    }
+
+    /** cell with formula becomes null on cloning a sheet*/
+     public void test35084() {
+
+        HSSFWorkbook wb = new HSSFWorkbook();
+        HSSFSheet s = wb.createSheet("Sheet1");
+        HSSFRow r = s.createRow(0);
+        r.createCell(0).setCellValue(1);
+        r.createCell(1).setCellFormula("A1*2");
+        HSSFSheet s1 = wb.cloneSheet(0);
+        r = s1.getRow(0);
+        assertEquals("double", r.getCell(0).getNumericCellValue(), 1, 0); // sanity check
+        assertNotNull(r.getCell(1));
+        assertEquals("formula", r.getCell(1).getCellFormula(), "A1*2");
+    }
+
+    /** test that new default column styles get applied */
+    public void testDefaultColumnStyle() {
+        HSSFWorkbook wb = new HSSFWorkbook();
+        HSSFCellStyle style = wb.createCellStyle();
+        HSSFSheet s = wb.createSheet();
+        s.setDefaultColumnStyle((short) 0, style);
+        HSSFRow r = s.createRow(0);
+        HSSFCell c = r.createCell(0);
+        assertEquals("style should match", style.getIndex(), c.getCellStyle().getIndex());
+    }
+
+
+    /**
+     *
+     */
+    public void testAddEmptyRow() {
+        //try to add 5 empty rows to a new sheet
+        HSSFWorkbook workbook = new HSSFWorkbook();
+        HSSFSheet sheet = workbook.createSheet();
+        for (int i = 0; i < 5; i++) {
+            sheet.createRow(i);
+        }
+
+        workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
+
+        //try adding empty rows in an existing worksheet
+        workbook = openSample("Simple.xls");
+
+        sheet = workbook.getSheetAt(0);
+        for (int i = 3; i < 10; i++) sheet.createRow(i);
+
+        workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
+    }
+
+    public void testAutoSizeColumn() {
+        HSSFWorkbook wb = openSample("43902.xls");
+        String sheetName = "my sheet";
+        HSSFSheet sheet = wb.getSheet(sheetName);
+
+        // Can't use literal numbers for column sizes, as
+        //  will come out with different values on different
+        //  machines based on the fonts available.
+        // So, we use ranges, which are pretty large, but
+        //  thankfully don't overlap!
+        int minWithRow1And2 = 6400;
+        int maxWithRow1And2 = 7800;
+        int minWithRow1Only = 2750;
+        int maxWithRow1Only = 3300;
+
+        // autoSize the first column and check its size before the merged region (1,0,1,1) is set:
+        // it has to be based on the 2nd row width
+        sheet.autoSizeColumn((short)0);
+        assertTrue("Column autosized with only one row: wrong width", sheet.getColumnWidth((short)0) >= minWithRow1And2);
+        assertTrue("Column autosized with only one row: wrong width", sheet.getColumnWidth((short)0) <= maxWithRow1And2);
+
+        //create a region over the 2nd row and auto size the first column
+        sheet.addMergedRegion(new CellRangeAddress(1,1,0,1));
+        sheet.autoSizeColumn((short)0);
+        HSSFWorkbook wb2 = HSSFTestDataSamples.writeOutAndReadBack(wb);
+
+        // check that the autoSized column width has ignored the 2nd row
+        // because it is included in a merged region (Excel like behavior)
+        HSSFSheet sheet2 = wb2.getSheet(sheetName);
+        assertTrue(sheet2.getColumnWidth((short)0) >= minWithRow1Only);
+        assertTrue(sheet2.getColumnWidth((short)0) <= maxWithRow1Only);
+
+        // remove the 2nd row merged region and check that the 2nd row value is used to the autoSizeColumn width
+        sheet2.removeMergedRegion(1);
+        sheet2.autoSizeColumn((short)0);
+        HSSFWorkbook wb3 = HSSFTestDataSamples.writeOutAndReadBack(wb2);
+        HSSFSheet sheet3 = wb3.getSheet(sheetName);
+        assertTrue(sheet3.getColumnWidth((short)0) >= minWithRow1And2);
+        assertTrue(sheet3.getColumnWidth((short)0) <= maxWithRow1And2);
+    }
+
+    /**
+     * Setting ForceFormulaRecalculation on sheets
+     */
+    public void testForceRecalculation() throws Exception {
+        HSSFWorkbook workbook = openSample("UncalcedRecord.xls");
+
+        HSSFSheet sheet = workbook.getSheetAt(0);
+        HSSFSheet sheet2 = workbook.getSheetAt(0);
+        HSSFRow row = sheet.getRow(0);
+        row.createCell(0).setCellValue(5);
+        row.createCell(1).setCellValue(8);
+        assertFalse(sheet.getForceFormulaRecalculation());
+        assertFalse(sheet2.getForceFormulaRecalculation());
+
+        // Save and manually verify that on column C we have 0, value in template
+        File tempFile = new File(System.getProperty("java.io.tmpdir")+"/uncalced_err.xls" );
+        tempFile.delete();
+        FileOutputStream fout = new FileOutputStream( tempFile );
+        workbook.write( fout );
+        fout.close();
+        sheet.setForceFormulaRecalculation(true);
+        assertTrue(sheet.getForceFormulaRecalculation());
+
+        // Save and manually verify that on column C we have now 13, calculated value
+        tempFile = new File(System.getProperty("java.io.tmpdir")+"/uncalced_succ.xls" );
+        tempFile.delete();
+        fout = new FileOutputStream( tempFile );
+        workbook.write( fout );
+        fout.close();
+
+        // Try it can be opened
+        HSSFWorkbook wb2 = new HSSFWorkbook(new FileInputStream(tempFile));
+
+        // And check correct sheet settings found
+        sheet = wb2.getSheetAt(0);
+        sheet2 = wb2.getSheetAt(1);
+        assertTrue(sheet.getForceFormulaRecalculation());
+        assertFalse(sheet2.getForceFormulaRecalculation());
+
+        // Now turn if back off again
+        sheet.setForceFormulaRecalculation(false);
+
+        fout = new FileOutputStream( tempFile );
+        wb2.write( fout );
+        fout.close();
+        wb2 = new HSSFWorkbook(new FileInputStream(tempFile));
+
+        assertFalse(wb2.getSheetAt(0).getForceFormulaRecalculation());
+        assertFalse(wb2.getSheetAt(1).getForceFormulaRecalculation());
+        assertFalse(wb2.getSheetAt(2).getForceFormulaRecalculation());
+
+        // Now add a new sheet, and check things work
+        //  with old ones unset, new one set
+        HSSFSheet s4 = wb2.createSheet();
+        s4.setForceFormulaRecalculation(true);
+
+        assertFalse(sheet.getForceFormulaRecalculation());
+        assertFalse(sheet2.getForceFormulaRecalculation());
+        assertTrue(s4.getForceFormulaRecalculation());
+
+        fout = new FileOutputStream( tempFile );
+        wb2.write( fout );
+        fout.close();
+
+        HSSFWorkbook wb3 = new HSSFWorkbook(new FileInputStream(tempFile));
+        assertFalse(wb3.getSheetAt(0).getForceFormulaRecalculation());
+        assertFalse(wb3.getSheetAt(1).getForceFormulaRecalculation());
+        assertFalse(wb3.getSheetAt(2).getForceFormulaRecalculation());
+        assertTrue(wb3.getSheetAt(3).getForceFormulaRecalculation());
+    }
+
+    public void testColumnWidth() {
+        //check we can correctly read column widths from a reference workbook
+        HSSFWorkbook wb = openSample("colwidth.xls");
+
+        //reference values
+        int[] ref = {365, 548, 731, 914, 1097, 1280, 1462, 1645, 1828, 2011, 2194, 2377, 2560, 2742, 2925, 3108, 3291, 3474, 3657};
+
+        HSSFSheet sh = wb.getSheetAt(0);
+        for (char i = 'A'; i <= 'S'; i++) {
+            int idx = i - 'A';
+            int w = sh.getColumnWidth((short)idx);
+            assertEquals(ref[idx], w);
+        }
+
+        //the second sheet doesn't have overridden column widths
+        sh = wb.getSheetAt(1);
+        int def_width = sh.getDefaultColumnWidth();
+        for (char i = 'A'; i <= 'S'; i++) {
+            int idx = i - 'A';
+            int w = sh.getColumnWidth((short)idx);
+            //getDefaultColumnWidth returns width measued in characters
+            //getColumnWidth returns width measued in 1/256th units
+            assertEquals(def_width*256, w);
+        }
+
+        //test new workbook
+        wb = new HSSFWorkbook();
+        sh = wb.createSheet();
+        sh.setDefaultColumnWidth((short)10);
+        assertEquals(10, sh.getDefaultColumnWidth());
+        assertEquals(256*10, sh.getColumnWidth((short)0));
+        assertEquals(256*10, sh.getColumnWidth((short)1));
+        assertEquals(256*10, sh.getColumnWidth((short)2));
+        for (char i = 'D'; i <= 'F'; i++) {
+            short w = (short)(256*12);
+            sh.setColumnWidth((short)i, w);
+            assertEquals(w, sh.getColumnWidth((short)i));
+        }
+
+        //serialize and read again
+        wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
+
+        sh = wb.getSheetAt(0);
+        assertEquals(10, sh.getDefaultColumnWidth());
+        //columns A-C have default width
+        assertEquals(256*10, sh.getColumnWidth((short)0));
+        assertEquals(256*10, sh.getColumnWidth((short)1));
+        assertEquals(256*10, sh.getColumnWidth((short)2));
+        //columns D-F have custom width
+        for (char i = 'D'; i <= 'F'; i++) {
+            short w = (short)(256*12);
+            assertEquals(w, sh.getColumnWidth((short)i));
+        }
+    }
+
+    /**
+     * Some utilities write Excel files without the ROW records.
+     * Excel, ooo, and google docs are OK with this.
+     * Now POI is too.
+     */
+    public void testMissingRowRecords_bug41187() {
+        HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("ex41187-19267.xls");
+
+        HSSFSheet sheet = wb.getSheetAt(0);
+        HSSFRow row = sheet.getRow(0);
+        if(row == null) {
+            throw new AssertionFailedError("Identified bug 41187 a");
+        }
+        if (row.getHeight() == 0) {
+            throw new AssertionFailedError("Identified bug 41187 b");
+        }
+        assertEquals("Hi Excel!", row.getCell(0).getRichStringCellValue().getString());
+        // check row height for 'default' flag
+        assertEquals((short)0xFF, row.getHeight());
+
+        HSSFTestDataSamples.writeOutAndReadBack(wb);
+    }
+
+    /**
+     * If we clone a sheet containing drawings,
+     * we must allocate a new ID of the drawing group and re-create shape IDs
+     *
+     * See bug #45720.
+     */
+    public void testCloneSheetWithDrawings() {
+        HSSFWorkbook wb1 = HSSFTestDataSamples.openSampleWorkbook("45720.xls");
+
+        HSSFSheet sheet1 = wb1.getSheetAt(0);
+
+        wb1.getWorkbook().findDrawingGroup();
+        DrawingManager2 dm1 = wb1.getWorkbook().getDrawingManager();
+
+        wb1.cloneSheet(0);
+
+        HSSFWorkbook wb2 = HSSFTestDataSamples.writeOutAndReadBack(wb1);
+        wb2.getWorkbook().findDrawingGroup();
+        DrawingManager2 dm2 = wb2.getWorkbook().getDrawingManager();
+
+        //check EscherDggRecord - a workbook-level registry of drawing objects
+        assertEquals(dm1.getDgg().getMaxDrawingGroupId() + 1, dm2.getDgg().getMaxDrawingGroupId());
+
+        HSSFSheet sheet2 = wb2.getSheetAt(1);
+
+        //check that id of the drawing group was updated
+        EscherDgRecord dg1 = (EscherDgRecord)sheet1.getDrawingEscherAggregate().findFirstWithId(EscherDgRecord.RECORD_ID);
+        EscherDgRecord dg2 = (EscherDgRecord)sheet2.getDrawingEscherAggregate().findFirstWithId(EscherDgRecord.RECORD_ID);
+        int dg_id_1 = dg1.getOptions() >> 4;
+        int dg_id_2 = dg2.getOptions() >> 4;
+        assertEquals(dg_id_1 + 1, dg_id_2);
+
+        //TODO: check shapeId in the cloned sheet
+    }
 }
index 658a3f48840e66c29e2b1f587734fdf0f62c1e45..1717aeff655565e81133eb4158273cba25033723 100644 (file)
@@ -429,9 +429,9 @@ public final class TestHSSFWorkbook extends TestCase {
         assertEquals("On2", nr.getNameText());
         assertEquals(0, nr.getSheetNumber());
         assertEquals(1, nr.getExternSheetNumber());
-        assertEquals(1, nr.getNameDefinition().size());
+        assertEquals(1, nr.getNameDefinition().length);
         
-        ptg = (Area3DPtg)nr.getNameDefinition().get(0);
+        ptg = (Area3DPtg)nr.getNameDefinition()[0];
         assertEquals(1, ptg.getExternSheetIndex());
         assertEquals(0, ptg.getFirstColumn());
         assertEquals(0, ptg.getFirstRow());
@@ -452,9 +452,9 @@ public final class TestHSSFWorkbook extends TestCase {
         assertEquals("OnOne", nr.getNameText());
         assertEquals(0, nr.getSheetNumber());
         assertEquals(0, nr.getExternSheetNumber());
-        assertEquals(1, nr.getNameDefinition().size());
+        assertEquals(1, nr.getNameDefinition().length);
         
-        ptg = (Area3DPtg)nr.getNameDefinition().get(0);
+        ptg = (Area3DPtg)nr.getNameDefinition()[0];
         assertEquals(0, ptg.getExternSheetIndex());
         assertEquals(0, ptg.getFirstColumn());
         assertEquals(2, ptg.getFirstRow());
@@ -475,9 +475,9 @@ public final class TestHSSFWorkbook extends TestCase {
         assertEquals("OnSheet3", nr.getNameText());
         assertEquals(0, nr.getSheetNumber());
         assertEquals(2, nr.getExternSheetNumber());
-        assertEquals(1, nr.getNameDefinition().size());
+        assertEquals(1, nr.getNameDefinition().length);
         
-        ptg = (Area3DPtg)nr.getNameDefinition().get(0);
+        ptg = (Area3DPtg)nr.getNameDefinition()[0];
         assertEquals(2, ptg.getExternSheetIndex());
         assertEquals(0, ptg.getFirstColumn());
         assertEquals(0, ptg.getFirstRow());
index a5dbb096d411b897ef92212a656ff382f64a715e..c9356b85271dcd197de81f1dd14313b68474bcc9 100644 (file)
@@ -19,7 +19,6 @@ package org.apache.poi.hssf.util;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.List;
 
 import junit.framework.TestCase;
 
@@ -28,6 +27,7 @@ import org.apache.poi.hssf.model.Workbook;
 import org.apache.poi.hssf.record.NameRecord;
 import org.apache.poi.hssf.record.formula.Area3DPtg;
 import org.apache.poi.hssf.record.formula.MemFuncPtg;
+import org.apache.poi.hssf.record.formula.Ptg;
 import org.apache.poi.hssf.record.formula.UnionPtg;
 import org.apache.poi.hssf.usermodel.HSSFCell;
 import org.apache.poi.hssf.usermodel.HSSFName;
@@ -85,7 +85,7 @@ public final class TestAreaReference extends TestCase {
     public void testReferenceWithSheet() {
         AreaReference ar;
         
-        ar = new AreaReference("Tabelle1!B5");
+        ar = new AreaReference("Tabelle1!B5:B5");
         assertTrue(ar.isSingleCell());
         TestCellReference.confirmCell(ar.getFirstCell(), "Tabelle1", 4, 1, false, false, "Tabelle1!B5");
         
@@ -115,11 +115,11 @@ public final class TestAreaReference extends TestCase {
         }
     }
 
-    public void testContiguousReferences() throws Exception {
-        String refSimple = "$C$10";
+    public void testContiguousReferences() {
+        String refSimple = "$C$10:$C$10";
         String ref2D = "$C$10:$D$11";
-        String refDCSimple = "$C$10,$D$12,$E$14";
-        String refDC2D = "$C$10:$C$11,$D$12,$E$14:$E$20";
+        String refDCSimple = "$C$10:$C$10,$D$12:$D$12,$E$14:$E$14";
+        String refDC2D = "$C$10:$C$11,$D$12:$D$12,$E$14:$E$20";
         String refDC3D = "Tabelle1!$C$10:$C$14,Tabelle1!$D$10:$D$12";
 
         // Check that we detect as contiguous properly
@@ -206,13 +206,13 @@ public final class TestAreaReference extends TestCase {
         assertNotNull(nr);
         assertEquals("test", nr.getNameText());
 
-        List def =nr.getNameDefinition();
-        assertEquals(4, def.size());
+        Ptg[] def =nr.getNameDefinition();
+        assertEquals(4, def.length);
 
-        MemFuncPtg ptgA = (MemFuncPtg)def.get(0);
-        Area3DPtg ptgB = (Area3DPtg)def.get(1);
-        Area3DPtg ptgC = (Area3DPtg)def.get(2);
-        UnionPtg ptgD = (UnionPtg)def.get(3);
+        MemFuncPtg ptgA = (MemFuncPtg)def[0];
+        Area3DPtg ptgB = (Area3DPtg)def[1];
+        Area3DPtg ptgC = (Area3DPtg)def[2];
+        UnionPtg ptgD = (UnionPtg)def[3];
         assertEquals("", ptgA.toFormulaString(wb));
         assertEquals(refA, ptgB.toFormulaString(wb));
         assertEquals(refB, ptgC.toFormulaString(wb));
@@ -245,16 +245,16 @@ public final class TestAreaReference extends TestCase {
     private static void confirmResolveCellRef(HSSFWorkbook wb, CellReference cref) {
         HSSFSheet s = wb.getSheet(cref.getSheetName());
         HSSFRow r = s.getRow(cref.getRow());
-        HSSFCell c = r.getCell(cref.getCol());
+        HSSFCell c = r.getCell((int)cref.getCol());
         assertNotNull(c);
     }
     
     public void testSpecialSheetNames() {
         AreaReference ar;
-        ar = new AreaReference("'Sheet A'!A1");
+        ar = new AreaReference("'Sheet A'!A1:A1");
         confirmAreaSheetName(ar, "Sheet A", "'Sheet A'!A1");
         
-        ar = new AreaReference("'Hey! Look Here!'!A1");
+        ar = new AreaReference("'Hey! Look Here!'!A1:A1");
         confirmAreaSheetName(ar, "Hey! Look Here!", "'Hey! Look Here!'!A1");
         
         ar = new AreaReference("'O''Toole'!A1:B2");
@@ -270,7 +270,24 @@ public final class TestAreaReference extends TestCase {
         assertEquals(expectedFullText, ar.formatAsString());
     }
     
-    public static void main(String[] args) {
-        junit.textui.TestRunner.run(TestAreaReference.class);
-    }
+    public void testWholeColumnRefs() {
+               confirmWholeColumnRef("A:A", 0, 0, false, false);
+               confirmWholeColumnRef("$C:D", 2, 3, true, false);
+               confirmWholeColumnRef("AD:$AE", 29, 30, false, true);
+               
+       }
+
+       private static void confirmWholeColumnRef(String ref, int firstCol, int lastCol, boolean firstIsAbs, boolean lastIsAbs) {
+               AreaReference ar = new AreaReference(ref);
+               confirmCell(ar.getFirstCell(), 0, firstCol, true, firstIsAbs);
+               confirmCell(ar.getLastCell(), 0xFFFF, lastCol, true, lastIsAbs);
+       }
+
+       private static void confirmCell(CellReference cell, int row, int col, boolean isRowAbs,
+                       boolean isColAbs) {
+               assertEquals(row, cell.getRow());
+               assertEquals(col, cell.getCol());
+               assertEquals(isRowAbs, cell.isRowAbsolute());
+               assertEquals(isColAbs, cell.isColAbsolute());
+       }
 }