]> source.dussan.org Git - poi.git/commitdiff
Merged revisions 699178,699487,699489,699761 via svnmerge from
authorJosh Micich <josh@apache.org>
Mon, 29 Sep 2008 20:09:09 +0000 (20:09 +0000)
committerJosh Micich <josh@apache.org>
Mon, 29 Sep 2008 20:09:09 +0000 (20:09 +0000)
https://svn.apache.org/repos/asf/poi/trunk

........
  r699178 | josh | 2008-09-25 21:49:20 -0700 (Thu, 25 Sep 2008) | 1 line

  Changed HSSFEvaluationWorkbook to avoid re-parsing cell formulas during execution. (working towards fix for bug 45865)
........
  r699487 | josh | 2008-09-26 13:25:45 -0700 (Fri, 26 Sep 2008) | 1 line

  Fix formula parser to properly support the range operator. Small fixes to parsing of sheet names and full column references.
........
  r699489 | josh | 2008-09-26 13:32:06 -0700 (Fri, 26 Sep 2008) | 1 line

  Code cleanup in junit
........
  r699761 | josh | 2008-09-27 19:04:31 -0700 (Sat, 27 Sep 2008) | 1 line

  Bug 45865 - modified Formula Parser/Evaluator to handle cross-worksheet formulas
........

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

44 files changed:
src/documentation/content/xdocs/changes.xml
src/documentation/content/xdocs/status.xml
src/java/org/apache/poi/hssf/model/LinkTable.java
src/java/org/apache/poi/hssf/model/Workbook.java
src/java/org/apache/poi/hssf/record/ExternSheetRecord.java
src/java/org/apache/poi/hssf/record/SupBookRecord.java
src/java/org/apache/poi/hssf/record/formula/Area3DPtg.java
src/java/org/apache/poi/hssf/record/formula/AreaI.java
src/java/org/apache/poi/hssf/record/formula/AreaPtgBase.java
src/java/org/apache/poi/hssf/record/formula/AttrPtg.java
src/java/org/apache/poi/hssf/record/formula/ExternSheetNameResolver.java
src/java/org/apache/poi/hssf/record/formula/Ref3DPtg.java
src/java/org/apache/poi/hssf/record/formula/SheetNameFormatter.java
src/java/org/apache/poi/hssf/record/formula/eval/RangeEval.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java
src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java
src/java/org/apache/poi/hssf/util/AreaReference.java
src/java/org/apache/poi/hssf/util/CellReference.java
src/java/org/apache/poi/ss/formula/CellLocation.java
src/java/org/apache/poi/ss/formula/CollaboratingWorkbooksEnvironment.java [new file with mode: 0644]
src/java/org/apache/poi/ss/formula/EvaluationCache.java
src/java/org/apache/poi/ss/formula/EvaluationWorkbook.java
src/java/org/apache/poi/ss/formula/ExternSheetReferenceToken.java [new file with mode: 0644]
src/java/org/apache/poi/ss/formula/FormulaParser.java
src/java/org/apache/poi/ss/formula/FormulaParsingWorkbook.java
src/java/org/apache/poi/ss/formula/FormulaRenderingWorkbook.java
src/java/org/apache/poi/ss/formula/OperandClassTransformer.java
src/java/org/apache/poi/ss/formula/OperationEvaluatorFactory.java
src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java
src/java/org/apache/poi/ss/util/AreaReference.java
src/java/org/apache/poi/ss/util/CellReference.java
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationWorkbook.java
src/testcases/org/apache/poi/hssf/HSSFTests.java
src/testcases/org/apache/poi/hssf/data/multibookFormulaA.xls [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/data/multibookFormulaB.xls [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java
src/testcases/org/apache/poi/hssf/record/TestSupBookRecord.java
src/testcases/org/apache/poi/hssf/record/formula/TestSheetNameFormatter.java
src/testcases/org/apache/poi/hssf/record/formula/eval/AllFormulaEvalTests.java
src/testcases/org/apache/poi/hssf/record/formula/eval/TestFormulaBugs.java
src/testcases/org/apache/poi/hssf/record/formula/eval/TestRangeEval.java [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/usermodel/TestFormulas.java
src/testcases/org/apache/poi/ss/formula/AllSSFormulaTests.java [new file with mode: 0644]
src/testcases/org/apache/poi/ss/formula/TestWorkbookEvaluator.java [new file with mode: 0644]

index 2d8a6b2c58fc3f9e4a051497f29d6d43d4ea713e..7dfc8868841cd0a0649f7677c7b75db87dc5c6f0 100644 (file)
@@ -67,6 +67,7 @@
            <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
         </release>
         <release version="3.2-alpha1" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="add">45865 modified Formula Parser/Evaluator to handle cross-worksheet formulas</action>
            <action dev="POI-DEVELOPERS" type="add">Optimised the FormulaEvaluator to take cell dependencies into account</action>
            <action dev="POI-DEVELOPERS" type="add">16936 - Initial support for whole-row cell styling</action>
            <action dev="POI-DEVELOPERS" type="add">Update hssf.extractor.ExcelExtractor to optionally output blank cells too</action>
index b25423c6f193ed9c858912d108f96cafc1c4a044..09014b72c5f4d398f90e9d329005d75a1f858266 100644 (file)
@@ -64,6 +64,7 @@
            <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
         </release>
         <release version="3.2-alpha1" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="add">45865 modified Formula Parser/Evaluator to handle cross-worksheet formulas</action>
            <action dev="POI-DEVELOPERS" type="add">Optimised the FormulaEvaluator to take cell dependencies into account</action>
            <action dev="POI-DEVELOPERS" type="add">16936 - Initial support for whole-row cell styling</action>
            <action dev="POI-DEVELOPERS" type="add">Update hssf.extractor.ExcelExtractor to optionally output blank cells too</action>
index 2caab0fb561543e1653df91f4a74aef7f3e2c7cd..998712e5e8875b43bf6ce7aac07279a410b4374d 100755 (executable)
@@ -29,6 +29,7 @@ import org.apache.poi.hssf.record.ExternalNameRecord;
 import org.apache.poi.hssf.record.NameRecord;
 import org.apache.poi.hssf.record.Record;
 import org.apache.poi.hssf.record.SupBookRecord;
+import org.apache.poi.hssf.record.UnicodeString;
 import org.apache.poi.hssf.record.formula.NameXPtg;
 
 /**
@@ -109,8 +110,8 @@ final class LinkTable {
                        temp.toArray(_crnBlocks);
                }
 
-               public ExternalBookBlock(short numberOfSheets) {
-                       _externalBookRecord = SupBookRecord.createInternalReferences(numberOfSheets);
+               public ExternalBookBlock(int numberOfSheets) {
+                       _externalBookRecord = SupBookRecord.createInternalReferences((short)numberOfSheets);
                        _externalNameRecords = new ExternalNameRecord[0];
                        _crnBlocks = new CRNBlock[0];
                }
@@ -197,7 +198,7 @@ final class LinkTable {
                return ExternSheetRecord.combine(esrs);
        }
 
-       public LinkTable(short numberOfSheets, WorkbookRecordList workbookRecordList) {
+       public LinkTable(int numberOfSheets, WorkbookRecordList workbookRecordList) {
                _workbookRecordList = workbookRecordList;
                _definedNames = new ArrayList();
                _externalBookBlocks = new ExternalBookBlock[] {
@@ -303,8 +304,62 @@ final class LinkTable {
                return lastName.getSheetNumber() == firstName.getSheetNumber();
        }
 
-       
-       public int getIndexToSheet(int extRefIndex) {
+       public String[] getExternalBookAndSheetName(int extRefIndex) {
+               int ebIx = _externSheetRecord.getExtbookIndexFromRefIndex(extRefIndex);
+               SupBookRecord ebr = _externalBookBlocks[ebIx].getExternalBookRecord();
+               if (!ebr.isExternalReferences()) {
+                       return null;
+               }
+               int shIx = _externSheetRecord.getFirstSheetIndexFromRefIndex(extRefIndex);
+               UnicodeString usSheetName = ebr.getSheetNames()[shIx];
+               return new String[] {
+                               ebr.getURL(),
+                               usSheetName.getString(),
+               };
+       }
+
+       public int getExternalSheetIndex(String workbookName, String sheetName) {
+               SupBookRecord ebrTarget = null;
+               int externalBookIndex = -1;
+               for (int i=0; i<_externalBookBlocks.length; i++) {
+                       SupBookRecord ebr = _externalBookBlocks[i].getExternalBookRecord();
+                       if (!ebr.isExternalReferences()) {
+                               continue;
+                       }
+                       if (workbookName.equals(ebr.getURL())) { // not sure if 'equals()' works when url has a directory
+                               ebrTarget = ebr;
+                               externalBookIndex = i;
+                               break;
+                       }
+               }
+               if (ebrTarget == null) {
+                       throw new RuntimeException("No external workbook with name '" + workbookName + "'");
+               }
+               int sheetIndex = getSheetIndex(ebrTarget.getSheetNames(), sheetName);
+               
+               int result = _externSheetRecord.getRefIxForSheet(externalBookIndex, sheetIndex);
+               if (result < 0) {
+                       throw new RuntimeException("ExternSheetRecord does not contain combination (" 
+                                       + externalBookIndex + ", " + sheetIndex + ")");
+               }
+               return result;
+       }
+
+       private static int getSheetIndex(UnicodeString[] sheetNames, String sheetName) {
+               for (int i = 0; i < sheetNames.length; i++) {
+                       if (sheetNames[i].getString().equals(sheetName)) {
+                               return i;
+                       }
+                       
+               }
+               throw new RuntimeException("External workbook does not contain sheet '" + sheetName + "'");
+       }
+
+       /**
+        * @param extRefIndex as from a {@link Ref3DPtg} or {@link Area3DPtg}
+        * @return -1 if the reference is to an external book
+        */
+       public int getIndexToInternalSheet(int extRefIndex) {
                return _externSheetRecord.getFirstSheetIndexFromRefIndex(extRefIndex);
        }
 
@@ -315,20 +370,26 @@ final class LinkTable {
                return _externSheetRecord.getFirstSheetIndexFromRefIndex(extRefIndex);
        }
 
-       public int addSheetIndexToExternSheet(int sheetNumber) {
-               // TODO - what about the first parameter (extBookIndex)?
-               return _externSheetRecord.addRef(0, sheetNumber, sheetNumber);
-       }
-
-       public short checkExternSheet(int sheetIndex) {
+       public int checkExternSheet(int sheetIndex) {
+               int thisWbIndex = -1; // this is probably always zero
+               for (int i=0; i<_externalBookBlocks.length; i++) {
+                       SupBookRecord ebr = _externalBookBlocks[i].getExternalBookRecord();
+                       if (ebr.isInternalReferences()) {
+                               thisWbIndex = i;
+                               break;
+                       }
+               }
+               if (thisWbIndex < 0) {
+                       throw new RuntimeException("Could not find 'internal references' EXTERNALBOOK");
+               }
 
                //Trying to find reference to this sheet
-               int i = _externSheetRecord.getRefIxForSheet(sheetIndex);
+               int i = _externSheetRecord.getRefIxForSheet(thisWbIndex, sheetIndex);
                if (i>=0) {
-                       return (short)i;
+                       return i;
                }
-               //We Haven't found reference to this sheet
-               return (short)addSheetIndexToExternSheet((short) sheetIndex);
+               //We haven't found reference to this sheet
+               return _externSheetRecord.addRef(thisWbIndex, sheetIndex, sheetIndex);
        }
 
 
index dbf7ecf7e148e35e213279ff409dc2fc74e902ba..0728d0e29ffdeb37d72cedcac65e7fa15ff494f3 100644 (file)
@@ -26,6 +26,7 @@ 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.ss.formula.EvaluationWorkbook.ExternalSheet;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
 
@@ -328,9 +329,9 @@ public final class Workbook implements Model {
         for ( int k = 0; k < nBoundSheets; k++ ) {   
             BoundSheetRecord bsr = retval.createBoundSheet(k);
 
-                       records.add(bsr);
-                       retval.boundsheets.add(bsr);
-                       retval.records.setBspos(records.size() - 1);
+            records.add(bsr);
+            retval.boundsheets.add(bsr);
+            retval.records.setBspos(records.size() - 1);
         }
         // retval.records.supbookpos = retval.records.bspos + 1;
         //        retval.records.namepos = retval.records.supbookpos + 2;
@@ -586,19 +587,19 @@ public final class Workbook implements Model {
      * @param hidden 0 for not hidden, 1 for hidden, 2 for very hidden
      */
     public void setSheetHidden(int sheetnum, int hidden) {
-       BoundSheetRecord bsr = getBoundSheetRec(sheetnum);
-       boolean h = false;
-       boolean vh = false;
-       if(hidden == 0) {
-       } else if(hidden == 1) {
-               h = true;
-       } else if(hidden == 2) {
-               vh = true;
-       } else {
-               throw new IllegalArgumentException("Invalid hidden flag " + hidden + " given, must be 0, 1 or 2");
-       }
-       bsr.setHidden(h);
-       bsr.setVeryHidden(vh);
+        BoundSheetRecord bsr = getBoundSheetRec(sheetnum);
+        boolean h = false;
+        boolean vh = false;
+        if(hidden == 0) {
+        } else if(hidden == 1) {
+            h = true;
+        } else if(hidden == 2) {
+            vh = true;
+        } else {
+            throw new IllegalArgumentException("Invalid hidden flag " + hidden + " given, must be 0, 1 or 2");
+        }
+        bsr.setHidden(h);
+        bsr.setVeryHidden(vh);
     }
     
     
@@ -761,23 +762,23 @@ public final class Workbook implements Model {
      *  have a Style set.
      */
     public StyleRecord getStyleRecord(int xfIndex) {
-       // Style records always follow after 
-       //  the ExtendedFormat records
-       boolean done = false;
-       for(int i=records.getXfpos(); i<records.size() &&
-                       !done; i++) {
-               Record r = records.get(i);
-               if(r instanceof ExtendedFormatRecord) {
-               } else if(r instanceof StyleRecord) {
-                       StyleRecord sr = (StyleRecord)r;
-                       if(sr.getIndex() == xfIndex) {
-                               return sr;
-                       }
-               } else {
-                       done = true;
-               }
-       }
-       return null;
+        // Style records always follow after 
+        //  the ExtendedFormat records
+        boolean done = false;
+        for(int i=records.getXfpos(); i<records.size() &&
+                !done; i++) {
+            Record r = records.get(i);
+            if(r instanceof ExtendedFormatRecord) {
+            } else if(r instanceof StyleRecord) {
+                StyleRecord sr = (StyleRecord)r;
+                if(sr.getIndex() == xfIndex) {
+                    return sr;
+                }
+            } else {
+                done = true;
+            }
+        }
+        return null;
     }
     /**
      * Creates a new StyleRecord, for the given Extended
@@ -785,29 +786,29 @@ public final class Workbook implements Model {
      *  records collection
      */
     public StyleRecord createStyleRecord(int xfIndex) {
-       // Style records always follow after 
-       //  the ExtendedFormat records
-       StyleRecord newSR = new StyleRecord();
-       newSR.setIndex((short)xfIndex);
-       
-       // Find the spot
-       int addAt = -1;
-       for(int i=records.getXfpos(); i<records.size() &&
-                       addAt == -1; i++) {
-               Record r = records.get(i);
-               if(r instanceof ExtendedFormatRecord ||
-                               r instanceof StyleRecord) {
-                       // Keep going
-               } else {
-                       addAt = i;
-               }
-       }
-       if(addAt == -1) {
-               throw new IllegalStateException("No XF Records found!");
-       }
-       records.add(addAt, newSR);
-       
-       return newSR;
+        // Style records always follow after 
+        //  the ExtendedFormat records
+        StyleRecord newSR = new StyleRecord();
+        newSR.setIndex((short)xfIndex);
+        
+        // Find the spot
+        int addAt = -1;
+        for(int i=records.getXfpos(); i<records.size() &&
+                addAt == -1; i++) {
+            Record r = records.get(i);
+            if(r instanceof ExtendedFormatRecord ||
+                    r instanceof StyleRecord) {
+                // Keep going
+            } else {
+                addAt = i;
+            }
+        }
+        if(addAt == -1) {
+            throw new IllegalStateException("No XF Records found!");
+        }
+        records.add(addAt, newSR);
+        
+        return newSR;
     }
 
     /**
@@ -1914,8 +1915,7 @@ public final class Workbook implements Model {
      */
     public String findSheetNameFromExternSheet(int externSheetIndex){
 
-        int indexToSheet = linkTable.getIndexToSheet(externSheetIndex);
-        
+        int indexToSheet = linkTable.getIndexToInternalSheet(externSheetIndex);
         if (indexToSheet < 0) {
             // TODO - what does '-1' mean here?
             //error check, bail out gracefully!
@@ -1927,6 +1927,13 @@ public final class Workbook implements Model {
         }
         return getSheetName(indexToSheet);
     }
+    public ExternalSheet getExternalSheet(int externSheetIndex) {
+        String[] extNames = linkTable.getExternalBookAndSheetName(externSheetIndex);
+        if (extNames == null) {
+            return null;
+        }
+        return new ExternalSheet(extNames[0], extNames[1]);
+    }
 
     /**
      * Finds the sheet index for a particular external sheet number.
@@ -1944,9 +1951,14 @@ public final class Workbook implements Model {
      * @return index to extern sheet
      */
     public short checkExternSheet(int sheetNumber){
-        return getOrCreateLinkTable().checkExternSheet(sheetNumber);
+        return (short)getOrCreateLinkTable().checkExternSheet(sheetNumber);
     }
 
+       public int getExternalSheetIndex(String workbookName, String sheetName) {
+               return getOrCreateLinkTable().getExternalSheetIndex(workbookName, sheetName);
+       }
+    
+
     /** gets the total number of names
      * @return number of names
      */
index c4f00581b2e13eb5be9373a2555a8da8ea42d3c7..64f86c3f124f0ca97407d2990107714fc0e1b17e 100644 (file)
@@ -250,10 +250,13 @@ public class ExternSheetRecord extends Record {
                return _list.size() - 1;
        }
 
-       public int getRefIxForSheet(int sheetIndex) {
+       public int getRefIxForSheet(int externalBookIndex, int sheetIndex) {
                int nItems = _list.size();
                for (int i = 0; i < nItems; i++) {
                        RefSubRecord ref = getRef(i);
+                       if (ref.getExtBookIndex() != externalBookIndex) {
+                               continue;
+                       }
                        if (ref.getFirstSheetIndex() == sheetIndex && ref.getLastSheetIndex() == sheetIndex) {
                                return i;
                        }
index c75e2db8944fef9f3231c29b5ecf1cf1ae3d1129..b4e46c6fdc8356024ea2499b94e8d2445d515afe 100644 (file)
@@ -221,8 +221,33 @@ public final class SupBookRecord extends Record {
     {
         return sid;
     }
-    public UnicodeString getURL() {
-        return field_2_encoded_url;
+    public String getURL() {
+        String encodedUrl = field_2_encoded_url.getString();
+        switch(encodedUrl.charAt(0)) {
+            case 0: // Reference to an empty workbook name
+                return encodedUrl.substring(1); // will this just be empty string?
+            case 1: // encoded file name
+                return decodeFileName(encodedUrl);
+            case 2: // Self-referential external reference
+                return encodedUrl.substring(1);
+                
+        }
+        return encodedUrl;
+    }
+    private static String decodeFileName(String encodedUrl) {
+        return encodedUrl.substring(1);
+        // TODO the following special characters may appear in the rest of the string, and need to get interpreted
+        /* see "MICROSOFT OFFICE EXCEL 97-2007  BINARY FILE FORMAT SPECIFICATION"
+        chVolume  1 
+        chSameVolume  2 
+        chDownDir  3
+        chUpDir  4 
+        chLongVolume  5
+        chStartupDir  6
+        chAltStartupDir 7
+        chLibDir  8
+        
+        */
     }
     public UnicodeString[] getSheetNames() {
         return (UnicodeString[]) field_3_sheet_names.clone();
index 9a538cf70dbe4ba9881c4bd1d94bb5ac6d8182cb..58cdc5b275bfc21aff88643fe9cc5d23bf8aee52 100644 (file)
@@ -18,8 +18,9 @@
 package org.apache.poi.hssf.record.formula;
 
 import org.apache.poi.hssf.record.RecordInputStream;
-import org.apache.poi.ss.formula.WorkbookDependentFormula;
+import org.apache.poi.ss.formula.ExternSheetReferenceToken;
 import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
+import org.apache.poi.ss.formula.WorkbookDependentFormula;
 import org.apache.poi.util.LittleEndian;
 
 /**
@@ -31,7 +32,7 @@ import org.apache.poi.util.LittleEndian;
  * @author Jason Height (jheight at chariot dot net dot au)
  * @version 1.0-pre
  */
-public final class Area3DPtg extends AreaPtgBase implements WorkbookDependentFormula {
+public final class Area3DPtg extends AreaPtgBase implements WorkbookDependentFormula, ExternSheetReferenceToken {
        public final static byte sid = 0x3b;
        private final static int SIZE = 11; // 10 + 1 for Ptg
        
@@ -76,8 +77,8 @@ public final class Area3DPtg extends AreaPtgBase implements WorkbookDependentFor
                return SIZE;
        }
 
-       public short getExternSheetIndex() {
-               return (short)field_1_index_extern_sheet;
+       public int getExternSheetIndex() {
+               return field_1_index_extern_sheet;
        }
 
        public void setExternSheetIndex(int index) {
index 477d32f677c4f472cc86557da70b0301a9bf9db0..319a9819c9f5a5b66b48cde966c0c4d3696f4e09 100644 (file)
@@ -50,10 +50,10 @@ public interface AreaI {
 
                public OffsetArea(int baseRow, int baseColumn, int relFirstRowIx, int relLastRowIx,
                                int relFirstColIx, int relLastColIx) {
-                       _firstRow = baseRow + relFirstRowIx;
-                       _lastRow = baseRow + relLastRowIx;
-                       _firstColumn = baseColumn + relFirstColIx;
-                       _lastColumn = baseColumn + relLastColIx;
+                       _firstRow = baseRow + Math.min(relFirstRowIx, relLastRowIx);
+                       _lastRow = baseRow + Math.max(relFirstRowIx, relLastRowIx);
+                       _firstColumn = baseColumn + Math.min(relFirstColIx, relLastColIx);
+                       _lastColumn = baseColumn + Math.max(relFirstColIx, relLastColIx);
                }
 
                public int getFirstColumn() {
@@ -72,5 +72,4 @@ public interface AreaI {
                        return _lastRow;
                }
        }
-
 }
\ No newline at end of file
index 7bfefc5526808a3e406e3016d239a0ed528c639d..a97ecd4f0388fd58dfaf55c53291f355c85d8103 100644 (file)
@@ -30,248 +30,264 @@ import org.apache.poi.util.LittleEndian;
  * @author Jason Height (jheight at chariot dot net dot au)
  */
 public abstract class AreaPtgBase extends OperandPtg implements AreaI {
-    /**
-     * TODO - (May-2008) fix subclasses of AreaPtg 'AreaN~' which are used in shared formulas.
-     * see similar comment in ReferencePtg
-     */
-    protected final RuntimeException notImplemented() {
-        return new RuntimeException("Coding Error: This method should never be called. This ptg should be converted");
-    }
+       /**
+        * TODO - (May-2008) fix subclasses of AreaPtg 'AreaN~' which are used in shared formulas.
+        * see similar comment in ReferencePtg
+        */
+       protected final RuntimeException notImplemented() {
+               return new RuntimeException("Coding Error: This method should never be called. This ptg should be converted");
+       }
 
-    /** zero based, unsigned 16 bit */
-    private int             field_1_first_row;
-    /** zero based, unsigned 16 bit */
-    private int             field_2_last_row;
-    /** zero based, unsigned 8 bit */
-    private int             field_3_first_column;
-    /** zero based, unsigned 8 bit */
-    private int             field_4_last_column;
-    
-    private final static BitField   rowRelative = BitFieldFactory.getInstance(0x8000);
-    private final static BitField   colRelative = BitFieldFactory.getInstance(0x4000);
-    private final static BitField   columnMask      = BitFieldFactory.getInstance(0x3FFF);
+       /** zero based, unsigned 16 bit */
+       private int             field_1_first_row;
+       /** zero based, unsigned 16 bit */
+       private int             field_2_last_row;
+       /** zero based, unsigned 8 bit */
+       private int             field_3_first_column;
+       /** zero based, unsigned 8 bit */
+       private int             field_4_last_column;
+       
+       private final static BitField   rowRelative = BitFieldFactory.getInstance(0x8000);
+       private final static BitField   colRelative = BitFieldFactory.getInstance(0x4000);
+       private final static BitField   columnMask      = BitFieldFactory.getInstance(0x3FFF);
 
-    protected AreaPtgBase() {
-        // do nothing
-    }
-    
-    protected AreaPtgBase(String arearef) {
-        AreaReference ar = new AreaReference(arearef);
-        CellReference firstCell = ar.getFirstCell();
-        CellReference lastCell = ar.getLastCell();
-        setFirstRow(firstCell.getRow());
-        setFirstColumn(firstCell.getCol());
-        setLastRow(lastCell.getRow());
-        setLastColumn(lastCell.getCol());
-        setFirstColRelative(!firstCell.isColAbsolute());
-        setLastColRelative(!lastCell.isColAbsolute());
-        setFirstRowRelative(!firstCell.isRowAbsolute());
-        setLastRowRelative(!lastCell.isRowAbsolute());        
-    }
-    
-    protected AreaPtgBase(int firstRow, int lastRow, int firstColumn, int lastColumn,
-            boolean firstRowRelative, boolean lastRowRelative, boolean firstColRelative, boolean lastColRelative) {
-        
-        checkColumnBounds(firstColumn);
-        checkColumnBounds(lastColumn);
-        checkRowBounds(firstRow);
-        checkRowBounds(lastRow);
-      setFirstRow(firstRow);
-      setLastRow(lastRow);
-      setFirstColumn(firstColumn);
-      setLastColumn(lastColumn);
-      setFirstRowRelative(firstRowRelative);
-      setLastRowRelative(lastRowRelative);
-      setFirstColRelative(firstColRelative);
-      setLastColRelative(lastColRelative);
-    }    
+       protected AreaPtgBase() {
+               // do nothing
+       }
+       
+       protected AreaPtgBase(String arearef) {
+               AreaReference ar = new AreaReference(arearef);
+               CellReference firstCell = ar.getFirstCell();
+               CellReference lastCell = ar.getLastCell();
+               setFirstRow(firstCell.getRow());
+               setFirstColumn(firstCell.getCol());
+               setLastRow(lastCell.getRow());
+               setLastColumn(lastCell.getCol());
+               setFirstColRelative(!firstCell.isColAbsolute());
+               setLastColRelative(!lastCell.isColAbsolute());
+               setFirstRowRelative(!firstCell.isRowAbsolute());
+               setLastRowRelative(!lastCell.isRowAbsolute());        
+       }
+       
+       protected AreaPtgBase(int firstRow, int lastRow, int firstColumn, int lastColumn,
+                       boolean firstRowRelative, boolean lastRowRelative, boolean firstColRelative, boolean lastColRelative) {
+               
+               checkColumnBounds(firstColumn);
+               checkColumnBounds(lastColumn);
+               checkRowBounds(firstRow);
+               checkRowBounds(lastRow);
+               
+               if (lastRow > firstRow) {
+                       setFirstRow(firstRow);
+                       setLastRow(lastRow);
+                       setFirstRowRelative(firstRowRelative);
+                       setLastRowRelative(lastRowRelative);
+               } else {
+                       setFirstRow(lastRow);
+                       setLastRow(firstRow);
+                       setFirstRowRelative(lastRowRelative);
+                       setLastRowRelative(firstRowRelative);
+               }
+                       
+               if (lastColumn > firstColumn) {
+                       setFirstColumn(firstColumn);
+                       setLastColumn(lastColumn);
+                       setFirstColRelative(firstColRelative);
+                       setLastColRelative(lastColRelative);
+               } else {
+                       setFirstColumn(lastColumn);
+                       setLastColumn(firstColumn);
+                       setFirstColRelative(lastColRelative);
+                       setLastColRelative(firstColRelative);
+               }
+       }    
 
-    private static void checkColumnBounds(int colIx) {
-        if((colIx & 0x0FF) != colIx) {
-            throw new IllegalArgumentException("colIx (" + colIx + ") is out of range");
-        }
-    }
-    private static void checkRowBounds(int rowIx) {
-        if((rowIx & 0x0FFFF) != rowIx) {
-            throw new IllegalArgumentException("rowIx (" + rowIx + ") is out of range");
-        }
-    }
+       private static void checkColumnBounds(int colIx) {
+               if((colIx & 0x0FF) != colIx) {
+                       throw new IllegalArgumentException("colIx (" + colIx + ") is out of range");
+               }
+       }
+       private static void checkRowBounds(int rowIx) {
+               if((rowIx & 0x0FFFF) != rowIx) {
+                       throw new IllegalArgumentException("rowIx (" + rowIx + ") is out of range");
+               }
+       }
 
-    protected final void readCoordinates(RecordInputStream in) {
-        field_1_first_row = in.readUShort();
-        field_2_last_row = in.readUShort();
-        field_3_first_column = in.readUShort();
-        field_4_last_column = in.readUShort();
-    }
-    protected final void writeCoordinates(byte[] array, int offset) {
-        LittleEndian.putUShort(array, offset + 0, field_1_first_row);
-        LittleEndian.putUShort(array, offset + 2, field_2_last_row);
-        LittleEndian.putUShort(array, offset + 4, field_3_first_column);
-        LittleEndian.putUShort(array, offset + 6, field_4_last_column);        
-    }
+       protected final void readCoordinates(RecordInputStream in) {
+               field_1_first_row = in.readUShort();
+               field_2_last_row = in.readUShort();
+               field_3_first_column = in.readUShort();
+               field_4_last_column = in.readUShort();
+       }
+       protected final void writeCoordinates(byte[] array, int offset) {
+               LittleEndian.putUShort(array, offset + 0, field_1_first_row);
+               LittleEndian.putUShort(array, offset + 2, field_2_last_row);
+               LittleEndian.putUShort(array, offset + 4, field_3_first_column);
+               LittleEndian.putUShort(array, offset + 6, field_4_last_column);        
+       }
 
-    /**
-     * @return the first row in the area
-     */
-    public final int getFirstRow() {
-        return field_1_first_row;
-    }
+       /**
+        * @return the first row in the area
+        */
+       public final int getFirstRow() {
+               return field_1_first_row;
+       }
 
-    /**
-     * sets the first row
-     * @param rowIx number (0-based)
-     */
-    public final void setFirstRow(int rowIx) {
-        checkRowBounds(rowIx);
-        field_1_first_row = rowIx;
-    }
+       /**
+        * sets the first row
+        * @param rowIx number (0-based)
+        */
+       public final void setFirstRow(int rowIx) {
+               checkRowBounds(rowIx);
+               field_1_first_row = rowIx;
+       }
 
-    /**
-     * @return last row in the range (x2 in x1,y1-x2,y2)
-     */
-    public final int getLastRow() {
-        return field_2_last_row;
-    }
+       /**
+        * @return last row in the range (x2 in x1,y1-x2,y2)
+        */
+       public final int getLastRow() {
+               return field_2_last_row;
+       }
 
-    /**
-     * @param rowIx last row number in the area 
-     */
-    public final void setLastRow(int rowIx) {
-        checkRowBounds(rowIx);
-        field_2_last_row = rowIx;
-    }
+       /**
+        * @param rowIx last row number in the area 
+        */
+       public final void setLastRow(int rowIx) {
+               checkRowBounds(rowIx);
+               field_2_last_row = rowIx;
+       }
 
-    /**
-     * @return the first column number in the area.
-     */
-    public final int getFirstColumn() {
-        return columnMask.getValue(field_3_first_column);
-    }
+       /**
+        * @return the first column number in the area.
+        */
+       public final int getFirstColumn() {
+               return columnMask.getValue(field_3_first_column);
+       }
 
-    /**
-     * @return the first column number + the options bit settings unstripped
-     */
-    public final short getFirstColumnRaw() {
-        return (short) field_3_first_column; // TODO
-    }
+       /**
+        * @return the first column number + the options bit settings unstripped
+        */
+       public final short getFirstColumnRaw() {
+               return (short) field_3_first_column; // TODO
+       }
 
-    /**
-     * @return whether or not the first row is a relative reference or not.
-     */
-    public final boolean isFirstRowRelative() {
-        return rowRelative.isSet(field_3_first_column);
-    }
-    
-    /**
-     * sets the first row to relative or not
-     * @param rel is relative or not.
-     */
-    public final void setFirstRowRelative(boolean rel) {
-        field_3_first_column=rowRelative.setBoolean(field_3_first_column,rel);
-    }
+       /**
+        * @return whether or not the first row is a relative reference or not.
+        */
+       public final boolean isFirstRowRelative() {
+               return rowRelative.isSet(field_3_first_column);
+       }
+       
+       /**
+        * sets the first row to relative or not
+        * @param rel is relative or not.
+        */
+       public final void setFirstRowRelative(boolean rel) {
+               field_3_first_column=rowRelative.setBoolean(field_3_first_column,rel);
+       }
 
-    /**
-     * @return isrelative first column to relative or not
-     */
-    public final boolean isFirstColRelative() {
-        return colRelative.isSet(field_3_first_column);
-    }
-    
-    /**
-     * set whether the first column is relative 
-     */
-    public final void setFirstColRelative(boolean rel) {
-        field_3_first_column=colRelative.setBoolean(field_3_first_column,rel);
-    }
+       /**
+        * @return isrelative first column to relative or not
+        */
+       public final boolean isFirstColRelative() {
+               return colRelative.isSet(field_3_first_column);
+       }
+       
+       /**
+        * set whether the first column is relative 
+        */
+       public final void setFirstColRelative(boolean rel) {
+               field_3_first_column=colRelative.setBoolean(field_3_first_column,rel);
+       }
 
-    /**
-     * set the first column in the area
-     */
-    public final void setFirstColumn(int colIx) {
-        checkColumnBounds(colIx);
-        field_3_first_column=columnMask.setValue(field_3_first_column, colIx);
-    }
+       /**
+        * set the first column in the area
+        */
+       public final void setFirstColumn(int colIx) {
+               checkColumnBounds(colIx);
+               field_3_first_column=columnMask.setValue(field_3_first_column, colIx);
+       }
 
-    /**
-     * set the first column irrespective of the bitmasks
-     */
-    public final void setFirstColumnRaw(int column) {
-        field_3_first_column = column;
-    }
+       /**
+        * set the first column irrespective of the bitmasks
+        */
+       public final void setFirstColumnRaw(int column) {
+               field_3_first_column = column;
+       }
 
-    /**
-     * @return lastcolumn in the area
-     */
-    public final int getLastColumn() {
-        return columnMask.getValue(field_4_last_column);
-    }
+       /**
+        * @return lastcolumn in the area
+        */
+       public final int getLastColumn() {
+               return columnMask.getValue(field_4_last_column);
+       }
 
-    /**
-     * @return last column and bitmask (the raw field)
-     */
-    public final short getLastColumnRaw() {
-        return (short) field_4_last_column;
-    }
+       /**
+        * @return last column and bitmask (the raw field)
+        */
+       public final short getLastColumnRaw() {
+               return (short) field_4_last_column;
+       }
 
-    /**
-     * @return last row relative or not
-     */
-    public final boolean isLastRowRelative() {
-        return rowRelative.isSet(field_4_last_column);
-    }
-    
-    /**
-     * set whether the last row is relative or not
-     * @param rel <code>true</code> if the last row relative, else
-     * <code>false</code>
-     */
-    public final void setLastRowRelative(boolean rel) {
-        field_4_last_column=rowRelative.setBoolean(field_4_last_column,rel);
-    }
+       /**
+        * @return last row relative or not
+        */
+       public final boolean isLastRowRelative() {
+               return rowRelative.isSet(field_4_last_column);
+       }
+       
+       /**
+        * set whether the last row is relative or not
+        * @param rel <code>true</code> if the last row relative, else
+        * <code>false</code>
+        */
+       public final void setLastRowRelative(boolean rel) {
+               field_4_last_column=rowRelative.setBoolean(field_4_last_column,rel);
+       }
 
-    /**
-     * @return lastcol relative or not
-     */
-    public final boolean isLastColRelative() {
-        return colRelative.isSet(field_4_last_column);
-    }
-    
-    /**
-     * set whether the last column should be relative or not
-     */
-    public final void setLastColRelative(boolean rel) {
-        field_4_last_column=colRelative.setBoolean(field_4_last_column,rel);
-    }
-    
-    /**
-     * set the last column in the area
-     */
-    public final void setLastColumn(int colIx) {
-        checkColumnBounds(colIx);
-        field_4_last_column=columnMask.setValue(field_4_last_column, colIx);
-    }
+       /**
+        * @return lastcol relative or not
+        */
+       public final boolean isLastColRelative() {
+               return colRelative.isSet(field_4_last_column);
+       }
+       
+       /**
+        * set whether the last column should be relative or not
+        */
+       public final void setLastColRelative(boolean rel) {
+               field_4_last_column=colRelative.setBoolean(field_4_last_column,rel);
+       }
+       
+       /**
+        * set the last column in the area
+        */
+       public final void setLastColumn(int colIx) {
+               checkColumnBounds(colIx);
+               field_4_last_column=columnMask.setValue(field_4_last_column, colIx);
+       }
 
-    /**
-     * set the last column irrespective of the bitmasks
-     */
-    public final void setLastColumnRaw(short column) {
-        field_4_last_column = column;
-    }
-    protected final String formatReferenceAsString() {
-        CellReference topLeft = new CellReference(getFirstRow(),getFirstColumn(),!isFirstRowRelative(),!isFirstColRelative());
-        CellReference botRight = new CellReference(getLastRow(),getLastColumn(),!isLastRowRelative(),!isLastColRelative());
-        
-        if(AreaReference.isWholeColumnReference(topLeft, botRight)) {
-            return (new AreaReference(topLeft, botRight)).formatAsString();
-        }
-        return topLeft.formatAsString() + ":" + botRight.formatAsString(); 
-    }
-    
-    public String toFormulaString() {
-        return formatReferenceAsString();
-    }
+       /**
+        * set the last column irrespective of the bitmasks
+        */
+       public final void setLastColumnRaw(short column) {
+               field_4_last_column = column;
+       }
+       protected final String formatReferenceAsString() {
+               CellReference topLeft = new CellReference(getFirstRow(),getFirstColumn(),!isFirstRowRelative(),!isFirstColRelative());
+               CellReference botRight = new CellReference(getLastRow(),getLastColumn(),!isLastRowRelative(),!isLastColRelative());
+               
+               if(AreaReference.isWholeColumnReference(topLeft, botRight)) {
+                       return (new AreaReference(topLeft, botRight)).formatAsString();
+               }
+               return topLeft.formatAsString() + ":" + botRight.formatAsString(); 
+       }
+       
+       public String toFormulaString() {
+               return formatReferenceAsString();
+       }
 
-    public byte getDefaultOperandClass() {
-        return Ptg.CLASS_REF;
-    }
+       public byte getDefaultOperandClass() {
+               return Ptg.CLASS_REF;
+       }
 }
index 34558ebdf8671722a72482e7258f8b73499ad0be..701a7aee749928a546b499736dc1e6165f3085ca 100644 (file)
@@ -34,13 +34,13 @@ public final class AttrPtg extends ControlPtg {
     private final static int  SIZE = 4;
     private byte              field_1_options;
     private short             field_2_data;
-    
+
     /** only used for tAttrChoose: table of offsets to starts of args */
     private final int[] _jumpTable;
     /** only used for tAttrChoose: offset to the tFuncVar for CHOOSE() */
     private final int   _chooseFuncOffset;
-    
-    // flags 'volatile' and 'space', can be combined.  
+
+    // flags 'volatile' and 'space', can be combined.
     // OOO spec says other combinations are theoretically possible but not likely to occur.
     private static final BitField semiVolatile = BitFieldFactory.getInstance(0x01);
     private static final BitField optiIf       = BitFieldFactory.getInstance(0x02);
@@ -49,12 +49,14 @@ public final class AttrPtg extends ControlPtg {
     private static final BitField sum          = BitFieldFactory.getInstance(0x10);
     private static final BitField baxcel       = BitFieldFactory.getInstance(0x20); // 'assignment-style formula in a macro sheet'
     private static final BitField space        = BitFieldFactory.getInstance(0x40);
-    
+
+    public static final AttrPtg SUM = new AttrPtg(0x0010, 0, null, -1);
+
     public static final class SpaceType {
         private SpaceType() {
             // no instances of this class
         }
-        
+
         /** 00H = Spaces before the next token (not allowed before tParen token) */
         public static final int SPACE_BEFORE = 0x00;
         /** 01H = Carriage returns before the next token (not allowed before tParen token) */
@@ -75,7 +77,7 @@ public final class AttrPtg extends ControlPtg {
         _jumpTable = null;
         _chooseFuncOffset = -1;
     }
-    
+
     public AttrPtg(RecordInputStream in)
     {
         field_1_options = in.readByte();
@@ -92,7 +94,7 @@ public final class AttrPtg extends ControlPtg {
             _jumpTable = null;
             _chooseFuncOffset = -1;
         }
-        
+
     }
     private AttrPtg(int options, int data, int[] jt, int chooseFuncOffset) {
         field_1_options = (byte) options;
@@ -100,7 +102,7 @@ public final class AttrPtg extends ControlPtg {
         _jumpTable = jt;
         _chooseFuncOffset = chooseFuncOffset;
     }
-    
+
     /**
      * @param type a constant from <tt>SpaceType</tt>
      * @param count the number of space characters
@@ -145,7 +147,7 @@ public final class AttrPtg extends ControlPtg {
     {
         return sum.isSet(getOptions());
     }
-    
+
     public void setSum(boolean bsum) {
         field_1_options=sum.setByteBoolean(field_1_options,bsum);
     }
@@ -155,13 +157,13 @@ public final class AttrPtg extends ControlPtg {
     }
 
     /**
-     * Flags this ptg as a goto/jump 
+     * Flags this ptg as a goto/jump
      * @param isGoto
      */
     public void setGoto(boolean isGoto) {
         field_1_options=optGoto.setByteBoolean(field_1_options, isGoto);
     }
-    
+
     // lets hope no one uses this anymore
     public boolean isBaxcel()
     {
@@ -201,7 +203,7 @@ public final class AttrPtg extends ControlPtg {
         } else if(isOptimizedChoose()) {
             sb.append("choose nCases=").append(getData());
         } else if(isGoto()) {
-            sb.append("skip dist=").append(getData()); 
+            sb.append("skip dist=").append(getData());
         } else if(isSum()) {
             sb.append("sum ");
         } else if(isBaxcel()) {
@@ -218,7 +220,7 @@ public final class AttrPtg extends ControlPtg {
         LittleEndian.putShort(array,offset+2, field_2_data);
         int[] jt = _jumpTable;
         if (jt != null) {
-            int joff = offset+4; 
+            int joff = offset+4;
             LittleEndian.putUShort(array, joff, _chooseFuncOffset);
             joff+=2;
             for (int i = 0; i < jt.length; i++) {
@@ -227,7 +229,7 @@ public final class AttrPtg extends ControlPtg {
             }
             LittleEndian.putUShort(array, joff, _chooseFuncOffset);
         }
-        
+
     }
 
     public int getSize()
@@ -249,7 +251,7 @@ public final class AttrPtg extends ControlPtg {
             return toFormulaString() + "(" + operands[ 0 ] + ")";
         }
     }
-  
+
 
     public int getNumberOfOperands()
     {
@@ -260,7 +262,7 @@ public final class AttrPtg extends ControlPtg {
     {
         return -1;
     }
-        
+
    public String toFormulaString() {
       if(semiVolatile.isSet(field_1_options)) {
         return "ATTR(semiVolatile)";
index 412c110d3a14382ff4ed473664c3afe6c8ed25c5..dadbcb8702fb1374ce5374db4ed2611923c56e33 100644 (file)
@@ -18,6 +18,7 @@
 package org.apache.poi.hssf.record.formula;
 
 import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
+import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheet;
 
 /**
  * @author Josh Micich
@@ -29,13 +30,22 @@ final class ExternSheetNameResolver {
        }
 
        public static String prependSheetName(FormulaRenderingWorkbook book, int field_1_index_extern_sheet, String cellRefText) {
-               String sheetName = book.getSheetNameByExternSheet(field_1_index_extern_sheet);
-               StringBuffer sb = new StringBuffer(sheetName.length() + cellRefText.length() + 4);
-               if (sheetName.length() < 1) {
-                       // What excel does if sheet has been deleted
-                       sb.append("#REF"); // note - '!' added just once below
+               ExternalSheet externalSheet = book.getExternalSheet(field_1_index_extern_sheet);
+               StringBuffer sb;
+               if (externalSheet != null) {
+                       String wbName = externalSheet.getWorkbookName();
+                       String sheetName = externalSheet.getSheetName();
+                       sb = new StringBuffer(wbName.length() + sheetName.length() + cellRefText.length() + 4);
+                       SheetNameFormatter.appendFormat(sb, wbName, sheetName);
                } else {
-               SheetNameFormatter.appendFormat(sb, sheetName);
+                       String sheetName = book.getSheetNameByExternSheet(field_1_index_extern_sheet);
+                       sb = new StringBuffer(sheetName.length() + cellRefText.length() + 4);
+                       if (sheetName.length() < 1) {
+                               // What excel does if sheet has been deleted
+                               sb.append("#REF"); // note - '!' added just once below
+                       } else {
+                               SheetNameFormatter.appendFormat(sb, sheetName);
+                       }
                }
                sb.append('!');
                sb.append(cellRefText);
index 261e98ce8b13e7862e2fa0e42e97c1bf5b8152a4..053057925f2af5c7eeebc8ffb5f5219ca51b5fd6 100644 (file)
@@ -19,8 +19,9 @@ package org.apache.poi.hssf.record.formula;
 
 import org.apache.poi.hssf.record.RecordInputStream;
 import org.apache.poi.ss.util.CellReference;
-import org.apache.poi.ss.formula.WorkbookDependentFormula;
+import org.apache.poi.ss.formula.ExternSheetReferenceToken;
 import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
+import org.apache.poi.ss.formula.WorkbookDependentFormula;
 import org.apache.poi.util.LittleEndian;
 
 /**
@@ -31,7 +32,7 @@ import org.apache.poi.util.LittleEndian;
  * @author Jason Height (jheight at chariot dot net dot au)
  * @version 1.0-pre
  */
-public final class Ref3DPtg extends RefPtgBase implements WorkbookDependentFormula {
+public final class Ref3DPtg extends RefPtgBase implements WorkbookDependentFormula, ExternSheetReferenceToken {
     public final static byte sid  = 0x3a;
 
     private final static int  SIZE = 7; // 6 + 1 for Ptg
@@ -75,11 +76,11 @@ public final class Ref3DPtg extends RefPtgBase implements WorkbookDependentFormu
         return SIZE;
     }
 
-    public int getExternSheetIndex(){
+    public int getExternSheetIndex() {
         return field_1_index_extern_sheet;
     }
 
-    public void setExternSheetIndex(int index){
+    public void setExternSheetIndex(int index) {
         field_1_index_extern_sheet = index;
     }
 
index ace857da1e23f032951249f18776e461fe190b01..fd822c39863ad872e6b0a0f98cd132dc96d4c459 100755 (executable)
@@ -66,6 +66,22 @@ public final class SheetNameFormatter {
                        out.append(rawSheetName);
                }
        }
+       public static void appendFormat(StringBuffer out, String workbookName, String rawSheetName) {
+               boolean needsQuotes = needsDelimiting(workbookName) || needsDelimiting(rawSheetName);
+               if(needsQuotes) {
+                       out.append(DELIMITER);
+                       out.append('[');
+                       appendAndEscape(out, workbookName.replace('[', '(').replace(']', ')'));
+                       out.append(']');
+                       appendAndEscape(out, rawSheetName);
+                       out.append(DELIMITER);
+               } else {
+                       out.append('[');
+                       out.append(workbookName);
+                       out.append(']');
+                       out.append(rawSheetName);
+               }
+       }
 
        private static void appendAndEscape(StringBuffer sb, String rawSheetName) {
                int len = rawSheetName.length();
@@ -101,13 +117,27 @@ public final class SheetNameFormatter {
                                return true;
                        }
                }
+               if (nameLooksLikeBooleanLiteral(rawSheetName)) {
+                       return true;
+               }
+               // Error constant literals all contain '#' and other special characters
+               // so they don't get this far
                return false;
        }
        
+       private static boolean nameLooksLikeBooleanLiteral(String rawSheetName) {
+               switch(rawSheetName.charAt(0)) {
+                       case 'T': case 't':
+                               return "TRUE".equalsIgnoreCase(rawSheetName);
+                       case 'F': case 'f':
+                               return "FALSE".equalsIgnoreCase(rawSheetName);
+               }
+               return false;
+       }
        /**
         * @return <code>true</code> if the presence of the specified character in a sheet name would 
         * require the sheet name to be delimited in formulas.  This includes every non-alphanumeric 
-        * character besides underscore '_'.
+        * character besides underscore '_' and dot '.'.
         */
        /* package */ static boolean isSpecialChar(char ch) {
                // note - Character.isJavaIdentifierPart() would allow dollars '$'
@@ -115,7 +145,8 @@ public final class SheetNameFormatter {
                        return false;
                }
                switch(ch) {
-                       case '_': // underscore is ok
+                       case '.': // dot is OK
+                       case '_': // underscore is OK
                                return false;
                        case '\n':
                        case '\r':
diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/RangeEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/RangeEval.java
new file mode 100644 (file)
index 0000000..9398108
--- /dev/null
@@ -0,0 +1,71 @@
+/* ====================================================================
+   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 
+ */
+public final class RangeEval implements OperationEval {
+
+       public static final OperationEval instance = new RangeEval();
+       
+       private RangeEval() {
+       }
+
+       public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
+               if(args.length != 2) {
+                       return ErrorEval.VALUE_INVALID;
+               }
+
+               try {
+                       RefEval reA = evaluateRef(args[0]);
+                       RefEval reB = evaluateRef(args[1]);
+                       return resolveRange(reA, reB);
+               } catch (EvaluationException e) {
+                       return e.getErrorEval();
+               }
+       }
+
+       private static AreaEval resolveRange(RefEval reA, RefEval reB) {
+               
+               int height = reB.getRow() - reA.getRow();
+               int width = reB.getColumn() - reA.getColumn();
+               
+               return reA.offset(0, height, 0, width);
+       }
+
+       private static RefEval evaluateRef(Eval arg) throws EvaluationException {
+               if (arg instanceof RefEval) {
+                       return (RefEval) arg;
+               }
+               if (arg instanceof ErrorEval) {
+                       throw new EvaluationException((ErrorEval)arg);
+               }
+               throw new IllegalArgumentException("Unexpected ref arg class (" + arg.getClass().getName() + ")");
+       }
+
+       public int getNumberOfOperands() {
+               return 2;
+       }
+
+       public int getType() {
+               throw new RuntimeException("obsolete code should not be called");
+       }
+}
index d82a9d1144e88758ce522f758c11a41e76619f97..0b6a9544996c98b5852ad413ee53f005452137e1 100644 (file)
@@ -2,7 +2,9 @@ package org.apache.poi.hssf.usermodel;
 \r
 import org.apache.poi.hssf.model.HSSFFormulaParser;\r
 import org.apache.poi.hssf.model.Workbook;\r
+import org.apache.poi.hssf.record.FormulaRecord;\r
 import org.apache.poi.hssf.record.NameRecord;\r
+import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;\r
 import org.apache.poi.hssf.record.formula.NamePtg;\r
 import org.apache.poi.hssf.record.formula.NameXPtg;\r
 import org.apache.poi.hssf.record.formula.Ptg;\r
@@ -39,6 +41,9 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
                int sheetIndex = _uBook.getSheetIndex(sheetName);\r
                return _iBook.checkExternSheet(sheetIndex);\r
        }\r
+       public int getExternalSheetIndex(String workbookName, String sheetName) {\r
+               return _iBook.getExternalSheetIndex(workbookName, sheetName);\r
+       }\r
 \r
        public EvaluationName getName(int index) {\r
                return new Name(_iBook.getNameRecord(index), index);\r
@@ -57,6 +62,9 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
        public int getSheetIndex(Sheet sheet) {\r
                return _uBook.getSheetIndex(sheet);\r
        }\r
+       public int getSheetIndex(String sheetName) {\r
+               return _uBook.getSheetIndex(sheetName);\r
+       }\r
 \r
        public String getSheetName(int sheetIndex) {\r
                return _uBook.getSheetName(sheetIndex);\r
@@ -75,8 +83,12 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
        }\r
        public int convertFromExternSheetIndex(int externSheetIndex) {\r
                return _iBook.getSheetIndexFromExternSheetIndex(externSheetIndex);\r
-}\r
+       }\r
 \r
+       public ExternalSheet getExternalSheet(int externSheetIndex) {\r
+               return _iBook.getExternalSheet(externSheetIndex);\r
+       }\r
+       \r
        public HSSFWorkbook getWorkbook() {\r
                return _uBook;\r
        }\r
@@ -96,7 +108,15 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
                return new Name(_iBook.getNameRecord(ix), ix);\r
        }\r
        public Ptg[] getFormulaTokens(Cell cell) {\r
-               return HSSFFormulaParser.parse(cell.getCellFormula(), _uBook);\r
+               if (false) {\r
+                       // re-parsing the formula text also works, but is a waste of time\r
+                       // It is useful from time to time to run all unit tests with this code\r
+                       // to make sure that all formulas POI can evaluate can also be parsed.\r
+                       return HSSFFormulaParser.parse(cell.getCellFormula(), _uBook);\r
+               }\r
+               HSSFCell hCell = (HSSFCell) cell;\r
+               FormulaRecord fr = ((FormulaRecordAggregate) hCell.getCellValueRecord()).getFormulaRecord();\r
+               return fr.getParsedExpression();\r
        }\r
 \r
        private static final class Name implements EvaluationName {\r
index d7a3207383182b94a18c21ae4eb4af426e383994..2f30d15c81bd8cf316e380e2b4cc90d5042c451a 100644 (file)
@@ -25,6 +25,7 @@ import org.apache.poi.hssf.record.formula.eval.ErrorEval;
 import org.apache.poi.hssf.record.formula.eval.NumberEval;\r
 import org.apache.poi.hssf.record.formula.eval.StringEval;\r
 import org.apache.poi.hssf.record.formula.eval.ValueEval;\r
+import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment;\r
 import org.apache.poi.ss.formula.WorkbookEvaluator;\r
 import org.apache.poi.ss.usermodel.Cell;\r
 import org.apache.poi.ss.usermodel.CellValue;\r
@@ -56,6 +57,21 @@ public class HSSFFormulaEvaluator /* almost implements FormulaEvaluator */ {
        public HSSFFormulaEvaluator(HSSFWorkbook workbook) {\r
                _bookEvaluator = new WorkbookEvaluator(HSSFEvaluationWorkbook.create(workbook));\r
        }\r
+       \r
+       /**\r
+        * Coordinates several formula evaluators together so that formulas that involve external\r
+        * references can be evaluated.\r
+        * @param workbookNames the simple file names used to identify the workbooks in formulas\r
+        * with external links (for example "MyData.xls" as used in a formula "[MyData.xls]Sheet1!A1")\r
+        * @param evaluators all evaluators for the full set of workbooks required by the formulas. \r
+        */\r
+       public static void setupEnvironment(String[] workbookNames, HSSFFormulaEvaluator[] evaluators) {\r
+               WorkbookEvaluator[] wbEvals = new WorkbookEvaluator[evaluators.length];\r
+               for (int i = 0; i < wbEvals.length; i++) {\r
+                       wbEvals[i] = evaluators[i]._bookEvaluator;\r
+               }\r
+               CollaboratingWorkbooksEnvironment.setup(workbookNames, wbEvals);\r
+       }\r
 \r
        /**\r
         * Does nothing\r
index d6575642ee2a363be5ed2c3877a08d86ccec345a..6a0b0e2d3ab64708b41f082be0c84aa86afecc6a 100644 (file)
@@ -30,6 +30,7 @@ public final class AreaReference extends org.apache.poi.ss.util.AreaReference {
     
     /**
      * Creates an area ref from a pair of Cell References.
+     * Also normalises such that the top-left
      */
     public AreaReference(CellReference topLeft, CellReference botRight) {
                super(topLeft, botRight);
index 0ddb0764ee33c2fcff8c4c7c7fa17872486a01ea..2e31b2a2157dc1179ba137c4793620856bd0796c 100644 (file)
 package org.apache.poi.hssf.util;
 
 /**
- * Common convertion functions between Excel style A1, C27 style
+ * Common conversion functions between Excel style A1, C27 style
  *  cell references, and POI usermodel style row=0, column=0
  *  style references.
  * @author  Avik Sengupta
  * @author  Dennis Doubleday (patch to seperateRowColumns())
  */
 public final class CellReference extends org.apache.poi.ss.util.CellReference {
-       /**
-        * Used to classify identifiers found in formulas as cell references or not.
-        */
-       public static final class NameType {
-               public static final int CELL = 1;
-               public static final int NAMED_RANGE = 2;
-               public static final int BAD_CELL_OR_NAMED_RANGE = -1;
-       }
-
     /**
      * Create an cell ref from a string representation.  Sheet names containing special characters should be
      * delimited and escaped as per normal syntax rules for formulas.
@@ -45,9 +36,6 @@ public final class CellReference extends org.apache.poi.ss.util.CellReference {
     public CellReference(int pRow, int pCol) {
        super(pRow, pCol, true, true);
     }
-    public CellReference(int pRow, short pCol) {
-       super(pRow, (int)pCol, true, true);
-    }
     
     public CellReference(int pRow, int pCol, boolean pAbsRow, boolean pAbsCol) {
         super(null, pRow, pCol, pAbsRow, pAbsCol);
@@ -55,10 +43,6 @@ public final class CellReference extends org.apache.poi.ss.util.CellReference {
     public CellReference(String pSheetName, int pRow, int pCol, boolean pAbsRow, boolean pAbsCol) {
        super(pSheetName, pRow, pCol, pAbsRow, pAbsCol);
     }
-    
-    protected void appendCellReference(StringBuffer sb) {
-       super.appendCellReference(sb);
-    }
     protected static String convertNumToColString(int col) {
        return org.apache.poi.ss.util.CellReference.convertNumToColString(col);
     }
index d6bb9b7bfbc05afae1fb622d1b72220550b6f2c8..6857c4bc1bbee17017745239aa5a619c5913c2f1 100644 (file)
 
 package org.apache.poi.ss.formula;
 
+import org.apache.poi.hssf.util.CellReference;
+
 /**
  * Stores the parameters that identify the evaluation of one cell.<br/>
  */
 final class CellLocation {
        public static final CellLocation[] EMPTY_ARRAY = { };
        
+       private final EvaluationWorkbook _book;
        private final int _sheetIndex;
        private final int _rowIndex;
        private final int _columnIndex;
        private final int _hashCode;
 
-       public CellLocation(int sheetIndex, int rowIndex, int columnIndex) {
+       public CellLocation(EvaluationWorkbook book, int sheetIndex, int rowIndex, int columnIndex) {
                if (sheetIndex < 0) {
                        throw new IllegalArgumentException("sheetIndex must not be negative");
                }
+               _book = book;
                _sheetIndex = sheetIndex;
                _rowIndex = rowIndex;
                _columnIndex = columnIndex;
-               _hashCode = sheetIndex + 17 * (rowIndex + 17 * columnIndex);
+               _hashCode = System.identityHashCode(book) + sheetIndex + 17 * (rowIndex + 17 * columnIndex);
+       }
+       public Object getBook() {
+               return _book;
        }
        public int getSheetIndex() {
                return _sheetIndex;
@@ -49,15 +56,18 @@ final class CellLocation {
 
        public boolean equals(Object obj) {
                CellLocation other = (CellLocation) obj;
-               if (getSheetIndex() != other.getSheetIndex()) {
-                       return false;
-               }
                if (getRowIndex() != other.getRowIndex()) {
                        return false;
                }
                if (getColumnIndex() != other.getColumnIndex()) {
                        return false;
                }
+               if (getSheetIndex() != other.getSheetIndex()) {
+                       return false;
+               }
+               if (getBook() != other.getBook()) {
+                       return false;
+               }
                return true;
        }
        public int hashCode() {
@@ -68,7 +78,8 @@ final class CellLocation {
         * @return human readable string for debug purposes
         */
        public String formatAsString() {
-               return  "ShIx=" + getSheetIndex() + " R=" + getRowIndex() + " C=" + getColumnIndex();
+               CellReference cr = new CellReference(_rowIndex, _columnIndex, false, false);
+               return  "ShIx=" + getSheetIndex() + " " + cr.formatAsString();
        }
 
        public String toString() {
diff --git a/src/java/org/apache/poi/ss/formula/CollaboratingWorkbooksEnvironment.java b/src/java/org/apache/poi/ss/formula/CollaboratingWorkbooksEnvironment.java
new file mode 100644 (file)
index 0000000..c62d2f1
--- /dev/null
@@ -0,0 +1,155 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+package org.apache.poi.ss.formula;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * Manages a collection of {@link WorkbookEvaluator}s, in order to support evaluation of formulas
+ * across spreadsheets.<p/>
+ *
+ * For POI internal use only
+ *
+ * @author Josh Micich
+ */
+public final class CollaboratingWorkbooksEnvironment {
+       
+       public static final CollaboratingWorkbooksEnvironment EMPTY = new CollaboratingWorkbooksEnvironment();
+       
+       private final Map _evaluatorsByName;
+       private final WorkbookEvaluator[] _evaluators;
+
+       private boolean _unhooked;
+       private CollaboratingWorkbooksEnvironment() {
+               _evaluatorsByName = Collections.EMPTY_MAP;
+               _evaluators = new WorkbookEvaluator[0];
+       }
+       public static void setup(String[] workbookNames, WorkbookEvaluator[] evaluators) {
+               int nItems = workbookNames.length;
+               if (evaluators.length != nItems) {
+                       throw new IllegalArgumentException("Number of workbook names is " + nItems 
+                                       + " but number of evaluators is " + evaluators.length);
+               }
+               if (nItems < 1) {
+                       throw new IllegalArgumentException("Must provide at least one collaborating worbook");
+               }
+               new CollaboratingWorkbooksEnvironment(workbookNames, evaluators, nItems);
+       }
+
+       private CollaboratingWorkbooksEnvironment(String[] workbookNames, WorkbookEvaluator[] evaluators, int nItems) {
+               Map m = new HashMap(nItems * 3 / 2);
+               IdentityHashMap uniqueEvals = new IdentityHashMap(nItems * 3 / 2);
+               for(int i=0; i<nItems; i++) {
+                       String wbName = workbookNames[i];
+                       WorkbookEvaluator wbEval = evaluators[i];
+                       if (m.containsKey(wbName)) {
+                               throw new IllegalArgumentException("Duplicate workbook name '" + wbName + "'");
+                       }
+                       if (uniqueEvals.containsKey(wbEval)) {
+                               String msg = "Attempted to register same workbook under names '"
+                                       + uniqueEvals.get(wbEval) + "' and '" + wbName + "'";
+                               throw new IllegalArgumentException(msg);
+                       }
+                       uniqueEvals.put(wbEval, wbName);
+                       m.put(wbName, wbEval);
+               }
+               unhookOldEnvironments(evaluators);
+               hookNewEnvironment(evaluators, this);
+               _unhooked = false;
+               _evaluators = evaluators;
+               _evaluatorsByName = m;
+       }
+
+       private static void hookNewEnvironment(WorkbookEvaluator[] evaluators, CollaboratingWorkbooksEnvironment env) {
+               
+               // All evaluators will need to share the same cache.
+               // but the cache takes an optional evaluation listener.
+               int nItems = evaluators.length;
+               IEvaluationListener evalListener = evaluators[0].getEvaluationListener();
+               // make sure that all evaluators have the same listener
+               for(int i=0; i<nItems; i++) {
+                       if(evalListener != evaluators[i].getEvaluationListener()) {
+                               // This would be very complex to support
+                               throw new RuntimeException("Workbook evaluators must all have the same evaluation listener");
+                       }
+               }
+               EvaluationCache cache = new EvaluationCache(evalListener);
+               
+               for(int i=0; i<nItems; i++) {
+                       evaluators[i].attachToEnvironment(env, cache);
+               }
+               
+       }
+       private void unhookOldEnvironments(WorkbookEvaluator[] evaluators) {
+               Set oldEnvs = new HashSet();
+               for(int i=0; i<evaluators.length; i++) {
+                       oldEnvs.add(evaluators[i].getEnvironment());
+               }
+               CollaboratingWorkbooksEnvironment[] oldCWEs = new CollaboratingWorkbooksEnvironment[oldEnvs.size()];
+               oldEnvs.toArray(oldCWEs);
+               for (int i = 0; i < oldCWEs.length; i++) {
+                       oldCWEs[i].unhook();
+               }
+       }
+
+       /**
+        * 
+        */
+       private void unhook() {
+               if (_evaluators.length < 1) {
+                       return;
+               }
+               for (int i = 0; i < _evaluators.length; i++) {
+                       _evaluators[i].detachFromEnvironment();
+               }
+               _unhooked = true;
+       }
+
+       public WorkbookEvaluator getWorkbookEvaluator(String workbookName) {
+               if (_unhooked) {
+                       throw new IllegalStateException("This environment has been unhooked");
+               }
+               WorkbookEvaluator result = (WorkbookEvaluator) _evaluatorsByName.get(workbookName);
+               if (result == null) {
+                       StringBuffer sb = new StringBuffer(256);
+                       sb.append("Could not resolve external workbook name '").append(workbookName).append("'.");
+                       if (_evaluators.length < 1) {
+                               sb.append(" Workbook environment has not been set up.");
+                       } else {
+                               sb.append(" The following workbook names are valid: (");
+                               Iterator i = _evaluatorsByName.keySet().iterator();
+                               int count=0;
+                               while(i.hasNext()) {
+                                       if (count++>0) {
+                                               sb.append(", ");
+                                       }
+                                       sb.append("'").append(i.next()).append("'");
+                               }
+                               sb.append(")");
+                       }
+                       throw new RuntimeException(sb.toString());
+               }
+               return result;
+       }
+}
index fdc933f6fa684ef0b0a5e66fe642d6c958b09cb5..b0c34fd78a01ff87e3669fa1b6c795a4d5443ce9 100644 (file)
@@ -81,13 +81,7 @@ final class EvaluationCache {
                                                + cellLoc.formatAsString());
                        }
                }
-               if (_evaluationListener == null) {
-                       // optimisation - don't bother sorting if there is no listener.
-               } else {
-                       // for testing
-                       // make order of callbacks to listener more deterministic
-                       Arrays.sort(usedCells, CellLocationComparator);
-               }
+               sortCellLocationsForLogging(usedCells);
                CellCacheEntry entry = getEntry(cellLoc);
                CellLocation[] consumingFormulaCells = entry.getConsumingCells();
                CellLocation[] prevUsedCells = entry.getUsedCells();
@@ -110,6 +104,18 @@ final class EvaluationCache {
                recurseClearCachedFormulaResults(consumingFormulaCells, 0);
        }
 
+       /**
+        * This method sorts the supplied cellLocs so that the order of call-backs to the evaluation 
+        * listener is more deterministic
+        */
+       private void sortCellLocationsForLogging(CellLocation[] cellLocs) {
+               if (_evaluationListener == null) {
+                       // optimisation - don't bother sorting if there is no listener.
+               } else {
+                       Arrays.sort(cellLocs, CellLocationComparator);
+               }
+       }
+
        private void unlinkConsumingCells(CellLocation[] prevUsedCells, CellLocation[] usedCells,
                        CellLocation cellLoc) {
                if (prevUsedCells == null) {
@@ -149,6 +155,7 @@ final class EvaluationCache {
         * @param formulaCells
         */
        private void recurseClearCachedFormulaResults(CellLocation[] formulaCells, int depth) {
+               sortCellLocationsForLogging(formulaCells);
                int nextDepth = depth+1;
                for (int i = 0; i < formulaCells.length; i++) {
                        CellLocation fc = formulaCells[i];
@@ -196,6 +203,10 @@ final class EvaluationCache {
                        CellLocation clB = (CellLocation) b;
                        
                        int cmp;
+                       cmp = System.identityHashCode(clA.getBook()) - System.identityHashCode(clB.getBook());
+                       if (cmp != 0) {
+                               return cmp;
+                       }
                        cmp = clA.getSheetIndex() - clB.getSheetIndex();
                        if (cmp != 0) {
                                return cmp;
index 02917d82a4fdfae4326619974127a2eb47ea2733..6cf938fe9f61eb4ebf0678e4ef4cf0a7629094c2 100644 (file)
@@ -31,12 +31,36 @@ import org.apache.poi.ss.usermodel.Sheet;
  */\r
 public interface EvaluationWorkbook {\r
        String getSheetName(int sheetIndex);\r
+       /**\r
+        * @return -1 if the specified sheet is from a different book\r
+        */\r
        int getSheetIndex(Sheet sheet);\r
+       int getSheetIndex(String sheetName);\r
 \r
        Sheet getSheet(int sheetIndex);\r
 \r
+       /**\r
+        * @return <code>null</code> if externSheetIndex refers to a sheet inside the current workbook\r
+        */\r
+       ExternalSheet getExternalSheet(int externSheetIndex);\r
        int convertFromExternSheetIndex(int externSheetIndex);\r
        EvaluationName getName(NamePtg namePtg);\r
        String resolveNameXText(NameXPtg ptg);\r
        Ptg[] getFormulaTokens(Cell cell);\r
+       \r
+       class ExternalSheet {\r
+               private final String _workbookName;\r
+               private final String _sheetName;\r
+\r
+               public ExternalSheet(String workbookName, String sheetName) {\r
+                       _workbookName = workbookName;\r
+                       _sheetName = sheetName;\r
+               }\r
+               public String getWorkbookName() {\r
+                       return _workbookName;\r
+               }\r
+               public String getSheetName() {\r
+                       return _sheetName;\r
+               }\r
+       }\r
 }\r
diff --git a/src/java/org/apache/poi/ss/formula/ExternSheetReferenceToken.java b/src/java/org/apache/poi/ss/formula/ExternSheetReferenceToken.java
new file mode 100644 (file)
index 0000000..09262a1
--- /dev/null
@@ -0,0 +1,29 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.ss.formula;
+
+/**
+ * Should be implemented by any {@link Ptg} subclass that needs has an extern sheet index <br/>
+ * 
+ * For POI internal use only
+ * 
+ * @author Josh Micich
+ */
+public interface ExternSheetReferenceToken {
+       int getExternSheetIndex();
+}
index df2f8c282f8e6e54f306b1726a578653b995db10..1398399ff4b9361ece7fdc910c88947c82a9994d 100644 (file)
@@ -49,6 +49,7 @@ import org.apache.poi.hssf.record.formula.ParenthesisPtg;
 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.RangePtg;
 import org.apache.poi.hssf.record.formula.Ref3DPtg;
 import org.apache.poi.hssf.record.formula.RefPtg;
 import org.apache.poi.hssf.record.formula.StringPtg;
@@ -60,7 +61,7 @@ import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
 import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
 import org.apache.poi.hssf.util.AreaReference;
 import org.apache.poi.hssf.util.CellReference;
-import org.apache.poi.hssf.util.CellReference.NameType;
+import org.apache.poi.ss.util.CellReference.NameType;
 
 /**
  * This class parses a formula string into a List of tokens in RPN order.
@@ -81,6 +82,33 @@ import org.apache.poi.hssf.util.CellReference.NameType;
  *  @author Josh Micich
  */
 public final class FormulaParser {
+    private static final class Identifier {
+        private final String _name;
+        private final boolean _isQuoted;
+
+        public Identifier(String name, boolean isQuoted) {
+            _name = name;
+            _isQuoted = isQuoted;
+        }
+        public String getName() {
+            return _name;
+        }
+        public boolean isQuoted() {
+            return _isQuoted;
+        }
+        public String toString() {
+            StringBuffer sb = new StringBuffer(64);
+            sb.append(getClass().getName());
+            sb.append(" [");
+            if (_isQuoted) {
+                sb.append("'").append(_name).append("'");
+            } else {
+                sb.append(_name);
+            }
+            sb.append("]");
+            return sb.toString();
+        }
+    }
 
     /**
      * Specific exception thrown when a supplied formula does not parse properly.<br/>
@@ -176,23 +204,23 @@ public final class FormulaParser {
     }
 
     /** Recognize an Alpha Character */
-    private boolean IsAlpha(char c) {
+    private static boolean IsAlpha(char c) {
         return Character.isLetter(c) || c == '$' || c=='_';
     }
 
     /** Recognize a Decimal Digit */
-    private boolean IsDigit(char c) {
+    private static boolean IsDigit(char c) {
         return Character.isDigit(c);
     }
 
     /** Recognize an Alphanumeric */
-    private boolean  IsAlNum(char c) {
-        return  (IsAlpha(c) || IsDigit(c));
+    private static boolean IsAlNum(char c) {
+        return IsAlpha(c) || IsDigit(c);
     }
 
     /** Recognize White Space */
-    private boolean IsWhite( char c) {
-        return  (c ==' ' || c== TAB);
+    private static boolean IsWhite( char c) {
+        return  c ==' ' || c== TAB;
     }
 
     /** Skip Over Leading White Space */
@@ -213,7 +241,13 @@ public final class FormulaParser {
         }
         GetChar();
     }
-
+    private String parseUnquotedIdentifier() {
+        Identifier iden = parseIdentifier();
+        if (iden.isQuoted()) {
+            throw expected("unquoted identifier");
+        }
+        return iden.getName();
+    }
     /**
      * Parses a sheet name, named range name, or simple cell reference.<br/>
      * Note - identifiers in Excel can contain dots, so this method may return a String
@@ -221,18 +255,17 @@ public final class FormulaParser {
      * may return a value like "A1..B2", in which case the caller must convert it to
      * an area reference like "A1:B2"
      */
-    private String parseIdentifier() {
-        StringBuffer Token = new StringBuffer();
-        if (!IsAlpha(look) && look != '\'') {
+    private Identifier parseIdentifier() {
+        StringBuffer sb = new StringBuffer();
+        if (!IsAlpha(look) && look != '\'' && look != '[') {
             throw expected("Name");
         }
-        if(look == '\'')
-        {
+        boolean isQuoted = look == '\''; 
+        if(isQuoted) {
             Match('\'');
             boolean done = look == '\'';
-            while(!done)
-            {
-                Token.append(look);
+            while(!done) {
+                sb.append(look);
                 GetChar();
                 if(look == '\'')
                 {
@@ -240,17 +273,15 @@ public final class FormulaParser {
                     done = look != '\'';
                 }
             }
-        }
-        else
-        {
+        } else {
             // allow for any sequence of dots and identifier chars
             // special case of two consecutive dots is best treated in the calling code
-            while (IsAlNum(look) || look == '.') {
-                Token.append(look);
+            while (IsAlNum(look) || look == '.' || look == '[' || look == ']') {
+                sb.append(look);
                 GetChar();
             }
         }
-        return Token.toString();
+        return new Identifier(sb.toString(), isQuoted);
     }
 
     /** Get a Number */
@@ -265,72 +296,112 @@ public final class FormulaParser {
     }
 
     private ParseNode parseFunctionReferenceOrName() {
-        String name = parseIdentifier();
+        Identifier iden = parseIdentifier();
         if (look == '('){
             //This is a function
-            return function(name);
+            return function(iden.getName());
         }
-        return new ParseNode(parseNameOrReference(name));
-    }
-
-    private Ptg parseNameOrReference(String name) {
-
-        AreaReference areaRef = parseArea(name);
-        if (areaRef != null) {
-            // will happen if dots are used instead of colon
-            return new AreaPtg(areaRef.formatAsString());
+        if (!iden.isQuoted()) {
+            String name = iden.getName();
+            if (name.equalsIgnoreCase("TRUE") || name.equalsIgnoreCase("FALSE")) {
+                return  new ParseNode(new BoolPtg(name.toUpperCase()));
+            }
         }
+        return parseRangeExpression(iden);
+    }
 
-        if (look == ':' || look == '.') { // this is a AreaReference
+    private ParseNode parseRangeExpression(Identifier iden) {
+        Ptg ptgA = parseNameOrCellRef(iden);
+        if (look == ':') {
             GetChar();
-
-            while (look == '.') { // formulas can have . or .. or ... instead of :
-                GetChar();
+            Identifier iden2 = parseIdentifier();
+            Ptg ptgB = parseNameOrCellRef(iden2);
+            Ptg simplified = reduceRangeExpression(ptgA, ptgB);
+            
+            if (simplified == null) {
+                ParseNode[] children = {
+                    new ParseNode(ptgA),    
+                    new ParseNode(ptgB),
+                };
+                return new ParseNode(RangePtg.instance, children);
             }
+            return new ParseNode(simplified);
+        }
+        return new ParseNode(ptgA);
+    } 
+    
+    /**
+     * 
+     * "A1", "B3" -> "A1:B3"   
+     * "sheet1!A1", "B3" -> "sheet1!A1:B3"
+     * 
+     * @return <code>null</code> if the range expression cannot / shouldn't be reduced.
+     */
+    private static Ptg reduceRangeExpression(Ptg ptgA, Ptg ptgB) {
+        if (!(ptgB instanceof RefPtg)) {
+            // only when second ref is simple 2-D ref can the range 
+            // expression be converted to an area ref
+            return null;
+        }
+        RefPtg refB = (RefPtg) ptgB;
 
-            String first = name;
-            String second = parseIdentifier();
-            return new AreaPtg(first+":"+second);
+        if (ptgA instanceof RefPtg) {
+            RefPtg refA = (RefPtg) ptgA;
+            return new AreaPtg(refA.getRow(), refB.getRow(), refA.getColumn(), refB.getColumn(),
+                    refA.isRowRelative(), refB.isRowRelative(), refA.isColRelative(), refB.isColRelative());
+        }
+        if (ptgA instanceof Ref3DPtg) {
+            Ref3DPtg refA = (Ref3DPtg) ptgA;
+            return new Area3DPtg(refA.getRow(), refB.getRow(), refA.getColumn(), refB.getColumn(),
+                    refA.isRowRelative(), refB.isRowRelative(), refA.isColRelative(), refB.isColRelative(),
+                    refA.getExternSheetIndex());
         }
+        // Note - other operand types (like AreaPtg) which probably can't evaluate 
+        // do not cause validation errors at parse time
+        return null;
+    }
 
+    private Ptg parseNameOrCellRef(Identifier iden) {
+        
         if (look == '!') {
-            Match('!');
-            String sheetName = name;
-            String first = parseIdentifier();
-            int externIdx = book.getExternalSheetIndex(sheetName);
-            areaRef = parseArea(name);
-            if (areaRef != null) {
-                // will happen if dots are used instead of colon
-                return new Area3DPtg(areaRef.formatAsString(), externIdx);
-            }
-            if (look == ':') {
-                Match(':');
-                String second=parseIdentifier();
-                if (look == '!') {
-                    //The sheet name was included in both of the areas. Only really
-                    //need it once
-                    Match('!');
-                    String third=parseIdentifier();
-
-                    if (!sheetName.equals(second))
-                        throw new RuntimeException("Unhandled double sheet reference.");
-
-                    return new Area3DPtg(first+":"+third,externIdx);
-                }
-                return new Area3DPtg(first+":"+second,externIdx);
+            GetChar();
+            // 3-D ref
+            // this code assumes iden is a sheetName
+            // TODO - handle <book name> ! <named range name>
+            int externIdx = getExternalSheetIndex(iden.getName());
+            String secondIden = parseUnquotedIdentifier();
+            AreaReference areaRef = parseArea(secondIden);
+            if (areaRef == null) {
+                return new Ref3DPtg(secondIden, externIdx);
             }
-            return new Ref3DPtg(first, externIdx);
-        }
-        if (name.equalsIgnoreCase("TRUE") || name.equalsIgnoreCase("FALSE")) {
-            return new BoolPtg(name.toUpperCase());
+            // will happen if dots are used instead of colon
+            return new Area3DPtg(areaRef.formatAsString(), externIdx);
         }
 
+        String name = iden.getName();
+        AreaReference areaRef = parseArea(name);
+        if (areaRef != null) {
+            // will happen if dots are used instead of colon
+            return new AreaPtg(areaRef.formatAsString());
+        }
         // This can be either a cell ref or a named range
-        // Try to spot which it is
+
+
         int nameType = CellReference.classifyCellReference(name);
         if (nameType == NameType.CELL) {
             return new RefPtg(name);
         }
+        if (look == ':') {
+            if (nameType == NameType.COLUMN) {
+                GetChar();
+                String secondIden = parseUnquotedIdentifier();
+                if (CellReference.classifyCellReference(secondIden) != NameType.COLUMN) {
+                    throw new FormulaParseException("Expected full column after '" + name 
+                            + ":' but got '" + secondIden + "'");
+                }
+                return new AreaPtg(name + ":" + secondIden);
+            }
+        }
         if (nameType != NameType.NAMED_RANGE) {
             new FormulaParseException("Name '" + name
                 + "' does not look like a cell reference or named range");
@@ -347,6 +418,17 @@ public final class FormulaParser {
                     + name + "' is not a range as expected");
     }
 
+    private int getExternalSheetIndex(String name) {
+        if (name.charAt(0) == '[') {
+            // we have a sheet name qualified with workbook name e.g. '[MyData.xls]Sheet1'
+            int pos = name.lastIndexOf(']'); // safe because sheet names never have ']'
+            String wbName = name.substring(1, pos);
+            String sheetName = name.substring(pos+1);
+            return book.getExternalSheetIndex(wbName, sheetName);
+        }
+        return book.getExternalSheetIndex(name);
+    }
+
     /**
      * @param name an 'identifier' like string (i.e. contains alphanums, and dots)
      * @return <code>null</code> if name cannot be split at a dot
@@ -585,7 +667,7 @@ public final class FormulaParser {
                 Match('}');
                 return arrayNode;
         }
-        if (IsAlpha(look) || look == '\''){
+        if (IsAlpha(look) || look == '\'' || look == '['){
             return parseFunctionReferenceOrName();
         }
         // else - assume number
@@ -662,7 +744,7 @@ public final class FormulaParser {
     }
 
     private Boolean parseBooleanLiteral() {
-        String iden = parseIdentifier();
+        String iden = parseUnquotedIdentifier();
         if ("TRUE".equalsIgnoreCase(iden)) {
             return Boolean.TRUE;
         }
@@ -720,7 +802,7 @@ public final class FormulaParser {
 
     private int parseErrorLiteral() {
         Match('#');
-        String part1 = parseIdentifier().toUpperCase();
+        String part1 = parseUnquotedIdentifier().toUpperCase();
 
         switch(part1.charAt(0)) {
             case 'V':
index 69431c2c221809a30d7cd53540df9acf0234caa4..e9be7d1d39dac4ed9b6a1d92d4d474640e025a34 100644 (file)
@@ -32,6 +32,16 @@ public interface FormulaParsingWorkbook {
         */\r
        EvaluationName getName(String name);\r
 \r
-       int getExternalSheetIndex(String sheetName);\r
        NameXPtg getNameXPtg(String name);\r
+\r
+       /**\r
+        * gets the externSheet index for a sheet from this workbook\r
+        */\r
+       int getExternalSheetIndex(String sheetName);\r
+       /**\r
+        * gets the externSheet index for a sheet from an external workbook\r
+        * @param workbookName e.g. "Budget.xls"\r
+        * @param sheetName a name of a sheet in that workbook\r
+        */\r
+       int getExternalSheetIndex(String workbookName, String sheetName);\r
 }\r
index ac95f4da0f04e3e3e165401eb0920187de2af881..c9b95f6b1c306ffd354a69da3f4a37c90089f779 100644 (file)
@@ -19,6 +19,7 @@ package org.apache.poi.ss.formula;
 \r
 import org.apache.poi.hssf.record.formula.NamePtg;\r
 import org.apache.poi.hssf.record.formula.NameXPtg;\r
+import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheet;\r
 \r
 /**\r
  * Abstracts a workbook for the purpose of converting formula to text.<br/>\r
@@ -29,6 +30,10 @@ import org.apache.poi.hssf.record.formula.NameXPtg;
  */\r
 public interface FormulaRenderingWorkbook {\r
 \r
+       /**\r
+        * @return <code>null</code> if externSheetIndex refers to a sheet inside the current workbook\r
+        */\r
+       ExternalSheet getExternalSheet(int externSheetIndex);\r
        String getSheetNameByExternSheet(int externSheetIndex);\r
        String resolveNameXText(NameXPtg nameXPtg);\r
        String getNameText(NamePtg namePtg);\r
index 79087e4d6db556585cc5def3bf67cd0c26f0b27a..91226bd53b936e2a902f09e50da846c5fde99a98 100644 (file)
@@ -19,6 +19,7 @@ package org.apache.poi.ss.formula;
 
 import org.apache.poi.hssf.record.formula.AbstractFunctionPtg;
 import org.apache.poi.hssf.record.formula.ControlPtg;
+import org.apache.poi.hssf.record.formula.RangePtg;
 import org.apache.poi.hssf.record.formula.ValueOperatorPtg;
 import org.apache.poi.hssf.record.formula.Ptg;
 
@@ -115,6 +116,10 @@ final class OperandClassTransformer {
                        return;
                }
                if (children.length > 0) {
+                       if (token == RangePtg.instance) {
+                               // TODO is any token transformation required under the various ref operators?
+                               return;
+                       }
                        throw new IllegalStateException("Node should not have any children");
                }
 
index eaa57c114fbd972ae2b8337661b2052d74447fc3..09c93723f98f6c0cded804d25e21a6a83009c910 100755 (executable)
@@ -40,6 +40,7 @@ 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.RangePtg;
 import org.apache.poi.hssf.record.formula.SubtractPtg;
 import org.apache.poi.hssf.record.formula.UnaryMinusPtg;
 import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
@@ -57,6 +58,7 @@ 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.RangeEval;
 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;
@@ -101,6 +103,7 @@ final class OperationEvaluatorFactory {
                add(m, SubtractPtg.class, SubtractEval.instance);
                add(m, UnaryMinusPtg.class, UnaryMinusEval.instance);
                add(m, UnaryPlusPtg.class, UnaryPlusEval.instance);
+               add(m, RangePtg.class, RangeEval.instance);
                return m;
        }
 
index b41e2c2c3fcd07e685a701f17d5f0c0357ab53a1..9c02ca9f9e9db5ccb7e7443ece66418826c9fb98 100644 (file)
@@ -22,12 +22,18 @@ import java.util.Map;
 import java.util.Stack;
 
 import org.apache.poi.hssf.record.formula.Area3DPtg;
+import org.apache.poi.hssf.record.formula.AreaErrPtg;
 import org.apache.poi.hssf.record.formula.AreaPtg;
+import org.apache.poi.hssf.record.formula.AttrPtg;
 import org.apache.poi.hssf.record.formula.BoolPtg;
 import org.apache.poi.hssf.record.formula.ControlPtg;
+import org.apache.poi.hssf.record.formula.DeletedArea3DPtg;
+import org.apache.poi.hssf.record.formula.DeletedRef3DPtg;
 import org.apache.poi.hssf.record.formula.ErrPtg;
+import org.apache.poi.hssf.record.formula.FuncVarPtg;
 import org.apache.poi.hssf.record.formula.IntPtg;
 import org.apache.poi.hssf.record.formula.MemErrPtg;
+import org.apache.poi.hssf.record.formula.MemFuncPtg;
 import org.apache.poi.hssf.record.formula.MissingArgPtg;
 import org.apache.poi.hssf.record.formula.NamePtg;
 import org.apache.poi.hssf.record.formula.NameXPtg;
@@ -35,6 +41,7 @@ import org.apache.poi.hssf.record.formula.NumberPtg;
 import org.apache.poi.hssf.record.formula.OperationPtg;
 import org.apache.poi.hssf.record.formula.Ptg;
 import org.apache.poi.hssf.record.formula.Ref3DPtg;
+import org.apache.poi.hssf.record.formula.RefErrorPtg;
 import org.apache.poi.hssf.record.formula.RefPtg;
 import org.apache.poi.hssf.record.formula.StringPtg;
 import org.apache.poi.hssf.record.formula.UnionPtg;
@@ -53,6 +60,7 @@ import org.apache.poi.hssf.record.formula.eval.RefEval;
 import org.apache.poi.hssf.record.formula.eval.StringEval;
 import org.apache.poi.hssf.record.formula.eval.ValueEval;
 import org.apache.poi.hssf.util.CellReference;
+import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheet;
 import org.apache.poi.ss.usermodel.Cell;
 import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.ss.usermodel.Sheet;
@@ -68,13 +76,14 @@ import org.apache.poi.ss.usermodel.Sheet;
  *
  * @author Josh Micich
  */
-public class WorkbookEvaluator {
+public final class WorkbookEvaluator {
 
        private final EvaluationWorkbook _workbook;
-       private final EvaluationCache _cache;
+       private EvaluationCache _cache;
 
        private final IEvaluationListener _evaluationListener;
        private final Map _sheetIndexesBySheet;
+       private CollaboratingWorkbooksEnvironment _collaboratingWorkbookEnvironment;
 
        public WorkbookEvaluator(EvaluationWorkbook workbook) {
                this (workbook, null);
@@ -84,6 +93,7 @@ public class WorkbookEvaluator {
                _evaluationListener = evaluationListener;
                _cache = new EvaluationCache(evaluationListener);
                _sheetIndexesBySheet = new IdentityHashMap();
+               _collaboratingWorkbookEnvironment = CollaboratingWorkbooksEnvironment.EMPTY;
        }
 
        /**
@@ -101,7 +111,22 @@ public class WorkbookEvaluator {
                        System.out.println(s);
                }
        }
+       /* package */ void attachToEnvironment(CollaboratingWorkbooksEnvironment collaboratingWorkbooksEnvironment, EvaluationCache cache) {
+               _collaboratingWorkbookEnvironment = collaboratingWorkbooksEnvironment;
+               _cache = cache;
+       }
+       /* package */ CollaboratingWorkbooksEnvironment getEnvironment() {
+               return _collaboratingWorkbookEnvironment;
+       }
 
+       /* package */ void detachFromEnvironment() {
+               _collaboratingWorkbookEnvironment = CollaboratingWorkbooksEnvironment.EMPTY;
+               _cache = new EvaluationCache(_evaluationListener);
+       }
+       /* package */ IEvaluationListener getEvaluationListener() {
+               return _evaluationListener;
+       }
+        
        /**
         * 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
@@ -123,7 +148,7 @@ public class WorkbookEvaluator {
                        throw new IllegalArgumentException("value must not be null");
                }
                int sheetIndex = getSheetIndex(sheet);
-               _cache.setValue(new CellLocation(sheetIndex, rowIndex, columnIndex), true, CellLocation.EMPTY_ARRAY, value);
+               _cache.setValue(new CellLocation(_workbook, sheetIndex, rowIndex, columnIndex), true, CellLocation.EMPTY_ARRAY, value);
 
        }
        /**
@@ -132,13 +157,17 @@ public class WorkbookEvaluator {
         */
        public void notifySetFormula(Sheet sheet, int rowIndex, int columnIndex) {
                int sheetIndex = getSheetIndex(sheet);
-               _cache.setValue(new CellLocation(sheetIndex, rowIndex, columnIndex), false, CellLocation.EMPTY_ARRAY, null);
+               _cache.setValue(new CellLocation(_workbook, sheetIndex, rowIndex, columnIndex), false, CellLocation.EMPTY_ARRAY, null);
 
        }
        private int getSheetIndex(Sheet sheet) {
                Integer result = (Integer) _sheetIndexesBySheet.get(sheet);
                if (result == null) {
-                       result = new Integer(_workbook.getSheetIndex(sheet));
+                       int sheetIndex = _workbook.getSheetIndex(sheet);
+                       if (sheetIndex < 0) {
+                               throw new RuntimeException("Specified sheet from a different book");
+                       }
+                       result = new Integer(sheetIndex);
                        _sheetIndexesBySheet.put(sheet, result);
                }
                return result.intValue();
@@ -146,7 +175,7 @@ public class WorkbookEvaluator {
 
        public ValueEval evaluate(Cell srcCell) {
                int sheetIndex = getSheetIndex(srcCell.getSheet());
-               CellLocation cellLoc = new CellLocation(sheetIndex, srcCell.getRowIndex(), srcCell.getCellNum());
+               CellLocation cellLoc = new CellLocation(_workbook, sheetIndex, srcCell.getRowIndex(), srcCell.getCellNum());
                return internalEvaluate(srcCell, cellLoc, new EvaluationTracker(_cache));
        }
 
@@ -181,10 +210,10 @@ public class WorkbookEvaluator {
                                isPlainFormulaCell = false;
                                Ptg[] ptgs = _workbook.getFormulaTokens(srcCell);
                                if(evalListener == null) {
-                                       result = evaluateCell(sheetIndex, rowIndex, (short)columnIndex, ptgs, tracker);
+                                       result = evaluateFormula(sheetIndex, rowIndex, (short)columnIndex, ptgs, tracker);
                                } else {
                                        evalListener.onStartEvaluate(sheetIndex, rowIndex, columnIndex, ptgs);
-                                       result = evaluateCell(sheetIndex, rowIndex, (short)columnIndex, ptgs, tracker);
+                                       result = evaluateFormula(sheetIndex, rowIndex, (short)columnIndex, ptgs, tracker);
                                        evalListener.onEndEvaluate(sheetIndex, rowIndex, columnIndex, result);
                                }
                        }
@@ -225,17 +254,31 @@ public class WorkbookEvaluator {
                }
                throw new RuntimeException("Unexpected cell type (" + cellType + ")");
        }
-       private ValueEval evaluateCell(int sheetIndex, int srcRowNum, short srcColNum, Ptg[] ptgs, EvaluationTracker tracker) {
+       // visibility raised for testing
+       /* package */ ValueEval evaluateFormula(int sheetIndex, int srcRowNum, int srcColNum, Ptg[] ptgs, EvaluationTracker tracker) {
 
                Stack stack = new Stack();
                for (int i = 0, iSize = ptgs.length; i < iSize; i++) {
 
                        // since we don't know how to handle these yet :(
                        Ptg ptg = ptgs[i];
+                       if (ptg instanceof AttrPtg) {
+                               AttrPtg attrPtg = (AttrPtg) ptg;
+                               if (attrPtg.isSum()) {
+                                       // Excel prefers to encode 'SUM()' as a tAttr token, but this evaluator
+                                       // expects the equivalent function token
+                                       byte nArgs = 1;  // tAttrSum always has 1 parameter
+                                       ptg = new FuncVarPtg("SUM", nArgs); 
+                               }
+                       }
                        if (ptg instanceof ControlPtg) {
                                // skip Parentheses, Attr, etc
                                continue;
                        }
+                       if (ptg instanceof MemFuncPtg) {
+                               // can ignore, rest of tokens for this expression are in OK RPN order
+                               continue;
+                       }
                        if (ptg instanceof MemErrPtg) { continue; }
                        if (ptg instanceof MissingArgPtg) {
                                // TODO - might need to push BlankEval or MissingArgEval
@@ -289,7 +332,7 @@ public class WorkbookEvaluator {
         * @return a <tt>NumberEval</tt>, <tt>StringEval</tt>, <tt>BoolEval</tt>,
         *  <tt>BlankEval</tt> or <tt>ErrorEval</tt>. Never <code>null</code>.
         */
-       private static ValueEval dereferenceValue(ValueEval evaluationResult, int srcRowNum, short srcColNum) {
+       private static ValueEval dereferenceValue(ValueEval evaluationResult, int srcRowNum, int srcColNum) {
                if (evaluationResult instanceof RefEval) {
                        RefEval rv = (RefEval) evaluationResult;
                        return rv.getInnerValueEval();
@@ -321,6 +364,20 @@ public class WorkbookEvaluator {
                }
                return operation.evaluate(ops, srcRowNum, (short)srcColNum);
        }
+       private SheetRefEvaluator createExternSheetRefEvaluator(EvaluationTracker tracker,
+                       ExternSheetReferenceToken ptg) {
+               int externSheetIndex = ptg.getExternSheetIndex();
+               ExternalSheet externalSheet = _workbook.getExternalSheet(externSheetIndex);
+               if (externalSheet != null) {
+                       WorkbookEvaluator otherEvaluator = _collaboratingWorkbookEnvironment.getWorkbookEvaluator(externalSheet.getWorkbookName());
+                       EvaluationWorkbook otherBook = otherEvaluator._workbook;
+                       int otherSheetIndex = otherBook.getSheetIndex(externalSheet.getSheetName());
+                       return new SheetRefEvaluator(otherEvaluator, tracker, otherBook, otherSheetIndex);
+               }
+               int otherSheetIndex = _workbook.convertFromExternSheetIndex(externSheetIndex);
+               return new SheetRefEvaluator(this, tracker, _workbook, otherSheetIndex);
+               
+       }
 
        /**
         * returns an appropriate Eval impl instance for the Ptg. The Ptg must be
@@ -329,6 +386,8 @@ public class WorkbookEvaluator {
         * passed here!
         */
        private Eval getEvalForPtg(Ptg ptg, int sheetIndex, EvaluationTracker tracker) {
+               //  consider converting all these (ptg instanceof XxxPtg) expressions to (ptg.getClass() == XxxPtg.class)
+
                if (ptg instanceof NamePtg) {
                        // named ranges, macro functions
                        NamePtg namePtg = (NamePtg) ptg;
@@ -361,16 +420,18 @@ public class WorkbookEvaluator {
                if (ptg instanceof ErrPtg) {
                        return ErrorEval.valueOf(((ErrPtg) ptg).getErrorCode());
                }
+               if (ptg instanceof AreaErrPtg ||ptg instanceof RefErrorPtg 
+                               || ptg instanceof DeletedArea3DPtg || ptg instanceof DeletedRef3DPtg) {
+                               return ErrorEval.REF_INVALID;
+               }
                if (ptg instanceof Ref3DPtg) {
                        Ref3DPtg refPtg = (Ref3DPtg) ptg;
-                       int otherSheetIndex = _workbook.convertFromExternSheetIndex(refPtg.getExternSheetIndex());
-                       SheetRefEvaluator sre = new SheetRefEvaluator(this, tracker, _workbook, otherSheetIndex);
+                       SheetRefEvaluator sre = createExternSheetRefEvaluator(tracker, refPtg);
                        return new LazyRefEval(refPtg, sre);
                }
                if (ptg instanceof Area3DPtg) {
                        Area3DPtg aptg = (Area3DPtg) ptg;
-                       int otherSheetIndex = _workbook.convertFromExternSheetIndex(aptg.getExternSheetIndex());
-                       SheetRefEvaluator sre = new SheetRefEvaluator(this, tracker, _workbook, otherSheetIndex);
+                       SheetRefEvaluator sre = createExternSheetRefEvaluator(tracker, aptg);
                        return new LazyAreaEval(aptg, sre);
                }
                SheetRefEvaluator sre = new SheetRefEvaluator(this, tracker, _workbook, sheetIndex);
@@ -410,7 +471,7 @@ public class WorkbookEvaluator {
                } else {
                        cell = row.getCell(columnIndex);
                }
-               CellLocation cellLoc = new CellLocation(sheetIndex, rowIndex, columnIndex);
+               CellLocation cellLoc = new CellLocation(_workbook, sheetIndex, rowIndex, columnIndex);
                tracker.acceptDependency(cellLoc);
                return internalEvaluate(cell, cellLoc, tracker);
        }
index ae2c9192f0d619fdd4760081cb5f326e4b25e29f..30d92ebcf45ac229ada7267d2fca2d62999ad9e9 100644 (file)
@@ -90,7 +90,7 @@ public class AreaReference {
         for(int i=refPart.length()-1; i>=0; i--) {
             int ch = refPart.charAt(i);
             if (ch == '$' && i==0) {
-               continue;
+                continue;
             }
             if (ch < 'A' || ch > 'Z') {
                 return false;
@@ -103,8 +103,45 @@ public class AreaReference {
      * Creates an area ref from a pair of Cell References.
      */
     public AreaReference(CellReference topLeft, CellReference botRight) {
-        _firstCell = topLeft;
-        _lastCell = botRight;
+        boolean swapRows = topLeft.getRow() > botRight.getRow();
+        boolean swapCols = topLeft.getCol() > botRight.getCol();
+        if (swapRows || swapCols) {
+            int firstRow; 
+            int lastRow; 
+            int firstColumn; 
+            int lastColumn;
+            boolean firstRowAbs; 
+            boolean lastRowAbs; 
+            boolean firstColAbs;
+            boolean lastColAbs;   
+            if (swapRows) {
+                firstRow = botRight.getRow();
+                firstRowAbs = botRight.isRowAbsolute();
+                lastRow = topLeft.getRow();
+                lastRowAbs = topLeft.isRowAbsolute();
+            } else {
+                firstRow = topLeft.getRow();
+                firstRowAbs = topLeft.isRowAbsolute();
+                lastRow = botRight.getRow();
+                lastRowAbs = botRight.isRowAbsolute();
+            }
+            if (swapCols) {
+                firstColumn = botRight.getCol();
+                firstColAbs = botRight.isColAbsolute();
+                lastColumn = topLeft.getCol();
+                lastColAbs = topLeft.isColAbsolute();
+            } else {
+                firstColumn = topLeft.getCol();
+                firstColAbs = topLeft.isColAbsolute();
+                lastColumn = botRight.getCol();
+                lastColAbs = botRight.isColAbsolute();
+            }
+            _firstCell = new CellReference(firstRow, firstColumn, firstRowAbs, firstColAbs);
+            _lastCell = new CellReference(lastRow, lastColumn, lastRowAbs, lastColAbs);
+        } else {
+            _firstCell = topLeft;
+            _lastCell = botRight;
+        }
         _isSingleCell = false;
     }
 
index e5005db5d4d9a88cfa78bf553723b0fce0269ce1..23b4c806e8ac6e867e361fb7ad09e6bf1c05ed2e 100644 (file)
@@ -34,21 +34,28 @@ public class CellReference {
        public static final class NameType {
                public static final int CELL = 1;
                public static final int NAMED_RANGE = 2;
+               public static final int COLUMN = 3;
                public static final int BAD_CELL_OR_NAMED_RANGE = -1;
        }
 
-    /** The character ($) that signifies a row or column value is absolute instead of relative */ 
-    private static final char ABSOLUTE_REFERENCE_MARKER = '$';
-    /** The character (!) that separates sheet names from cell references */ 
-    private static final char SHEET_NAME_DELIMITER = '!';
-    /** The character (') used to quote sheet names when they contain special characters */
-    private static final char SPECIAL_NAME_DELIMITER = '\'';
-    
-    /**
-     * Matches a run of letters followed by a run of digits.  The run of letters is group 1 and the
-     * run of digits is group 2.  Each group may optionally be prefixed with a single '$'.
-     */
+       /** The character ($) that signifies a row or column value is absolute instead of relative */ 
+       private static final char ABSOLUTE_REFERENCE_MARKER = '$';
+       /** The character (!) that separates sheet names from cell references */ 
+       private static final char SHEET_NAME_DELIMITER = '!';
+       /** The character (') used to quote sheet names when they contain special characters */
+       private static final char SPECIAL_NAME_DELIMITER = '\'';
+       
+       /**
+        * Matches a run of one or more letters followed by a run of one or more digits.
+        * The run of letters is group 1 and the run of digits is group 2.  
+        * Each group may optionally be prefixed with a single '$'.
+        */
        private static final Pattern CELL_REF_PATTERN = Pattern.compile("\\$?([A-Za-z]+)\\$?([0-9]+)");
+       /**
+        * Matches a run of one or more letters.  The run of letters is group 1.  
+        * The text may optionally be prefixed with a single '$'.
+        */
+       private static final Pattern COLUMN_REF_PATTERN = Pattern.compile("\\$?([A-Za-z]+)");
        /**
         * Named range names must start with a letter or underscore.  Subsequent characters may include
         * digits or dot.  (They can even end in dot).
@@ -65,96 +72,96 @@ public class CellReference {
     private final boolean _isRowAbs;
     private final boolean _isColAbs;
 
-    /**
-     * Create an cell ref from a string representation.  Sheet names containing special characters should be
-     * delimited and escaped as per normal syntax rules for formulas.
-     */
-    public CellReference(String cellRef) {
-        String[] parts = separateRefParts(cellRef);
-        _sheetName = parts[0];
-        String colRef = parts[1]; 
-        if (colRef.length() < 1) {
-            throw new IllegalArgumentException("Invalid Formula cell reference: '"+cellRef+"'");
-        }
-        _isColAbs = colRef.charAt(0) == '$';
-        if (_isColAbs) {
-            colRef=colRef.substring(1);
-        }
-        _colIndex = convertColStringToIndex(colRef);
-        
-        String rowRef=parts[2];
-        if (rowRef.length() < 1) {
-            throw new IllegalArgumentException("Invalid Formula cell reference: '"+cellRef+"'");
-        }
-        _isRowAbs = rowRef.charAt(0) == '$';
-        if (_isRowAbs) {
-            rowRef=rowRef.substring(1);
-        }
-        _rowIndex = Integer.parseInt(rowRef)-1; // -1 to convert 1-based to zero-based
-    }
-
-    public CellReference(int pRow, int pCol) {
-       this(pRow, pCol & 0xFFFF, false, false);
-    }
-    public CellReference(int pRow, short pCol) {
-       this(pRow, (int)pCol, false, false);
-    }
+       /**
+        * Create an cell ref from a string representation.  Sheet names containing special characters should be
+        * delimited and escaped as per normal syntax rules for formulas.
+        */
+       public CellReference(String cellRef) {
+               String[] parts = separateRefParts(cellRef);
+               _sheetName = parts[0];
+               String colRef = parts[1]; 
+               if (colRef.length() < 1) {
+                       throw new IllegalArgumentException("Invalid Formula cell reference: '"+cellRef+"'");
+               }
+               _isColAbs = colRef.charAt(0) == '$';
+               if (_isColAbs) {
+                       colRef=colRef.substring(1);
+               }
+               _colIndex = convertColStringToIndex(colRef);
+               
+               String rowRef=parts[2];
+               if (rowRef.length() < 1) {
+                       throw new IllegalArgumentException("Invalid Formula cell reference: '"+cellRef+"'");
+               }
+               _isRowAbs = rowRef.charAt(0) == '$';
+               if (_isRowAbs) {
+                       rowRef=rowRef.substring(1);
+               }
+               _rowIndex = Integer.parseInt(rowRef)-1; // -1 to convert 1-based to zero-based
+       }
 
-    public CellReference(int pRow, int pCol, boolean pAbsRow, boolean pAbsCol) {
-        this(null, pRow, pCol, pAbsRow, pAbsCol);
-    }
-    public CellReference(String pSheetName, int pRow, int pCol, boolean pAbsRow, boolean pAbsCol) {
-        // TODO - "-1" is a special value being temporarily used for whole row and whole column area references.
-        // so these checks are currently N.Q.R.
-        if(pRow < -1) {
-            throw new IllegalArgumentException("row index may not be negative");
-        }
-        if(pCol < -1) {
-            throw new IllegalArgumentException("column index may not be negative");
-        }
-        _sheetName = pSheetName;
-        _rowIndex=pRow;
-        _colIndex=pCol;
-        _isRowAbs = pAbsRow;
-        _isColAbs=pAbsCol;
-    }
+       public CellReference(int pRow, int pCol) {
+               this(pRow, pCol, false, false);
+       }
+       public CellReference(int pRow, short pCol) {
+               this(pRow, pCol & 0xFFFF, false, false);
+       }
 
-    public int getRow(){return _rowIndex;}
-    public short getCol(){return (short) _colIndex;}
-    public boolean isRowAbsolute(){return _isRowAbs;}
-    public boolean isColAbsolute(){return _isColAbs;}
-    /**
-      * @return possibly <code>null</code> if this is a 2D reference.  Special characters are not
-      * escaped or delimited
-      */
-    public String getSheetName(){
-        return _sheetName;
-    }
-    public static boolean isPartAbsolute(String part) {
-        return part.charAt(0) == ABSOLUTE_REFERENCE_MARKER;
-    }
+       public CellReference(int pRow, int pCol, boolean pAbsRow, boolean pAbsCol) {
+               this(null, pRow, pCol, pAbsRow, pAbsCol);
+       }
+       public CellReference(String pSheetName, int pRow, int pCol, boolean pAbsRow, boolean pAbsCol) {
+               // TODO - "-1" is a special value being temporarily used for whole row and whole column area references.
+               // so these checks are currently N.Q.R.
+               if(pRow < -1) {
+                       throw new IllegalArgumentException("row index may not be negative");
+               }
+               if(pCol < -1) {
+                       throw new IllegalArgumentException("column index may not be negative");
+               }
+               _sheetName = pSheetName;
+               _rowIndex=pRow;
+               _colIndex=pCol;
+               _isRowAbs = pAbsRow;
+               _isColAbs=pAbsCol;
+       }
 
-    /**
-     * 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
-     */
-    protected static int convertColStringToIndex(String ref) {
-        int pos = 0;
-        int retval=0;
+       public int getRow(){return _rowIndex;}
+       public short getCol(){return (short) _colIndex;}
+       public boolean isRowAbsolute(){return _isRowAbs;}
+       public boolean isColAbsolute(){return _isColAbs;}
+       /**
+         * @return possibly <code>null</code> if this is a 2D reference.  Special characters are not
+         * escaped or delimited
+         */
+       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
+        */
+       public 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;
-            }
+                       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);
@@ -162,64 +169,70 @@ public class CellReference {
                        pos++;
                }
                return retval-1;
-    }
+       }
 
-    /**
-     * Classifies an identifier as either a simple (2D) cell reference or a named range name
-     * @return one of the values from <tt>NameType</tt> 
-     */
-    public static int classifyCellReference(String str) {
-       int len = str.length();
-       if (len < 1) {
-               throw new IllegalArgumentException("Empty string not allowed");
-       }
-       char firstChar = str.charAt(0);
-       switch (firstChar) {
-               case ABSOLUTE_REFERENCE_MARKER:
-               case '.':
-               case '_':
-                       break;
-               default:
-                       if (!Character.isLetter(firstChar)) {
-                       throw new IllegalArgumentException("Invalid first char (" + firstChar 
-                                       + ") of cell reference or named range.  Letter expected");
-               }
-       }
-       if (!Character.isDigit(str.charAt(len-1))) {
-               // no digits at end of str
-               return validateNamedRangeName(str);
-       }
-       Matcher cellRefPatternMatcher = CELL_REF_PATTERN.matcher(str);
-       if (!cellRefPatternMatcher.matches()) {
-               return validateNamedRangeName(str);
-       }
-       String lettersGroup = cellRefPatternMatcher.group(1);
-       String digitsGroup = cellRefPatternMatcher.group(2);
-       if (cellReferenceIsWithinRange(lettersGroup, digitsGroup)) {
-               // valid cell reference
-               return NameType.CELL;
-       }
-       // If str looks like a cell reference, but is out of (row/col) range, it is a valid
-       // named range name
-       // This behaviour is a little weird.  For example, "IW123" is a valid named range name
-       // because the column "IW" is beyond the maximum "IV".  Note - this behaviour is version
-       // dependent.  In Excel 2007, "IW123" is not a valid named range name.
-       if (str.indexOf(ABSOLUTE_REFERENCE_MARKER) >= 0) {
-               // Of course, named range names cannot have '$'
-               return NameType.BAD_CELL_OR_NAMED_RANGE;
-       }
-       return NameType.NAMED_RANGE;
-    }
+       /**
+        * Classifies an identifier as either a simple (2D) cell reference or a named range name
+        * @return one of the values from <tt>NameType</tt> 
+        */
+       public static int classifyCellReference(String str) {
+               int len = str.length();
+               if (len < 1) {
+                       throw new IllegalArgumentException("Empty string not allowed");
+               }
+               char firstChar = str.charAt(0);
+               switch (firstChar) {
+                       case ABSOLUTE_REFERENCE_MARKER:
+                       case '.':
+                       case '_':
+                               break;
+                       default:
+                               if (!Character.isLetter(firstChar)) {
+                                       throw new IllegalArgumentException("Invalid first char (" + firstChar 
+                                                       + ") of cell reference or named range.  Letter expected");
+                               }
+               }
+               if (!Character.isDigit(str.charAt(len-1))) {
+                       // no digits at end of str
+                       return validateNamedRangeName(str);
+               }
+               Matcher cellRefPatternMatcher = CELL_REF_PATTERN.matcher(str);
+               if (!cellRefPatternMatcher.matches()) {
+                       return validateNamedRangeName(str);
+               }
+               String lettersGroup = cellRefPatternMatcher.group(1);
+               String digitsGroup = cellRefPatternMatcher.group(2);
+               if (cellReferenceIsWithinRange(lettersGroup, digitsGroup)) {
+                       // valid cell reference
+                       return NameType.CELL;
+               }
+               // If str looks like a cell reference, but is out of (row/col) range, it is a valid
+               // named range name
+               // This behaviour is a little weird.  For example, "IW123" is a valid named range name
+               // because the column "IW" is beyond the maximum "IV".  Note - this behaviour is version
+               // dependent.  In BIFF12, "IW123" is not a valid named range name, but in BIFF8 it is.
+               if (str.indexOf(ABSOLUTE_REFERENCE_MARKER) >= 0) {
+                       // Of course, named range names cannot have '$'
+                       return NameType.BAD_CELL_OR_NAMED_RANGE;
+               }
+               return NameType.NAMED_RANGE;
+       }
 
-    private static int validateNamedRangeName(String str) {
+       private static int validateNamedRangeName(String str) {
+               Matcher colMatcher = COLUMN_REF_PATTERN.matcher(str);
+               if (colMatcher.matches()) {
+                       String colStr = colMatcher.group(1);
+                       if (isColumnWithnRange(colStr)) {
+                               return NameType.COLUMN;
+                       }
+               }
                if (!NAMED_RANGE_NAME_PATTERN.matcher(str).matches()) {
                        return NameType.BAD_CELL_OR_NAMED_RANGE;
                }
                return NameType.NAMED_RANGE;
-               
        }
-    
-    
+       
+       
        /**
         * Used to decide whether a name of the form "[A-Z]*[0-9]*" that appears in a formula can be 
         * interpreted as a cell reference.  Names of that form can be also used for sheets and/or
@@ -240,7 +253,7 @@ public class CellReference {
         * <blockquote><table border="0" cellpadding="1" cellspacing="0" 
         *                 summary="Notable cases.">
         *   <tr><th>Input&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</th>
-        *      <th>Result&nbsp;</th></tr>
+        *       <th>Result&nbsp;</th></tr>
         *   <tr><td>"A", "1"</td><td>true</td></tr>
         *   <tr><td>"a", "111"</td><td>true</td></tr>
         *   <tr><td>"A", "65536"</td><td>true</td></tr>
@@ -257,23 +270,13 @@ public class CellReference {
         * @return <code>true</code> if the row and col parameters are within range of a BIFF8 spreadsheet.
         */
        public static boolean cellReferenceIsWithinRange(String colStr, String rowStr) {
-               int numberOfLetters = colStr.length();
-               if(numberOfLetters > BIFF8_LAST_COLUMN_TEXT_LEN) {
-                       // "Sheet1" case etc
-                       return false; // that was easy
+               if (!isColumnWithnRange(colStr)) {
+                       return false;
                }
                int nDigits = rowStr.length();
                if(nDigits > BIFF8_LAST_ROW_TEXT_LEN) {
                        return false; 
                }
-               if(numberOfLetters == BIFF8_LAST_COLUMN_TEXT_LEN) {
-                       if(colStr.toUpperCase().compareTo(BIFF8_LAST_COLUMN) > 0) {
-                               return false;
-                       }
-               } else {
-                       // apparent column name has less chars than max
-                       // no need to check range
-               }
                
                if(nDigits == BIFF8_LAST_ROW_TEXT_LEN) {
                        // ASCII comparison is valid if digit count is same
@@ -288,87 +291,104 @@ public class CellReference {
                return true;
        }
 
+       private static boolean isColumnWithnRange(String colStr) {
+               int numberOfLetters = colStr.length();
+               if(numberOfLetters > BIFF8_LAST_COLUMN_TEXT_LEN) {
+                       // "Sheet1" case etc
+                       return false; // that was easy
+               }
+               if(numberOfLetters == BIFF8_LAST_COLUMN_TEXT_LEN) {
+                       if(colStr.toUpperCase().compareTo(BIFF8_LAST_COLUMN) > 0) {
+                               return false;
+                       }
+               } else {
+                       // apparent column name has less chars than max
+                       // no need to check range
+               }
+               return true;
+       }
+
        /**
-     * Separates the row from the columns and returns an array of three Strings.  The first element
-     * is the sheet name. Only the first element may be null.  The second element in is the column 
-     * name still in ALPHA-26 number format.  The third element is the row.
-     */
-    private static String[] separateRefParts(String reference) {
-        
-        int plingPos = reference.lastIndexOf(SHEET_NAME_DELIMITER);
-        String sheetName = parseSheetName(reference, plingPos);
-        int start = plingPos+1;
+        * Separates the row from the columns and returns an array of three Strings.  The first element
+        * is the sheet name. Only the first element may be null.  The second element in is the column 
+        * name still in ALPHA-26 number format.  The third element is the row.
+        */
+       private static String[] separateRefParts(String reference) {
+               
+               int plingPos = reference.lastIndexOf(SHEET_NAME_DELIMITER);
+               String sheetName = parseSheetName(reference, plingPos);
+               int start = plingPos+1;
 
-        int length = reference.length();
+               int length = reference.length();
 
 
-        int loc = start;
-        // skip initial dollars 
-        if (reference.charAt(loc)==ABSOLUTE_REFERENCE_MARKER) {
-            loc++;
-        }
-        // step over column name chars until first digit (or dollars) for row number.
-        for (; loc < length; loc++) {
-            char ch = reference.charAt(loc);
-            if (Character.isDigit(ch) || ch == ABSOLUTE_REFERENCE_MARKER) {
-                break;
-            }
-        }
-        return new String[] {
-           sheetName,
-           reference.substring(start,loc),
-           reference.substring(loc),
-        };
-    }
+               int loc = start;
+               // skip initial dollars 
+               if (reference.charAt(loc)==ABSOLUTE_REFERENCE_MARKER) {
+                       loc++;
+               }
+               // step over column name chars until first digit (or dollars) for row number.
+               for (; loc < length; loc++) {
+                       char ch = reference.charAt(loc);
+                       if (Character.isDigit(ch) || ch == ABSOLUTE_REFERENCE_MARKER) {
+                               break;
+                       }
+               }
+               return new String[] {
+                  sheetName,
+                  reference.substring(start,loc),
+                  reference.substring(loc),
+               };
+       }
 
-    private static String parseSheetName(String reference, int indexOfSheetNameDelimiter) {
-        if(indexOfSheetNameDelimiter < 0) {
-            return null;
-        }
-        
-        boolean isQuoted = reference.charAt(0) == SPECIAL_NAME_DELIMITER;
-        if(!isQuoted) {
-            return reference.substring(0, indexOfSheetNameDelimiter);
-        }
-        int lastQuotePos = indexOfSheetNameDelimiter-1;
-        if(reference.charAt(lastQuotePos) != SPECIAL_NAME_DELIMITER) {
-            throw new RuntimeException("Mismatched quotes: (" + reference + ")");
-        }
+       private static String parseSheetName(String reference, int indexOfSheetNameDelimiter) {
+               if(indexOfSheetNameDelimiter < 0) {
+                       return null;
+               }
+               
+               boolean isQuoted = reference.charAt(0) == SPECIAL_NAME_DELIMITER;
+               if(!isQuoted) {
+                       return reference.substring(0, indexOfSheetNameDelimiter);
+               }
+               int lastQuotePos = indexOfSheetNameDelimiter-1;
+               if(reference.charAt(lastQuotePos) != SPECIAL_NAME_DELIMITER) {
+                       throw new RuntimeException("Mismatched quotes: (" + reference + ")");
+               }
 
-        // TODO - refactor cell reference parsing logic to one place.
-        // Current known incarnations: 
-        //   FormulaParser.GetName()
-        //   CellReference.parseSheetName() (here)
-        //   AreaReference.separateAreaRefs() 
-        //   SheetNameFormatter.format() (inverse)
-        
-        StringBuffer sb = new StringBuffer(indexOfSheetNameDelimiter);
-        
-        for(int i=1; i<lastQuotePos; i++) { // Note boundaries - skip outer quotes
-            char ch = reference.charAt(i);
-            if(ch != SPECIAL_NAME_DELIMITER) {
-                sb.append(ch);
-                continue;
-            }
-            if(i < lastQuotePos) {
-                if(reference.charAt(i+1) == SPECIAL_NAME_DELIMITER) {
-                    // two consecutive quotes is the escape sequence for a single one
-                    i++; // skip this and keep parsing the special name
-                    sb.append(ch);
-                    continue;
-                }
-            }
-            throw new RuntimeException("Bad sheet name quote escaping: (" + reference + ")");
-        }
-        return sb.toString();
-    }
+               // TODO - refactor cell reference parsing logic to one place.
+               // Current known incarnations: 
+               //   FormulaParser.GetName()
+               //   CellReference.parseSheetName() (here)
+               //   AreaReference.separateAreaRefs() 
+               //   SheetNameFormatter.format() (inverse)
+               
+               StringBuffer sb = new StringBuffer(indexOfSheetNameDelimiter);
+               
+               for(int i=1; i<lastQuotePos; i++) { // Note boundaries - skip outer quotes
+                       char ch = reference.charAt(i);
+                       if(ch != SPECIAL_NAME_DELIMITER) {
+                               sb.append(ch);
+                               continue;
+                       }
+                       if(i < lastQuotePos) {
+                               if(reference.charAt(i+1) == SPECIAL_NAME_DELIMITER) {
+                                       // two consecutive quotes is the escape sequence for a single one
+                                       i++; // skip this and keep parsing the special name
+                                       sb.append(ch);
+                                       continue;
+                               }
+                       }
+                       throw new RuntimeException("Bad sheet name quote escaping: (" + reference + ")");
+               }
+               return sb.toString();
+       }
 
-    /**
-     * Takes in a 0-based base-10 column and returns a ALPHA-26
-     *  representation.
-     * eg column #3 -> D
-     */
-    protected static String convertNumToColString(int col) {
+       /**
+        * Takes in a 0-based base-10 column and returns a ALPHA-26
+        *  representation.
+        * eg column #3 -> D
+        */
+       protected static String convertNumToColString(int col) {
                // Excel counts column A as the 1st column, we
                //  treat it as the 0th one
                int excelColNum = col + 1;
@@ -387,36 +407,36 @@ public class CellReference {
                }
                
                return colRef;
-    }
+       }
+
+       /**
+        *  Example return values:
+        *      <table border="0" cellpadding="1" cellspacing="0" summary="Example return values">
+        *        <tr><th align='left'>Result</th><th align='left'>Comment</th></tr>
+        *        <tr><td>A1</td><td>Cell reference without sheet</td></tr>
+        *        <tr><td>Sheet1!A1</td><td>Standard sheet name</td></tr>
+        *        <tr><td>'O''Brien''s Sales'!A1'&nbsp;</td><td>Sheet name with special characters</td></tr>
+        *      </table>
+        * @return the text representation of this cell reference as it would appear in a formula.
+        */
+       public String formatAsString() {
+               StringBuffer sb = new StringBuffer(32);
+               if(_sheetName != null) {
+                       SheetNameFormatter.appendFormat(sb, _sheetName);
+                       sb.append(SHEET_NAME_DELIMITER);
+               }
+               appendCellReference(sb);
+               return sb.toString();
+       }
+       
+       public String toString() {
+               StringBuffer sb = new StringBuffer(64);
+               sb.append(getClass().getName()).append(" [");
+               sb.append(formatAsString());
+               sb.append("]");
+               return sb.toString();
+       }
 
-    /**
-     *  Example return values:
-     *    <table border="0" cellpadding="1" cellspacing="0" summary="Example return values">
-     *      <tr><th align='left'>Result</th><th align='left'>Comment</th></tr>
-     *      <tr><td>A1</td><td>Cell reference without sheet</td></tr>
-     *      <tr><td>Sheet1!A1</td><td>Standard sheet name</td></tr>
-     *      <tr><td>'O''Brien''s Sales'!A1'&nbsp;</td><td>Sheet name with special characters</td></tr>
-     *    </table>
-     * @return the text representation of this cell reference as it would appear in a formula.
-     */
-    public String formatAsString() {
-        StringBuffer sb = new StringBuffer(32);
-        if(_sheetName != null) {
-            SheetNameFormatter.appendFormat(sb, _sheetName);
-            sb.append(SHEET_NAME_DELIMITER);
-        }
-        appendCellReference(sb);
-        return sb.toString();
-    }
-    
-    public String toString() {
-        StringBuffer sb = new StringBuffer(64);
-        sb.append(getClass().getName()).append(" [");
-        sb.append(formatAsString());
-        sb.append("]");
-        return sb.toString();
-    }
-    
        /**
         * Returns the three parts of the cell reference, the
         *  Sheet name (or null if none supplied), the 1 based
@@ -433,18 +453,18 @@ public class CellReference {
                };
        }
 
-    /**
-     * Appends cell reference with '$' markers for absolute values as required.
-     * Sheet name is not included.
-     */
-    protected void appendCellReference(StringBuffer sb) {
-        if(_isColAbs) {
-            sb.append(ABSOLUTE_REFERENCE_MARKER);
-        }
-        sb.append( convertNumToColString(_colIndex));
-        if(_isRowAbs) {
-            sb.append(ABSOLUTE_REFERENCE_MARKER);
-        }
-        sb.append(_rowIndex+1);
-    }
+       /**
+        * Appends cell reference with '$' markers for absolute values as required.
+        * Sheet name is not included.
+        */
+       /* package */ void appendCellReference(StringBuffer sb) {
+               if(_isColAbs) {
+                       sb.append(ABSOLUTE_REFERENCE_MARKER);
+               }
+               sb.append( convertNumToColString(_colIndex));
+               if(_isRowAbs) {
+                       sb.append(ABSOLUTE_REFERENCE_MARKER);
+               }
+               sb.append(_rowIndex+1);
+       }
 }
index 7c03ebe7ff9e21e3ee604be633a66c9f4f0e37ae..ddadf96eb981868dfe40448fea8fdd5a827e3a98 100644 (file)
@@ -57,10 +57,6 @@ public final class XSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
                return convertToExternalSheetIndex(sheetIndex);\r
        }\r
 \r
-       public EvaluationName getName(int index) {\r
-               return new Name(_uBook.getNameAt(index), index, this);\r
-       }\r
-\r
        public EvaluationName getName(String name) {\r
                for(int i=0; i < _uBook.getNumberOfNames(); i++) {\r
                        String nameText = _uBook.getNameName(i);\r
@@ -88,14 +84,15 @@ public final class XSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
                return _uBook.getSheetAt(sheetIndex);\r
        }\r
 \r
-    /**\r
-     * Doesn't do anything - returns the same index\r
-     * TODO - figure out if this is a ole2 specific thing, or\r
-     *  if we need to do something proper here too!\r
-     */\r
-       public Sheet getSheetByExternSheetIndex(int externSheetIndex) {\r
-               int sheetIndex = convertFromExternalSheetIndex(externSheetIndex);\r
-               return _uBook.getSheetAt(sheetIndex);\r
+       public ExternalSheet getExternalSheet(int externSheetIndex) {\r
+               // TODO Auto-generated method stub\r
+               return null;\r
+       }\r
+       public int getExternalSheetIndex(String workbookName, String sheetName) {\r
+               throw new RuntimeException("not implemented yet");\r
+       }\r
+       public int getSheetIndex(String sheetName) {\r
+               return _uBook.getSheetIndex(sheetName);\r
        }\r
 \r
        public Workbook getWorkbook() {\r
index aa5bf46d31fd3b501ee25d1e05fc83bbf2a079a6..d5a491099dff41a8e2942056e634456891bb964a 100644 (file)
@@ -28,7 +28,7 @@ import org.apache.poi.hssf.model.AllModelTests;
 import org.apache.poi.hssf.record.AllRecordTests;
 import org.apache.poi.hssf.usermodel.AllUserModelTests;
 import org.apache.poi.hssf.util.AllHSSFUtilTests;
-import org.apache.poi.ss.formula.TestEvaluationCache;
+import org.apache.poi.ss.formula.AllSSFormulaTests;
 
 /**
  * Test Suite for all sub-packages of org.apache.poi.hssf<br/>
@@ -53,7 +53,7 @@ public final class HSSFTests {
         }
         suite.addTest(new TestSuite(TestEventRecordFactory.class));
         suite.addTest(new TestSuite(TestModelFactory.class));
-        suite.addTest(new TestSuite(TestEvaluationCache.class));
+        suite.addTest(AllSSFormulaTests.suite());
         return suite;
     }
 }
diff --git a/src/testcases/org/apache/poi/hssf/data/multibookFormulaA.xls b/src/testcases/org/apache/poi/hssf/data/multibookFormulaA.xls
new file mode 100644 (file)
index 0000000..b844fc1
Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/multibookFormulaA.xls differ
diff --git a/src/testcases/org/apache/poi/hssf/data/multibookFormulaB.xls b/src/testcases/org/apache/poi/hssf/data/multibookFormulaB.xls
new file mode 100644 (file)
index 0000000..3e4708d
Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/multibookFormulaB.xls differ
index 016fa8398c3a582315c829316f7bb1dfbe3fda77..53061a228b5db183cca96c9a02cc977f92c82404 100644 (file)
@@ -43,11 +43,13 @@ import org.apache.poi.hssf.record.formula.NumberPtg;
 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.Ref3DPtg;
 import org.apache.poi.hssf.record.formula.RefPtg;
 import org.apache.poi.hssf.record.formula.StringPtg;
 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.usermodel.FormulaExtractor;
 import org.apache.poi.hssf.usermodel.HSSFCell;
 import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
 import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook;
@@ -553,14 +555,14 @@ public final class TestFormulaParser extends TestCase {
 
                Class[] expClss;
 
-               expClss = new Class[] { 
-                               RefPtg.class, 
+               expClss = new Class[] {
+                               RefPtg.class,
                                AttrPtg.class, // tAttrIf
-                               MissingArgPtg.class, 
+                               MissingArgPtg.class,
                                AttrPtg.class, // tAttrSkip
                                RefPtg.class,
                                AttrPtg.class, // tAttrSkip
-                               FuncVarPtg.class, 
+                               FuncVarPtg.class,
                };
 
                confirmTokenClasses("if(A1, ,C1)", expClss);
@@ -735,10 +737,10 @@ public final class TestFormulaParser extends TestCase {
        /**
         * Make sure that POI uses the right Func Ptg when encoding formulas.  Functions with variable
         * number of args should get FuncVarPtg, functions with fixed args should get FuncPtg.<p/>
-        * 
+        *
         * Prior to the fix for bug 44675 POI would encode FuncVarPtg for all functions.  In many cases
-        * Excel tolerates the wrong Ptg and evaluates the formula OK (e.g. SIN), but in some cases 
-        * (e.g. COUNTIF) Excel fails to evaluate the formula, giving '#VALUE!' instead. 
+        * Excel tolerates the wrong Ptg and evaluates the formula OK (e.g. SIN), but in some cases
+        * (e.g. COUNTIF) Excel fails to evaluate the formula, giving '#VALUE!' instead.
         */
        public void testFuncPtgSelection() {
 
@@ -777,7 +779,7 @@ public final class TestFormulaParser extends TestCase {
                        parseFormula("round(3.14;2)");
                        throw new AssertionFailedError("Didn't get parse exception as expected");
                } catch (RuntimeException e) {
-                       FormulaParserTestHelper.confirmParseException(e, 
+                       FormulaParserTestHelper.confirmParseException(e,
                                        "Parse error near char 10 ';' in specified formula 'round(3.14;2)'. Expected ',' or ')'");
                }
 
@@ -785,11 +787,11 @@ public final class TestFormulaParser extends TestCase {
                        parseFormula(" =2+2");
                        throw new AssertionFailedError("Didn't get parse exception as expected");
                } catch (RuntimeException e) {
-                       FormulaParserTestHelper.confirmParseException(e, 
+                       FormulaParserTestHelper.confirmParseException(e,
                                        "The specified formula ' =2+2' starts with an equals sign which is not allowed.");
                }
        }
-       
+
        /**
         * this function name has a dot in it.
         */
@@ -798,8 +800,8 @@ public final class TestFormulaParser extends TestCase {
                Ptg[] ptgs;
                try {
                        ptgs = parseFormula("error.type(A1)");
-                       
-                       
+
+
                } catch (IllegalArgumentException e) {
                        if (e.getMessage().equals("Invalid Formula cell reference: 'error'")) {
                                throw new AssertionFailedError("Identified bug 45334");
@@ -811,7 +813,7 @@ public final class TestFormulaParser extends TestCase {
                FuncPtg funcPtg = (FuncPtg) ptgs[1];
                assertEquals("ERROR.TYPE", funcPtg.getName());
        }
-       
+
        public void testNamedRangeThatLooksLikeCell() {
                HSSFWorkbook wb = new HSSFWorkbook();
                HSSFSheet sheet = wb.createSheet("Sheet1");
@@ -838,35 +840,35 @@ public final class TestFormulaParser extends TestCase {
                        cell.setCellFormula("count(pf1)");
                        throw new AssertionFailedError("Expected formula parse execption");
                } catch (RuntimeException e) {
-                       FormulaParserTestHelper.confirmParseException(e, 
+                       FormulaParserTestHelper.confirmParseException(e,
                                        "Specified named range 'pf1' does not exist in the current workbook.");
                }
                cell.setCellFormula("count(fp1)"); // plain cell ref, col is in range
        }
-       
+
        public void testParseAreaRefHighRow_bug45358() {
                Ptg[] ptgs;
                AreaI aptg;
-               
+
                HSSFWorkbook book = new HSSFWorkbook();
                book.createSheet("Sheet1");
-               
+
                ptgs = HSSFFormulaParser.parse("Sheet1!A10:A40000", book);
                aptg = (AreaI) ptgs[0];
                if (aptg.getLastRow() == -25537) {
                        throw new AssertionFailedError("Identified bug 45358");
                }
                assertEquals(39999, aptg.getLastRow());
-               
+
                ptgs = HSSFFormulaParser.parse("Sheet1!A10:A65536", book);
                aptg = (AreaI) ptgs[0];
                assertEquals(65535, aptg.getLastRow());
-               
+
                // plain area refs should be ok too
                ptgs = parseFormula("A10:A65536");
                aptg = (AreaI) ptgs[0];
                assertEquals(65535, aptg.getLastRow());
-               
+
        }
        public void testParseArray()  {
                Ptg[] ptgs;
@@ -875,11 +877,71 @@ public final class TestFormulaParser extends TestCase {
                Ptg ptg0 = ptgs[0];
                assertEquals(ArrayPtg.class, ptg0.getClass());
                assertEquals("{1.0,2.0,2.0,#REF!;FALSE,3.0,3.0,2.0}", ptg0.toFormulaString());
-               
+
                ArrayPtg aptg = (ArrayPtg) ptg0;
                Object[][] values = aptg.getTokenArrayValues();
                assertEquals(ErrorConstant.valueOf(HSSFErrorConstants.ERROR_REF), values[0][3]);
                assertEquals(Boolean.FALSE, values[1][0]);
+       }
+
+       public void testRangeOperator() {
+
+               HSSFWorkbook wb = new HSSFWorkbook();
+               HSSFSheet sheet = wb.createSheet();
+               HSSFCell cell = sheet.createRow(0).createCell(0);
+
+               wb.setSheetName(0, "Sheet1");
+               cell.setCellFormula("Sheet1!B$4:Sheet1!$C1"); // explicit range ':' operator
+               assertEquals("Sheet1!B$4:Sheet1!$C1", cell.getCellFormula()); 
+
+               cell.setCellFormula("Sheet1!B$4:$C1"); // plain area ref
+               assertEquals("Sheet1!B1:$C$4", cell.getCellFormula()); // note - area ref is normalised
                
+               cell.setCellFormula("Sheet1!$C1...B$4"); // different syntax for plain area ref
+               assertEquals("Sheet1!B1:$C$4", cell.getCellFormula());
+
+               // with funny sheet name
+               wb.setSheetName(0, "A1...A2");
+               cell.setCellFormula("A1...A2!B1");
+               assertEquals("A1...A2!B1", cell.getCellFormula());
        }
-}
\ No newline at end of file
+
+       public void testBooleanNamedSheet() {
+
+               HSSFWorkbook wb = new HSSFWorkbook();
+               HSSFSheet sheet = wb.createSheet("true");
+               HSSFCell cell = sheet.createRow(0).createCell(0);
+               cell.setCellFormula("'true'!B2");
+
+               assertEquals("'true'!B2", cell.getCellFormula());
+       }
+       
+       public void testParseExternalWorkbookReference() {
+               HSSFWorkbook wbA = HSSFTestDataSamples.openSampleWorkbook("multibookFormulaA.xls");
+               HSSFCell cell = wbA.getSheetAt(0).getRow(0).getCell(0);
+
+               // make sure formula in sample is as expected
+               assertEquals("[multibookFormulaB.xls]BSheet1!B1", cell.getCellFormula());
+               Ptg[] expectedPtgs = FormulaExtractor.getPtgs(cell);
+               confirmSingle3DRef(expectedPtgs, 1);
+               
+               // now try (re-)parsing the formula
+               Ptg[] actualPtgs = HSSFFormulaParser.parse("[multibookFormulaB.xls]BSheet1!B1", wbA);
+               confirmSingle3DRef(actualPtgs, 1); // externalSheetIndex 1 -> BSheet1
+               
+               // try parsing a formula pointing to a different external sheet
+               Ptg[] otherPtgs = HSSFFormulaParser.parse("[multibookFormulaB.xls]AnotherSheet!B1", wbA);
+               confirmSingle3DRef(otherPtgs, 0); // externalSheetIndex 0 -> AnotherSheet
+               
+               // try setting the same formula in a cell
+               cell.setCellFormula("[multibookFormulaB.xls]AnotherSheet!B1");
+               assertEquals("[multibookFormulaB.xls]AnotherSheet!B1", cell.getCellFormula());
+       }
+       private static void confirmSingle3DRef(Ptg[] ptgs, int expectedExternSheetIndex) {
+               assertEquals(1, ptgs.length);
+               Ptg ptg0 = ptgs[0];
+               assertEquals(Ref3DPtg.class, ptg0.getClass());
+               assertEquals(expectedExternSheetIndex, ((Ref3DPtg)ptg0).getExternSheetIndex());
+       }
+       
+}
index 3727989efb1670ca3004d78d72d16dedf1a2b513..e2a61dde79b32adc19fef584b5f2526903483adc 100644 (file)
@@ -78,7 +78,7 @@ public final class TestSupBookRecord extends TestCase {
 
         assertEquals( 34, record.getRecordSize() );  //sid+size+data
         
-        assertEquals("testURL", record.getURL().getString());
+        assertEquals("testURL", record.getURL());
         UnicodeString[] sheetNames = record.getSheetNames();
         assertEquals(2, sheetNames.length);
         assertEquals("Sheet1", sheetNames[0].getString());
index 369c09583a57777c427985c781c9dac1e5ce8a2b..b7c1664eeeac2e74373ebb69a87d8c1397bfe378 100644 (file)
@@ -20,16 +20,12 @@ package org.apache.poi.hssf.record.formula;
 import junit.framework.TestCase;
 
 /**
- * Tests for SheetNameFormatter
+ * Tests for {@link SheetNameFormatter}
  * 
  * @author Josh Micich
  */
 public final class TestSheetNameFormatter extends TestCase {
 
-       public TestSheetNameFormatter(String testName) {
-               super(testName);
-       }
-       
        private static void confirmFormat(String rawSheetName, String expectedSheetNameEncoding) {
                assertEquals(expectedSheetNameEncoding, SheetNameFormatter.format(rawSheetName));
        }
@@ -55,6 +51,16 @@ public final class TestSheetNameFormatter extends TestCase {
                confirmFormat("TAXRETURN19980415", "TAXRETURN19980415"); 
        }
        
+       public void testBooleanLiterals() {
+               confirmFormat("TRUE", "'TRUE'");
+               confirmFormat("FALSE", "'FALSE'");
+               confirmFormat("True", "'True'");
+               confirmFormat("fAlse", "'fAlse'");
+               
+               confirmFormat("Yes", "Yes");
+               confirmFormat("No", "No");
+       }
+       
        private static void confirmCellNameMatch(String rawSheetName, boolean expected) {
                assertEquals(expected, SheetNameFormatter.nameLooksLikePlainCellReference(rawSheetName));
        }
index 75510619420583e3cc3b749dc2e47fbd7ad53975..fe37a3c84b139f86b9f9c659d003882ae045769e 100755 (executable)
@@ -37,6 +37,7 @@ public class AllFormulaEvalTests {
                result.addTestSuite(TestFormulaBugs.class);
                result.addTestSuite(TestFormulasFromSpreadsheet.class);
                result.addTestSuite(TestPercentEval.class);
+               result.addTestSuite(TestRangeEval.class);
                result.addTestSuite(TestUnaryPlusEval.class);
                return result;
        }
index 45b0f3a659dc1699b52347118737cc81828fccfb..1b97905ed238bc2e8a47f5b06d4f18463ef01e1f 100755 (executable)
@@ -146,7 +146,7 @@ public final class TestFormulaBugs extends TestCase {
                        throw new AssertionFailedError("Identified bug 42448");
                }
 
-               assertEquals("SUMPRODUCT(A!C7:C67,B8:B68)/B69", cell.getCellFormula());
+               assertEquals("SUMPRODUCT(A!C7:A!C67,B8:B68)/B69", cell.getCellFormula());
 
                // might as well evaluate the sucker...
 
diff --git a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestRangeEval.java b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestRangeEval.java
new file mode 100644 (file)
index 0000000..e98517a
--- /dev/null
@@ -0,0 +1,95 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hssf.record.formula.eval;
+
+import org.apache.poi.hssf.record.formula.AreaI;
+import org.apache.poi.hssf.record.formula.AreaI.OffsetArea;
+import org.apache.poi.hssf.util.AreaReference;
+import org.apache.poi.hssf.util.CellReference;
+
+import junit.framework.TestCase;
+
+/**
+ * Test for unary plus operator evaluator.
+ *
+ * @author Josh Micich
+ */
+public final class TestRangeEval extends TestCase {
+       
+       public void testPermutations() {
+               
+               confirm("B3", "D7", "B3:D7");
+               confirm("B1", "B1", "B1:B1");
+               
+               confirm("B7", "D3", "B3:D7");
+               confirm("D3", "B7", "B3:D7");
+               confirm("D7", "B3", "B3:D7");
+       }
+
+       private static void confirm(String refA, String refB, String expectedAreaRef) {
+               
+               Eval[] args = {
+                       createRefEval(refA),    
+                       createRefEval(refB),    
+               };
+               AreaReference ar = new AreaReference(expectedAreaRef);
+               Eval result = RangeEval.instance.evaluate(args, 0, (short)0);
+               assertTrue(result instanceof AreaEval);
+               AreaEval ae = (AreaEval) result;
+               assertEquals(ar.getFirstCell().getRow(), ae.getFirstRow());
+               assertEquals(ar.getLastCell().getRow(), ae.getLastRow());
+               assertEquals(ar.getFirstCell().getCol(), ae.getFirstColumn());
+               assertEquals(ar.getLastCell().getCol(), ae.getLastColumn());
+       }
+
+       private static Eval createRefEval(String refStr) {
+               CellReference cr = new CellReference(refStr);
+               return new MockRefEval(cr.getRow(), cr.getCol());
+               
+       }
+
+       private static final class MockRefEval extends RefEvalBase {
+
+               public MockRefEval(int rowIndex, int columnIndex) {
+                       super(rowIndex, columnIndex);
+               }
+               public ValueEval getInnerValueEval() {
+                       throw new RuntimeException("not expected to be called during this test");
+               }
+               public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx,
+                               int relLastColIx) {
+                       AreaI area = new OffsetArea(getRow(), getColumn(),
+                                       relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);
+                       return new MockAreaEval(area);
+               }
+       }
+
+       private static final class MockAreaEval extends AreaEvalBase {
+
+               public MockAreaEval(AreaI ptg) {
+                       super(ptg);
+               }
+               public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) {
+                       throw new RuntimeException("not expected to be called during this test");
+               }
+               public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx,
+                               int relLastColIx) {
+                       throw new RuntimeException("not expected to be called during this test");
+               }
+       }
+}
index 397c1e52c9b5e339916f0ff4ae369dcc03c4aea0..03f47d681196fa467d0c82011f079c8960a8ad9e 100644 (file)
@@ -18,7 +18,6 @@
 package org.apache.poi.hssf.usermodel;
 
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.Date;
@@ -42,12 +41,8 @@ public final class TestFormulas extends TestCase {
     /**
      * Add 1+1 -- WHoohoo!
      */
+    public void testBasicAddIntegers() {
 
-    public void testBasicAddIntegers()
-    throws Exception {
-
-        File file = TempFile.createTempFile("testFormula",".xls");
-        FileOutputStream out    = new FileOutputStream(file);
         HSSFWorkbook     wb     = new HSSFWorkbook();
         HSSFSheet        s      = wb.createSheet();
         HSSFRow          r      = null;
@@ -58,95 +53,78 @@ public final class TestFormulas extends TestCase {
         c = r.createCell(1);
         c.setCellFormula(1 + "+" + 1);
 
-        wb.write(out);
-        out.close();
-
-        FileInputStream in = new FileInputStream(file);
-        wb = new HSSFWorkbook(in);
+        wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
         s  = wb.getSheetAt(0);
         r  = s.getRow(1);
         c  = r.getCell(1);
 
         assertTrue("Formula is as expected",("1+1".equals(c.getCellFormula())));
-        in.close();
     }
 
     /**
      * Add various integers
      */
-
-    public void testAddIntegers()
-    throws Exception {
+    public void testAddIntegers() {
         binomialOperator("+");
     }
 
     /**
      * Multiply various integers
      */
-
-    public void testMultplyIntegers()
-    throws Exception {
+    public void testMultplyIntegers() {
         binomialOperator("*");
     }
 
     /**
      * Subtract various integers
      */
-    public void testSubtractIntegers()
-    throws Exception {
+    public void testSubtractIntegers() {
         binomialOperator("-");
     }
 
     /**
      * Subtract various integers
      */
-    public void testDivideIntegers()
-    throws Exception {
+    public void testDivideIntegers() {
         binomialOperator("/");
     }
 
     /**
      * Exponentialize various integers;
      */
-    public void testPowerIntegers()
-    throws Exception {
+    public void testPowerIntegers() {
         binomialOperator("^");
     }
 
     /**
-     * Concatinate two numbers 1&2 = 12
+     * Concatenate two numbers 1&2 = 12
      */
-    public void testConcatIntegers()
-    throws Exception {
+    public void testConcatIntegers() {
         binomialOperator("&");
     }
 
     /**
      * tests 1*2+3*4
      */
-    public void testOrderOfOperationsMultiply()
-    throws Exception {
+    public void testOrderOfOperationsMultiply() {
         orderTest("1*2+3*4");
     }
 
     /**
      * tests 1*2+3^4
      */
-    public void testOrderOfOperationsPower()
-    throws Exception {
+    public void testOrderOfOperationsPower() {
         orderTest("1*2+3^4");
     }
 
     /**
      * Tests that parenthesis are obeyed
      */
-    public void testParenthesis()
-    throws Exception {
+    public void testParenthesis() {
         orderTest("(1*3)+2+(1+2)*(3^4)^5");
     }
 
-    public void testReferencesOpr()
-    throws Exception {
+    public void testReferencesOpr() {
         String[] operation = new String[] {
                             "+", "-", "*", "/", "^", "&"
                            };
@@ -159,16 +137,12 @@ public final class TestFormulas extends TestCase {
      * Tests creating a file with floating point in a formula.
      *
      */
-    public void testFloat()
-    throws Exception {
+    public void testFloat() {
         floatTest("*");
         floatTest("/");
     }
 
-    private void floatTest(String operator)
-    throws Exception {
-        File file = TempFile.createTempFile("testFormulaFloat",".xls");
-        FileOutputStream out    = new FileOutputStream(file);
+    private static void floatTest(String operator) {
         HSSFWorkbook     wb     = new HSSFWorkbook();
         HSSFSheet        s      = wb.createSheet();
         HSSFRow          r      = null;
@@ -180,10 +154,10 @@ public final class TestFormulas extends TestCase {
         c = r.createCell(1);
         c.setCellFormula(""+Float.MIN_VALUE + operator + Float.MIN_VALUE);
 
-       for (short x = 1; x < Short.MAX_VALUE && x > 0; x=(short)(x*2) ) {
+       for (int x = 1; x < Short.MAX_VALUE && x > 0; x=(short)(x*2) ) {
             r = s.createRow(x);
 
-            for (short y = 1; y < 256 && y > 0; y= (short) (y +2)) {
+            for (int y = 1; y < 256 && y > 0; y= (short) (y +2)) {
 
                 c = r.createCell(y);
                 c.setCellFormula("" + x+"."+y + operator + y +"."+x);
@@ -196,69 +170,48 @@ public final class TestFormulas extends TestCase {
             c = r.createCell(0);
             c.setCellFormula("" + Float.MAX_VALUE + operator + Float.MAX_VALUE);
         }
-        wb.write(out);
-        out.close();
-        assertTrue("file exists",file.exists());
-        out=null;wb=null;  //otherwise we get out of memory error!
-        floatVerify(operator,file);
+        wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
 
+        floatVerify(operator, wb);
     }
 
-            private void floatVerify(String operator, File file)
-    throws Exception {
+    private static void floatVerify(String operator, HSSFWorkbook wb) {
 
-        FileInputStream  in     = new FileInputStream(file);
-        HSSFWorkbook     wb     = new HSSFWorkbook(in);
         HSSFSheet        s      = wb.getSheetAt(0);
-        HSSFRow          r      = null;
-        HSSFCell         c      = null;
 
-        // dont know how to check correct result .. for the moment, we just verify that the file can be read.
+        // don't know how to check correct result .. for the moment, we just verify that the file can be read.
 
-        for (short x = 1; x < Short.MAX_VALUE && x > 0; x=(short)(x*2)) {
-            r = s.getRow(x);
+        for (int x = 1; x < Short.MAX_VALUE && x > 0; x=(short)(x*2)) {
+               HSSFRow r = s.getRow(x);
 
-            for (short y = 1; y < 256 && y > 0; y=(short)(y+2)) {
+            for (int y = 1; y < 256 && y > 0; y=(short)(y+2)) {
 
-                c = r.getCell(y);
+               HSSFCell c = r.getCell(y);
                 assertTrue("got a formula",c.getCellFormula()!=null);
 
                 assertTrue("loop Formula is as expected "+x+"."+y+operator+y+"."+x+"!="+c.getCellFormula(),(
                 (""+x+"."+y+operator+y+"."+x).equals(c.getCellFormula()) ));
-
             }
         }
-
-       in.close();
-       assertTrue("file exists",file.exists());
     }
 
-    public void testAreaSum()
-    throws Exception {
+    public void testAreaSum() {
         areaFunctionTest("SUM");
     }
 
-    public void testAreaAverage()
-    throws Exception {
+    public void testAreaAverage() {
         areaFunctionTest("AVERAGE");
     }
 
-    public void testRefArraySum()
-    throws Exception {
+    public void testRefArraySum() {
         refArrayFunctionTest("SUM");
     }
 
-    public void testAreaArraySum()
-    throws Exception {
+    public void testAreaArraySum() {
         refAreaArrayFunctionTest("SUM");
     }
 
-
-
-    private void operationRefTest(String operator)
-    throws Exception {
-        File file = TempFile.createTempFile("testFormula",".xls");
-        FileOutputStream out    = new FileOutputStream(file);
+    private static void operationRefTest(String operator) {
         HSSFWorkbook     wb     = new HSSFWorkbook();
         HSSFSheet        s      = wb.createSheet();
         HSSFRow          r      = null;
@@ -317,21 +270,16 @@ public final class TestFormulas extends TestCase {
             c.setCellFormula("" + "B1" + operator + "IV255");
         }
 
-        wb.write(out);
-        out.close();
-        assertTrue("file exists",file.exists());
-        operationalRefVerify(operator,file);
+        wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
+        operationalRefVerify(operator, wb);
     }
 
     /**
      * Opens the sheet we wrote out by binomialOperator and makes sure the formulas
      * all match what we expect (x operator y)
      */
-    private void operationalRefVerify(String operator, File file)
-    throws Exception {
+    private static void operationalRefVerify(String operator, HSSFWorkbook wb) {
 
-        FileInputStream  in     = new FileInputStream(file);
-        HSSFWorkbook     wb     = new HSSFWorkbook(in);
         HSSFSheet        s      = wb.getSheetAt(0);
         HSSFRow          r      = null;
         HSSFCell         c      = null;
@@ -345,46 +293,43 @@ public final class TestFormulas extends TestCase {
         ));
 
 
-        for (short x = 1; x < Short.MAX_VALUE && x > 0; x=(short)(x*2)) {
+        for (int x = 1; x < Short.MAX_VALUE && x > 0; x=(short)(x*2)) {
             r = s.getRow(x);
 
-            for (short y = 1; y < 256 && y > 0; y++) {
+            for (int y = 1; y < 256 && y > 0; y++) {
 
-                String ref=null;
-                String ref2=null;
-                short refx1=0;
-                short refy1=0;
-                short refx2=0;
-                short refy2=0;
+                int refx1;
+                int refy1;
+                int refx2;
+                int refy2;
                 if (x +50 < Short.MAX_VALUE) {
-                    refx1=(short)(x+50);
-                    refx2=(short)(x+46);
+                    refx1=x+50;
+                    refx2=x+46;
                 } else {
-                    refx1=(short)(x-4);
-                    refx2=(short)(x-3);
+                    refx1=x-4;
+                    refx2=x-3;
                 }
 
                 if (y+50 < 255) {
-                    refy1=(short)(y+50);
-                    refy2=(short)(y+49);
+                    refy1=y+50;
+                    refy2=y+49;
                 } else {
-                    refy1=(short)(y-4);
-                    refy2=(short)(y-3);
+                    refy1=y-4;
+                    refy2=y-3;
                 }
 
                 c = r.getCell(y);
                 CellReference cr= new CellReference(refx1, refy1, false, false);
+                String ref=cr.formatAsString();
                 ref=cr.formatAsString();
                 cr=new CellReference(refx2,refy2, false, false);
-                ref2=cr.formatAsString();
+                String ref2=cr.formatAsString();
 
 
                 assertTrue("loop Formula is as expected "+ref+operator+ref2+"!="+c.getCellFormula(),(
                 (""+ref+operator+ref2).equals(c.getCellFormula())
                                                          )
                 );
-
-
             }
         }
 
@@ -392,13 +337,7 @@ public final class TestFormulas extends TestCase {
         r = s.getRow(0);
         c = r.getCell(0);
 
-        assertTrue("maxval Formula is as expected",(
-        ("B1"+operator+"IV255").equals(c.getCellFormula())
-                                                   )
-        );
-
-        in.close();
-        assertTrue("file exists",file.exists());
+        assertEquals("B1"+operator+"IV255", c.getCellFormula());
     }
 
 
@@ -406,10 +345,7 @@ public final class TestFormulas extends TestCase {
     /**
      * tests order wrting out == order writing in for a given formula
      */
-    private void orderTest(String formula)
-    throws Exception {
-        File file = TempFile.createTempFile("testFormula",".xls");
-        FileOutputStream out    = new FileOutputStream(file);
+    private static void orderTest(String formula) {
         HSSFWorkbook     wb     = new HSSFWorkbook();
         HSSFSheet        s      = wb.createSheet();
         HSSFRow          r      = null;
@@ -420,12 +356,7 @@ public final class TestFormulas extends TestCase {
         c = r.createCell(1);
         c.setCellFormula(formula);
 
-        wb.write(out);
-        out.close();
-        assertTrue("file exists",file.exists());
-
-        FileInputStream  in     = new FileInputStream(file);
-        wb     = new HSSFWorkbook(in);
+        wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
         s      = wb.getSheetAt(0);
 
         //get our minimum values
@@ -434,8 +365,6 @@ public final class TestFormulas extends TestCase {
         assertTrue("minval Formula is as expected",
                    formula.equals(c.getCellFormula())
                   );
-
-        in.close();
     }
 
     /**
@@ -443,10 +372,7 @@ public final class TestFormulas extends TestCase {
      * huge set of x operator y formulas.  Next we call binomialVerify and verify
      * that they are all how we expect.
      */
-    private void binomialOperator(String operator)
-    throws Exception {
-        File file = TempFile.createTempFile("testFormula",".xls");
-        FileOutputStream out    = new FileOutputStream(file);
+    private static void binomialOperator(String operator) {
         HSSFWorkbook     wb     = new HSSFWorkbook();
         HSSFSheet        s      = wb.createSheet();
         HSSFRow          r      = null;
@@ -474,23 +400,15 @@ public final class TestFormulas extends TestCase {
             c = r.createCell(0);
             c.setCellFormula("" + Short.MAX_VALUE + operator + Short.MAX_VALUE);
         }
-
-        wb.write(out);
-        out.close();
-        assertTrue("file exists",file.exists());
-
-        binomialVerify(operator,file);
+        wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
+        binomialVerify(operator, wb);
     }
 
     /**
      * Opens the sheet we wrote out by binomialOperator and makes sure the formulas
      * all match what we expect (x operator y)
      */
-    private void binomialVerify(String operator, File file)
-    throws Exception {
-
-        FileInputStream  in     = new FileInputStream(file);
-        HSSFWorkbook     wb     = new HSSFWorkbook(in);
+    private static void binomialVerify(String operator, HSSFWorkbook wb) {
         HSSFSheet        s      = wb.getSheetAt(0);
         HSSFRow          r      = null;
         HSSFCell         c      = null;
@@ -502,10 +420,10 @@ public final class TestFormulas extends TestCase {
         ( ("1"+operator+"1").equals(c.getCellFormula())
         ));
 
-        for (short x = 1; x < Short.MAX_VALUE && x > 0; x=(short)(x*2)) {
+        for (int x = 1; x < Short.MAX_VALUE && x > 0; x=(short)(x*2)) {
             r = s.getRow(x);
 
-            for (short y = 1; y < 256 && y > 0; y++) {
+            for (int y = 1; y < 256 && y > 0; y++) {
 
                 c = r.getCell(y);
 
@@ -513,8 +431,6 @@ public final class TestFormulas extends TestCase {
                 (""+x+operator+y).equals(c.getCellFormula())
                                                          )
                 );
-
-
             }
         }
 
@@ -522,90 +438,62 @@ public final class TestFormulas extends TestCase {
         r = s.getRow(0);
         c = r.getCell(0);
 
-
         assertTrue("maxval Formula is as expected",(
         (""+Short.MAX_VALUE+operator+Short.MAX_VALUE).equals(c.getCellFormula())
                                                    )
         );
-
-        in.close();
-        assertTrue("file exists",file.exists());
     }
 
-
-
     /**
      * Writes a function then tests to see if its correct
-     *
      */
-    public void areaFunctionTest(String function)
-    throws Exception {
-
-            File file = TempFile.createTempFile("testFormulaAreaFunction"+function,".xls");
-            FileOutputStream out    = new FileOutputStream(file);
-            HSSFWorkbook     wb     = new HSSFWorkbook();
-            HSSFSheet        s      = wb.createSheet();
-            HSSFRow          r      = null;
-            HSSFCell         c      = null;
-
+    public static void areaFunctionTest(String function) {
 
-            r = s.createRow(0);
+        HSSFWorkbook     wb     = new HSSFWorkbook();
+        HSSFSheet        s      = wb.createSheet();
+        HSSFRow          r      = null;
+        HSSFCell         c      = null;
 
-            c = r.createCell(0);
-            c.setCellFormula(function+"(A2:A3)");
 
+        r = s.createRow(0);
 
-            wb.write(out);
-            out.close();
-            assertTrue("file exists",file.exists());
+        c = r.createCell(0);
+        c.setCellFormula(function+"(A2:A3)");
 
-            FileInputStream in = new FileInputStream(file);
-            wb = new HSSFWorkbook(in);
-            s = wb.getSheetAt(0);
-            r = s.getRow(0);
-            c = r.getCell(0);
+        wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
+        s = wb.getSheetAt(0);
+        r = s.getRow(0);
+        c = r.getCell(0);
 
-            assertTrue("function ="+function+"(A2:A3)",
-                        ( (function+"(A2:A3)").equals((function+"(A2:A3)")) )
-                      );
-            in.close();
+        assertTrue("function ="+function+"(A2:A3)",
+                    ( (function+"(A2:A3)").equals((function+"(A2:A3)")) )
+                  );
     }
 
     /**
      * Writes a function then tests to see if its correct
-     *
      */
-    public void refArrayFunctionTest(String function)
-    throws Exception {
+    public void refArrayFunctionTest(String function) {
 
-            File file = TempFile.createTempFile("testFormulaArrayFunction"+function,".xls");
-            FileOutputStream out    = new FileOutputStream(file);
-            HSSFWorkbook     wb     = new HSSFWorkbook();
-            HSSFSheet        s      = wb.createSheet();
-            HSSFRow          r      = null;
-            HSSFCell         c      = null;
-
-
-            r = s.createRow(0);
+        HSSFWorkbook     wb     = new HSSFWorkbook();
+        HSSFSheet        s      = wb.createSheet();
+        HSSFRow          r      = null;
+        HSSFCell         c      = null;
 
-            c = r.createCell(0);
-            c.setCellFormula(function+"(A2,A3)");
 
+        r = s.createRow(0);
 
-            wb.write(out);
-            out.close();
-            assertTrue("file exists",file.exists());
+        c = r.createCell(0);
+        c.setCellFormula(function+"(A2,A3)");
 
-            FileInputStream in = new FileInputStream(file);
-            wb = new HSSFWorkbook(in);
-            s = wb.getSheetAt(0);
-            r = s.getRow(0);
-            c = r.getCell(0);
+        wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
+        s = wb.getSheetAt(0);
+        r = s.getRow(0);
+        c = r.getCell(0);
 
-            assertTrue("function ="+function+"(A2,A3)",
-                        ( (function+"(A2,A3)").equals(c.getCellFormula()) )
-                      );
-            in.close();
+        assertTrue("function ="+function+"(A2,A3)",
+                    ( (function+"(A2,A3)").equals(c.getCellFormula()) )
+                  );
     }
 
 
@@ -613,294 +501,224 @@ public final class TestFormulas extends TestCase {
      * Writes a function then tests to see if its correct
      *
      */
-    public void refAreaArrayFunctionTest(String function)
-    throws Exception {
-
-            File file = TempFile.createTempFile("testFormulaAreaArrayFunction"+function,".xls");
-            FileOutputStream out    = new FileOutputStream(file);
-            HSSFWorkbook     wb     = new HSSFWorkbook();
-            HSSFSheet        s      = wb.createSheet();
-            HSSFRow          r      = null;
-            HSSFCell         c      = null;
+    public void refAreaArrayFunctionTest(String function) {
 
+        HSSFWorkbook     wb     = new HSSFWorkbook();
+        HSSFSheet        s      = wb.createSheet();
+        HSSFRow          r      = null;
+        HSSFCell         c      = null;
 
-            r = s.createRow(0);
 
-            c = r.createCell(0);
-            c.setCellFormula(function+"(A2:A4,B2:B4)");
-            c=r.createCell(1);
-            c.setCellFormula(function+"($A$2:$A4,B$2:B4)");
+        r = s.createRow(0);
 
-            wb.write(out);
-            out.close();
-            assertTrue("file exists",file.exists());
+        c = r.createCell(0);
+        c.setCellFormula(function+"(A2:A4,B2:B4)");
+        c=r.createCell(1);
+        c.setCellFormula(function+"($A$2:$A4,B$2:B4)");
 
-            FileInputStream in = new FileInputStream(file);
-            wb = new HSSFWorkbook(in);
-            s = wb.getSheetAt(0);
-            r = s.getRow(0);
-            c = r.getCell(0);
+        wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
+        s = wb.getSheetAt(0);
+        r = s.getRow(0);
+        c = r.getCell(0);
 
-            assertTrue("function ="+function+"(A2:A4,B2:B4)",
-                        ( (function+"(A2:A4,B2:B4)").equals(c.getCellFormula()) )
-                      );
+        assertTrue("function ="+function+"(A2:A4,B2:B4)",
+                    ( (function+"(A2:A4,B2:B4)").equals(c.getCellFormula()) )
+                  );
 
-            c=r.getCell(1);
-             assertTrue("function ="+function+"($A$2:$A4,B$2:B4)",
-                        ( (function+"($A$2:$A4,B$2:B4)").equals(c.getCellFormula()) )
-                      );
-            in.close();
+        c=r.getCell(1);
+        assertTrue("function ="+function+"($A$2:$A4,B$2:B4)",
+                    ( (function+"($A$2:$A4,B$2:B4)").equals(c.getCellFormula()) )
+                  );
     }
 
 
 
-    public void testAbsRefs() throws Exception {
-            File file = TempFile.createTempFile("testFormulaAbsRef",".xls");
-            FileOutputStream out    = new FileOutputStream(file);
-            HSSFWorkbook     wb     = new HSSFWorkbook();
-            HSSFSheet        s      = wb.createSheet();
-            HSSFRow          r      = null;
-            HSSFCell         c      = null;
+    public void testAbsRefs() {
+        HSSFWorkbook wb = new HSSFWorkbook();
+               HSSFSheet s = wb.createSheet();
+               HSSFRow r;
+               HSSFCell c;
 
+        r = s.createRow(0);
+        c = r.createCell(0);
+        c.setCellFormula("A3+A2");
+        c=r.createCell(1);
+        c.setCellFormula("$A3+$A2");
+        c=r.createCell(2);
+        c.setCellFormula("A$3+A$2");
+        c=r.createCell(3);
+        c.setCellFormula("$A$3+$A$2");
+        c=r.createCell(4);
+        c.setCellFormula("SUM($A$3,$A$2)");
+
+        wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
+        s = wb.getSheetAt(0);
+        r = s.getRow(0);
+        c = r.getCell(0);
+        assertTrue("A3+A2", ("A3+A2").equals(c.getCellFormula()));
+         c = r.getCell(1);
+        assertTrue("$A3+$A2", ("$A3+$A2").equals(c.getCellFormula()));
+         c = r.getCell(2);
+        assertTrue("A$3+A$2", ("A$3+A$2").equals(c.getCellFormula()));
+         c = r.getCell(3);
+        assertTrue("$A$3+$A$2", ("$A$3+$A$2").equals(c.getCellFormula()));
+         c = r.getCell(4);
+        assertTrue("SUM($A$3,$A$2)", ("SUM($A$3,$A$2)").equals(c.getCellFormula()));
+    }
+
+    public void testSheetFunctions() {
+        HSSFWorkbook     wb     = new HSSFWorkbook();
+        HSSFSheet        s      = wb.createSheet("A");
+        HSSFRow          r      = null;
+        HSSFCell         c      = null;
+        r = s.createRow(0);
+        c = r.createCell(0);c.setCellValue(1);
+        c = r.createCell(1);c.setCellValue(2);
 
-            r = s.createRow(0);
+        s      = wb.createSheet("B");
+        r = s.createRow(0);
+        c=r.createCell(0); c.setCellFormula("AVERAGE(A!A1:B1)");
+        c=r.createCell(1); c.setCellFormula("A!A1+A!B1");
+        c=r.createCell(2); c.setCellFormula("A!$A$1+A!$B1");
 
-            c = r.createCell(0);
-            c.setCellFormula("A3+A2");
-            c=r.createCell(1);
-            c.setCellFormula("$A3+$A2");
-            c=r.createCell(2);
-            c.setCellFormula("A$3+A$2");
-            c=r.createCell(3);
-            c.setCellFormula("$A$3+$A$2");
-            c=r.createCell(4);
-            c.setCellFormula("SUM($A$3,$A$2)");
-
-            wb.write(out);
-            out.close();
-            assertTrue("file exists",file.exists());
-
-            FileInputStream in = new FileInputStream(file);
-            wb = new HSSFWorkbook(in);
-            s = wb.getSheetAt(0);
-            r = s.getRow(0);
-            c = r.getCell(0);
-            assertTrue("A3+A2", ("A3+A2").equals(c.getCellFormula()));
-             c = r.getCell(1);
-            assertTrue("$A3+$A2", ("$A3+$A2").equals(c.getCellFormula()));
-             c = r.getCell(2);
-            assertTrue("A$3+A$2", ("A$3+A$2").equals(c.getCellFormula()));
-             c = r.getCell(3);
-            assertTrue("$A$3+$A$2", ("$A$3+$A$2").equals(c.getCellFormula()));
-             c = r.getCell(4);
-            assertTrue("SUM($A$3,$A$2)", ("SUM($A$3,$A$2)").equals(c.getCellFormula()));
-            in.close();
+        wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
+
+        s = wb.getSheet("B");
+        r = s.getRow(0);
+        c = r.getCell(0);
+        assertTrue("expected: AVERAGE(A!A1:B1) got: "+c.getCellFormula(), ("AVERAGE(A!A1:B1)").equals(c.getCellFormula()));
+        c = r.getCell(1);
+        assertTrue("expected: A!A1+A!B1 got: "+c.getCellFormula(), ("A!A1+A!B1").equals(c.getCellFormula()));
     }
 
-    public void testSheetFunctions()
-        throws IOException
-    {
-            File file = TempFile.createTempFile("testSheetFormula",".xls");
-            FileOutputStream out    = new FileOutputStream(file);
-            HSSFWorkbook     wb     = new HSSFWorkbook();
-            HSSFSheet        s      = wb.createSheet("A");
-            HSSFRow          r      = null;
-            HSSFCell         c      = null;
-            r = s.createRow(0);
-            c = r.createCell(0);c.setCellValue(1);
-            c = r.createCell(1);c.setCellValue(2);
+    public void testRVAoperands() throws Exception {
+        File file = TempFile.createTempFile("testFormulaRVA",".xls");
+        FileOutputStream out    = new FileOutputStream(file);
+        HSSFWorkbook     wb     = new HSSFWorkbook();
+        HSSFSheet        s      = wb.createSheet();
+        HSSFRow          r      = null;
+        HSSFCell         c      = null;
 
-            s      = wb.createSheet("B");
-            r = s.createRow(0);
-            c=r.createCell(0); c.setCellFormula("AVERAGE(A!A1:B1)");
-            c=r.createCell(1); c.setCellFormula("A!A1+A!B1");
-            c=r.createCell(2); c.setCellFormula("A!$A$1+A!$B1");
-            wb.write(out);
-            out.close();
 
-             assertTrue("file exists",file.exists());
+        r = s.createRow(0);
 
-            FileInputStream in = new FileInputStream(file);
-            wb = new HSSFWorkbook(in);
-            s = wb.getSheet("B");
-            r = s.getRow(0);
-            c = r.getCell(0);
-            assertTrue("expected: AVERAGE(A!A1:B1) got: "+c.getCellFormula(), ("AVERAGE(A!A1:B1)").equals(c.getCellFormula()));
-            c = r.getCell(1);
-            assertTrue("expected: A!A1+A!B1 got: "+c.getCellFormula(), ("A!A1+A!B1").equals(c.getCellFormula()));
-            in.close();
-    }
+        c = r.createCell(0);
+        c.setCellFormula("A3+A2");
+        c=r.createCell(1);
+        c.setCellFormula("AVERAGE(A3,A2)");
+        c=r.createCell(2);
+        c.setCellFormula("ROW(A3)");
+        c=r.createCell(3);
+        c.setCellFormula("AVERAGE(A2:A3)");
+        c=r.createCell(4);
+        c.setCellFormula("POWER(A2,A3)");
+        c=r.createCell(5);
+        c.setCellFormula("SIN(A2)");
 
-    public void testRVAoperands() throws Exception {
-         File file = TempFile.createTempFile("testFormulaRVA",".xls");
-            FileOutputStream out    = new FileOutputStream(file);
-            HSSFWorkbook     wb     = new HSSFWorkbook();
-            HSSFSheet        s      = wb.createSheet();
-            HSSFRow          r      = null;
-            HSSFCell         c      = null;
+        c=r.createCell(6);
+        c.setCellFormula("SUM(A2:A3)");
 
+        c=r.createCell(7);
+        c.setCellFormula("SUM(A2,A3)");
 
-            r = s.createRow(0);
+        r = s.createRow(1);c=r.createCell(0); c.setCellValue(2.0);
+         r = s.createRow(2);c=r.createCell(0); c.setCellValue(3.0);
 
-            c = r.createCell(0);
-            c.setCellFormula("A3+A2");
-            c=r.createCell(1);
-            c.setCellFormula("AVERAGE(A3,A2)");
-            c=r.createCell(2);
-            c.setCellFormula("ROW(A3)");
-            c=r.createCell(3);
-            c.setCellFormula("AVERAGE(A2:A3)");
-            c=r.createCell(4);
-            c.setCellFormula("POWER(A2,A3)");
-            c=r.createCell(5);
-            c.setCellFormula("SIN(A2)");
-
-            c=r.createCell(6);
-            c.setCellFormula("SUM(A2:A3)");
-
-            c=r.createCell(7);
-            c.setCellFormula("SUM(A2,A3)");
-
-            r = s.createRow(1);c=r.createCell(0); c.setCellValue(2.0);
-             r = s.createRow(2);c=r.createCell(0); c.setCellValue(3.0);
-
-            wb.write(out);
-            out.close();
-            assertTrue("file exists",file.exists());
+        wb.write(out);
+        out.close();
+        assertTrue("file exists",file.exists());
     }
 
-    public void testStringFormulas()
-        throws IOException
-    {
-            File file = TempFile.createTempFile("testStringFormula",".xls");
-            FileOutputStream out    = new FileOutputStream(file);
-            HSSFWorkbook     wb     = new HSSFWorkbook();
-            HSSFSheet        s      = wb.createSheet("A");
-            HSSFRow          r      = null;
-            HSSFCell         c      = null;
-            r = s.createRow(0);
-            c=r.createCell(1); c.setCellFormula("UPPER(\"abc\")");
-            c=r.createCell(2); c.setCellFormula("LOWER(\"ABC\")");
-            c=r.createCell(3); c.setCellFormula("CONCATENATE(\" my \",\" name \")");
+    public void testStringFormulas() {
+        HSSFWorkbook     wb     = new HSSFWorkbook();
+        HSSFSheet        s      = wb.createSheet("A");
+        HSSFRow          r      = null;
+        HSSFCell         c      = null;
+        r = s.createRow(0);
+        c=r.createCell(1); c.setCellFormula("UPPER(\"abc\")");
+        c=r.createCell(2); c.setCellFormula("LOWER(\"ABC\")");
+        c=r.createCell(3); c.setCellFormula("CONCATENATE(\" my \",\" name \")");
 
-            wb.write(out);
-            out.close();
+        HSSFTestDataSamples.writeOutAndReadBack(wb);
 
-            wb = openSample("StringFormulas.xls");
-            s = wb.getSheetAt(0);
-            r = s.getRow(0);
-            c = r.getCell(0);
-            assertTrue("expected: UPPER(\"xyz\") got "+c.getCellFormula(), ("UPPER(\"xyz\")").equals(c.getCellFormula()));
-            //c = r.getCell((short)1);
-            //assertTrue("expected: A!A1+A!B1 got: "+c.getCellFormula(), ("A!A1+A!B1").equals(c.getCellFormula()));
+        wb = openSample("StringFormulas.xls");
+        s = wb.getSheetAt(0);
+        r = s.getRow(0);
+        c = r.getCell(0);
+        assertEquals("UPPER(\"xyz\")", c.getCellFormula());
     }
 
+    public void testLogicalFormulas() {
 
+        HSSFWorkbook     wb     = new HSSFWorkbook();
+        HSSFSheet        s      = wb.createSheet("A");
+        HSSFRow          r      = null;
+        HSSFCell         c      = null;
+        r = s.createRow(0);
+        c=r.createCell(1); c.setCellFormula("IF(A1<A2,B1,B2)");
 
-    public void testLogicalFormulas()
-        throws IOException
-    {
-
-            File file = TempFile.createTempFile("testLogicalFormula",".xls");
-            FileOutputStream out    = new FileOutputStream(file);
-            HSSFWorkbook     wb     = new HSSFWorkbook();
-            HSSFSheet        s      = wb.createSheet("A");
-            HSSFRow          r      = null;
-            HSSFCell         c      = null;
-            r = s.createRow(0);
-            c=r.createCell(1); c.setCellFormula("IF(A1<A2,B1,B2)");
+        wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
+        s = wb.getSheetAt(0);
+        r = s.getRow(0);
+        c = r.getCell(1);
+        assertEquals("Formula in cell 1 ","IF(A1<A2,B1,B2)",c.getCellFormula());
+    }
 
+    public void testDateFormulas() {
+        HSSFWorkbook     wb     = new HSSFWorkbook();
+        HSSFSheet        s      = wb.createSheet("testSheet1");
+        HSSFRow          r      = null;
+        HSSFCell         c      = null;
 
-            wb.write(out);
-            out.close();
+        r = s.createRow(0 );
+        c = r.createCell(0 );
 
-             assertTrue("file exists",file.exists());
+        HSSFCellStyle cellStyle = wb.createCellStyle();
+        cellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat("m/d/yy h:mm"));
+        c.setCellValue(new Date());
+        c.setCellStyle(cellStyle);
 
-            FileInputStream in = new FileInputStream(file);
-            wb = new HSSFWorkbook(in);
-            s = wb.getSheetAt(0);
-            r = s.getRow(0);
-            c = r.getCell(1);
-            assertEquals("Formula in cell 1 ","IF(A1<A2,B1,B2)",c.getCellFormula());
-            in.close();
-    }
+       // assertEquals("Checking hour = " + hour, date.getTime().getTime(),
+       //              HSSFDateUtil.getJavaDate(excelDate).getTime());
 
-    public void testDateFormulas()
-        throws IOException
-    {
-            File file = TempFile.createTempFile("testDateFormula",".xls");
-            FileOutputStream out    = new FileOutputStream(file);
-            HSSFWorkbook     wb     = new HSSFWorkbook();
-            HSSFSheet        s      = wb.createSheet("testSheet1");
-            HSSFRow          r      = null;
-            HSSFCell         c      = null;
-
-            r = s.createRow(0 );
-            c = r.createCell(0 );
-
-            HSSFCellStyle cellStyle = wb.createCellStyle();
-            cellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat("m/d/yy h:mm"));
-            c.setCellValue(new Date());
-            c.setCellStyle(cellStyle);
-
-           // assertEquals("Checking hour = " + hour, date.getTime().getTime(),
-           //              HSSFDateUtil.getJavaDate(excelDate).getTime());
-
-            for (int k=1; k < 100; k++) {
-              r=s.createRow(k);
-              c=r.createCell(0);
-              c.setCellFormula("A"+(k)+"+1");
-              c.setCellStyle(cellStyle);
-            }
+        for (int k=1; k < 100; k++) {
+          r=s.createRow(k);
+          c=r.createCell(0);
+          c.setCellFormula("A"+(k)+"+1");
+          c.setCellStyle(cellStyle);
+        }
 
-            wb.write(out);
-            out.close();
+        HSSFTestDataSamples.writeOutAndReadBack(wb);
+    }
 
-            assertTrue("file exists",file.exists());
 
-    }
+    public void testIfFormulas() {
+        HSSFWorkbook     wb     = new HSSFWorkbook();
+        HSSFSheet        s      = wb.createSheet("testSheet1");
+        HSSFRow          r      = null;
+        HSSFCell         c      = null;
+        r = s.createRow(0);
+        c=r.createCell(1); c.setCellValue(1);
+        c=r.createCell(2); c.setCellValue(2);
+        c=r.createCell(3); c.setCellFormula("MAX(A1:B1)");
+        c=r.createCell(4); c.setCellFormula("IF(A1=D1,\"A1\",\"B1\")");
 
+        wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
+        s = wb.getSheetAt(0);
+        r = s.getRow(0);
+        c = r.getCell(4);
 
-    public void testIfFormulas()
-        throws IOException
-    {
-            File file = TempFile.createTempFile("testIfFormula",".xls");
-            FileOutputStream out    = new FileOutputStream(file);
-            HSSFWorkbook     wb     = new HSSFWorkbook();
-            HSSFSheet        s      = wb.createSheet("testSheet1");
-            HSSFRow          r      = null;
-            HSSFCell         c      = null;
-            r = s.createRow(0);
-            c=r.createCell(1); c.setCellValue(1);
-            c=r.createCell(2); c.setCellValue(2);
-            c=r.createCell(3); c.setCellFormula("MAX(A1:B1)");
-            c=r.createCell(4); c.setCellFormula("IF(A1=D1,\"A1\",\"B1\")");
+        assertTrue("expected: IF(A1=D1,\"A1\",\"B1\") got "+c.getCellFormula(), ("IF(A1=D1,\"A1\",\"B1\")").equals(c.getCellFormula()));
 
-            wb.write(out);
-            out.close();
+        wb = openSample("IfFormulaTest.xls");
+        s = wb.getSheetAt(0);
+        r = s.getRow(3);
+        c = r.getCell(0);
+        assertTrue("expected: IF(A3=A1,\"A1\",\"A2\") got "+c.getCellFormula(), ("IF(A3=A1,\"A1\",\"A2\")").equals(c.getCellFormula()));
+        //c = r.getCell((short)1);
+        //assertTrue("expected: A!A1+A!B1 got: "+c.getCellFormula(), ("A!A1+A!B1").equals(c.getCellFormula()));
 
-            assertTrue("file exists",file.exists());
 
-            FileInputStream in = new FileInputStream(file);
-            wb = new HSSFWorkbook(in);
-            s = wb.getSheetAt(0);
-            r = s.getRow(0);
-            c = r.getCell(4);
-
-            assertTrue("expected: IF(A1=D1,\"A1\",\"B1\") got "+c.getCellFormula(), ("IF(A1=D1,\"A1\",\"B1\")").equals(c.getCellFormula()));
-            in.close();
-
-            wb = openSample("IfFormulaTest.xls");
-            s = wb.getSheetAt(0);
-            r = s.getRow(3);
-            c = r.getCell(0);
-            assertTrue("expected: IF(A3=A1,\"A1\",\"A2\") got "+c.getCellFormula(), ("IF(A3=A1,\"A1\",\"A2\")").equals(c.getCellFormula()));
-            //c = r.getCell((short)1);
-            //assertTrue("expected: A!A1+A!B1 got: "+c.getCellFormula(), ("A!A1+A!B1").equals(c.getCellFormula()));
-            in.close();
-
-        File simpleIf = TempFile.createTempFile("testSimpleIfFormulaWrite",".xls");
-        out    = new FileOutputStream(simpleIf);
         wb     = new HSSFWorkbook();
         s      = wb.createSheet("testSheet1");
         r      = null;
@@ -908,14 +726,8 @@ public final class TestFormulas extends TestCase {
         r = s.createRow(0);
         c=r.createCell(0); c.setCellFormula("IF(1=1,0,1)");
 
-        wb.write(out);
-        out.close();
-        assertTrue("file exists", simpleIf.exists());
-
-        assertTrue("length of simpleIf file is zero", (simpleIf.length()>0));
+        HSSFTestDataSamples.writeOutAndReadBack(wb);
 
-        File nestedIf = TempFile.createTempFile("testNestedIfFormula",".xls");
-        out    = new FileOutputStream(nestedIf);
         wb     = new HSSFWorkbook();
         s      = wb.createSheet("testSheet1");
         r      = null;
@@ -939,17 +751,10 @@ public final class TestFormulas extends TestCase {
 
         formulaCell.setCellFormula("IF(A1=B1,AVERAGE(A1:B1),AVERAGE(A2:B2))");
 
-
-        wb.write(out);
-        out.close();
-        assertTrue("file exists", nestedIf.exists());
-
-        assertTrue("length of nestedIf file is zero", (nestedIf.length()>0));
+        HSSFTestDataSamples.writeOutAndReadBack(wb);
     }
 
-    public void testSumIf()
-        throws IOException
-    {
+    public void testSumIf() {
         String function ="SUMIF(A1:A5,\">4000\",B1:B5)";
 
         HSSFWorkbook wb = openSample("sumifformula.xls");
@@ -960,40 +765,34 @@ public final class TestFormulas extends TestCase {
         assertEquals(function, c.getCellFormula());
 
 
-        File file = TempFile.createTempFile("testSumIfFormula",".xls");
-        FileOutputStream out    = new FileOutputStream(file);
         wb     = new HSSFWorkbook();
         s      = wb.createSheet();
 
         r = s.createRow(0);
-        c=r.createCell(0); c.setCellValue((double)1000);
-        c=r.createCell(1); c.setCellValue((double)1);
+        c=r.createCell(0); c.setCellValue(1000);
+        c=r.createCell(1); c.setCellValue(1);
 
 
         r = s.createRow(1);
-        c=r.createCell(0); c.setCellValue((double)2000);
-        c=r.createCell(1); c.setCellValue((double)2);
+        c=r.createCell(0); c.setCellValue(2000);
+        c=r.createCell(1); c.setCellValue(2);
 
         r = s.createRow(2);
-        c=r.createCell(0); c.setCellValue((double)3000);
-        c=r.createCell(1); c.setCellValue((double)3);
+        c=r.createCell(0); c.setCellValue(3000);
+        c=r.createCell(1); c.setCellValue(3);
 
         r = s.createRow(3);
-        c=r.createCell(0); c.setCellValue((double)4000);
-        c=r.createCell(1); c.setCellValue((double)4);
+        c=r.createCell(0); c.setCellValue(4000);
+        c=r.createCell(1); c.setCellValue(4);
 
         r = s.createRow(4);
-        c=r.createCell(0); c.setCellValue((double)5000);
-        c=r.createCell(1); c.setCellValue((double)5);
+        c=r.createCell(0); c.setCellValue(5000);
+        c=r.createCell(1); c.setCellValue(5);
 
         r = s.getRow(0);
         c=r.createCell(2); c.setCellFormula(function);
 
-        wb.write(out);
-        out.close();
-
-        assertTrue("sumif file doesnt exists", (file.exists()));
-        assertTrue("sumif == 0 bytes", file.length() > 0);
+        HSSFTestDataSamples.writeOutAndReadBack(wb);
     }
 
     public void testSquareMacro() {
@@ -1064,7 +863,7 @@ public final class TestFormulas extends TestCase {
     /** Unknown Ptg 3D*/
     public void test27272_2() throws Exception {
         HSSFWorkbook wb = openSample("27272_2.xls");
-        assertEquals("Reference for named range ", "'LOAD.POD_HISTORIES'!#REF!",wb.getNameAt(0).getReference());
+        assertEquals("Reference for named range ", "LOAD.POD_HISTORIES!#REF!",wb.getNameAt(0).getReference());
         File outF = File.createTempFile("bug27272_2",".xls");
         wb.write(new FileOutputStream(outF));
         System.out.println("Open "+outF.getAbsolutePath()+" in Excel");
@@ -1085,8 +884,4 @@ public final class TestFormulas extends TestCase {
         assertEquals("DZ2*2", wb.getSheetAt(0).getRow(1).getCell(128).toString());
         assertEquals("B32770*2", wb.getSheetAt(0).getRow(32768).getCell(1).toString());
     }
-
-    public static void main(String [] args) {
-        junit.textui.TestRunner.run(TestFormulas.class);
-    }
 }
diff --git a/src/testcases/org/apache/poi/ss/formula/AllSSFormulaTests.java b/src/testcases/org/apache/poi/ss/formula/AllSSFormulaTests.java
new file mode 100644 (file)
index 0000000..dd1360f
--- /dev/null
@@ -0,0 +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.
+==================================================================== */
+
+package org.apache.poi.ss.formula;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+/**
+ * Test suite for org.apache.poi.ss.formula
+ * 
+ * @author Josh Micich
+ */
+public final class AllSSFormulaTests {
+    public static Test suite() {
+        TestSuite result = new TestSuite(AllSSFormulaTests.class.getName());
+        result.addTestSuite(TestEvaluationCache.class);
+        result.addTestSuite(TestWorkbookEvaluator.class);
+        return result;
+    }
+}
diff --git a/src/testcases/org/apache/poi/ss/formula/TestWorkbookEvaluator.java b/src/testcases/org/apache/poi/ss/formula/TestWorkbookEvaluator.java
new file mode 100644 (file)
index 0000000..0d3e93e
--- /dev/null
@@ -0,0 +1,154 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.ss.formula;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.hssf.HSSFTestDataSamples;
+import org.apache.poi.hssf.record.formula.AreaErrPtg;
+import org.apache.poi.hssf.record.formula.AttrPtg;
+import org.apache.poi.hssf.record.formula.DeletedArea3DPtg;
+import org.apache.poi.hssf.record.formula.DeletedRef3DPtg;
+import org.apache.poi.hssf.record.formula.IntPtg;
+import org.apache.poi.hssf.record.formula.Ptg;
+import org.apache.poi.hssf.record.formula.RefErrorPtg;
+import org.apache.poi.hssf.record.formula.eval.ErrorEval;
+import org.apache.poi.hssf.record.formula.eval.NumberEval;
+import org.apache.poi.hssf.record.formula.eval.ValueEval;
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+
+/**
+ * Tests {@link WorkbookEvaluator}.
+ *
+ * @author Josh Micich
+ */
+public class TestWorkbookEvaluator extends TestCase {
+
+       /**
+        * Make sure that the evaluator can directly handle tAttrSum (instead of relying on re-parsing
+        * the whole formula which converts tAttrSum to tFuncVar("SUM") )
+        */
+       public void testAttrSum() {
+
+               Ptg[] ptgs = {
+                       new IntPtg(42),
+                       AttrPtg.SUM,
+               };
+
+               ValueEval result = new WorkbookEvaluator(null).evaluateFormula(0, 0, 0, ptgs, null);
+               assertEquals(42, ((NumberEval)result).getNumberValue(), 0.0);
+       }
+
+       /**
+        * Make sure that the evaluator can directly handle (deleted) ref error tokens
+        * (instead of relying on re-parsing the whole formula which converts these
+        * to the error constant #REF! )
+        */
+       public void testRefErr() {
+
+               confirmRefErr(new RefErrorPtg());
+               confirmRefErr(new AreaErrPtg());
+               confirmRefErr(new DeletedRef3DPtg(0));
+               confirmRefErr(new DeletedArea3DPtg(0));
+       }
+       private static void confirmRefErr(Ptg ptg) {
+               Ptg[] ptgs = {
+                       ptg,
+               };
+
+               ValueEval result = new WorkbookEvaluator(null).evaluateFormula(0, 0, 0, ptgs, null);
+               assertEquals(ErrorEval.REF_INVALID, result);
+       }
+
+       /**
+        * Make sure that the evaluator can directly handle tAttrSum (instead of relying on re-parsing
+        * the whole formula which converts tAttrSum to tFuncVar("SUM") )
+        */
+       public void testMemFunc() {
+
+               Ptg[] ptgs = {
+                       new IntPtg(42),
+                       AttrPtg.SUM,
+               };
+
+               ValueEval result = new WorkbookEvaluator(null).evaluateFormula(0, 0, 0, ptgs, null);
+               assertEquals(42, ((NumberEval)result).getNumberValue(), 0.0);
+       }
+
+
+       public void testEvaluateMultipleWorkbooks() {
+               HSSFWorkbook wbA = HSSFTestDataSamples.openSampleWorkbook("multibookFormulaA.xls");
+               HSSFWorkbook wbB = HSSFTestDataSamples.openSampleWorkbook("multibookFormulaB.xls");
+
+               HSSFFormulaEvaluator evaluatorA = new HSSFFormulaEvaluator(wbA);
+               HSSFFormulaEvaluator evaluatorB = new HSSFFormulaEvaluator(wbB);
+
+               // Hook up the workbook evaluators to enable evaluation of formulas across books
+               String[] bookNames = { "multibookFormulaA.xls", "multibookFormulaB.xls", };
+               HSSFFormulaEvaluator[] evaluators = { evaluatorA, evaluatorB, };
+               HSSFFormulaEvaluator.setupEnvironment(bookNames, evaluators);
+
+               HSSFCell cell;
+
+               HSSFSheet aSheet1 = wbA.getSheetAt(0);
+               HSSFSheet bSheet1 = wbB.getSheetAt(0);
+
+               // Simple case - single link from wbA to wbB
+               confirmFormula(wbA, 0, 0, 0, "[multibookFormulaB.xls]BSheet1!B1");
+               cell = aSheet1.getRow(0).getCell(0);
+               confirmEvaluation(35, evaluatorA, cell);
+
+
+               // more complex case - back link into wbA
+               // [wbA]ASheet1!A2 references (among other things) [wbB]BSheet1!B2
+               confirmFormula(wbA, 0, 1, 0, "[multibookFormulaB.xls]BSheet1!$B$2+2*A3");
+               // [wbB]BSheet1!B2 references (among other things) [wbA]AnotherSheet!A1:B2
+               confirmFormula(wbB, 0, 1, 1, "SUM([multibookFormulaA.xls]AnotherSheet!$A$1:$B$2)+B3");
+
+               cell = aSheet1.getRow(1).getCell(0);
+               confirmEvaluation(264, evaluatorA, cell);
+
+               // change [wbB]BSheet1!B3 (from 50 to 60)
+               bSheet1.getRow(2).getCell(1).setCellValue(60);
+               evaluatorB.setCachedPlainValue(bSheet1, 2, 1, new NumberEval(60));
+               confirmEvaluation(274, evaluatorA, cell);
+
+               // change [wbA]ASheet1!A3 (from 100 to 80)
+               aSheet1.getRow(2).getCell(0).setCellValue(80);
+               evaluatorA.setCachedPlainValue(aSheet1, 2, 0, new NumberEval(80));
+               confirmEvaluation(234, evaluatorA, cell);
+
+               // change [wbA]AnotherSheet!A1 (from 2 to 3)
+               wbA.getSheetAt(1).getRow(0).getCell(0).setCellValue(3);
+               evaluatorA.setCachedPlainValue(wbA.getSheetAt(1), 0, 0, new NumberEval(3));
+               confirmEvaluation(235, evaluatorA, cell);
+       }
+
+       private static void confirmEvaluation(double expectedValue, HSSFFormulaEvaluator fe, HSSFCell cell) {
+               assertEquals(expectedValue, fe.evaluate(cell).getNumberValue(), 0.0);
+       }
+
+       private static void confirmFormula(HSSFWorkbook wb, int sheetIndex, int rowIndex, int columnIndex,
+                       String expectedFormula) {
+               HSSFCell cell = wb.getSheetAt(sheetIndex).getRow(rowIndex).getCell(columnIndex);
+               assertEquals(expectedFormula, cell.getCellFormula());
+       }
+}