]> source.dussan.org Git - poi.git/commitdiff
Shuffle the FormulaParser stuff about, and get it all working with the new interfaces...
authorNick Burch <nick@apache.org>
Sat, 29 Mar 2008 22:03:18 +0000 (22:03 +0000)
committerNick Burch <nick@apache.org>
Sat, 29 Mar 2008 22:03:18 +0000 (22:03 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@642624 13f79535-47bb-0310-9956-ffa450edef68

24 files changed:
src/java/org/apache/poi/hssf/record/formula/eval/ExternalFunction.java
src/java/org/apache/poi/hssf/record/formula/functions/FreeRefFunction.java
src/java/org/apache/poi/hssf/record/formula/functions/Indirect.java
src/java/org/apache/poi/hssf/record/formula/functions/Offset.java
src/java/org/apache/poi/hssf/usermodel/EvaluationCycleDetector.java [deleted file]
src/java/org/apache/poi/hssf/usermodel/EvaluationCycleDetectorManager.java [deleted file]
src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java
src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
src/java/org/apache/poi/hssf/usermodel/OperationEvaluatorFactory.java [deleted file]
src/java/org/apache/poi/ss/usermodel/EvaluationCycleDetector.java [new file with mode: 0755]
src/java/org/apache/poi/ss/usermodel/EvaluationCycleDetectorManager.java [new file with mode: 0755]
src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java [new file with mode: 0644]
src/java/org/apache/poi/ss/usermodel/OperationEvaluatorFactory.java [new file with mode: 0755]
src/ooxml/interfaces-jdk15/org/apache/poi/ss/usermodel/Workbook.java
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java
src/testcases/org/apache/poi/hssf/model/TestFormulaParserEval.java
src/testcases/org/apache/poi/hssf/record/formula/eval/TestCircularReferences.java
src/testcases/org/apache/poi/hssf/record/formula/eval/TestExternalFunction.java
src/testcases/org/apache/poi/hssf/record/formula/eval/TestFormulaBugs.java
src/testcases/org/apache/poi/hssf/record/formula/eval/TestFormulasFromSpreadsheet.java
src/testcases/org/apache/poi/hssf/record/formula/eval/TestPercentEval.java
src/testcases/org/apache/poi/hssf/record/formula/functions/TestIsBlank.java
src/testcases/org/apache/poi/hssf/record/formula/functions/TestLookupFunctionsFromSpreadsheet.java
src/testcases/org/apache/poi/hssf/usermodel/TestBug42464.java

index b1d81e652476d18244c6f4c04e6095e75868ff85..f40c83202e1ba57f6774c870baa6a44907e65559 100755 (executable)
@@ -18,8 +18,8 @@
 package org.apache.poi.hssf.record.formula.eval;
 
 import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;
-import org.apache.poi.hssf.usermodel.HSSFSheet;
-import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
 /**
  * 
  * Common entry point for all external functions (where 
@@ -29,7 +29,7 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook;
  */
 final class ExternalFunction implements FreeRefFunction {
 
-       public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, HSSFWorkbook workbook, HSSFSheet sheet) {
+       public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, Workbook workbook, Sheet sheet) {
                
                int nIncomingArgs = args.length;
                if(nIncomingArgs < 1) {
@@ -56,7 +56,7 @@ final class ExternalFunction implements FreeRefFunction {
                return targetFunc.evaluate(outGoingArgs, srcCellRow, srcCellCol, workbook, sheet);
        }
 
-       private FreeRefFunction findTargetFunction(HSSFWorkbook workbook, NameEval functionNameEval) throws EvaluationException {
+       private FreeRefFunction findTargetFunction(Workbook workbook, NameEval functionNameEval) throws EvaluationException {
 
                int numberOfNames = workbook.getNumberOfNames();
                
index 56d285543e876a6941d7f375992161cc585870c1..2163d8f5ed004105a91b2e84821e15b5e4b98187 100755 (executable)
@@ -19,8 +19,8 @@ package org.apache.poi.hssf.record.formula.functions;
 
 import org.apache.poi.hssf.record.formula.eval.Eval;
 import org.apache.poi.hssf.record.formula.eval.ValueEval;
-import org.apache.poi.hssf.usermodel.HSSFSheet;
-import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
 
 
 /**
@@ -53,5 +53,5 @@ public interface FreeRefFunction {
         * a specified Excel error (Exceptions are never thrown to represent Excel errors).
         * 
         */
-       ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, HSSFWorkbook workbook, HSSFSheet sheet);
+       ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, Workbook workbook, Sheet sheet);
 }
index 935e7cdbbdaebb968132013efa276563a4373a90..8d7d4463e5dc97c75f5c14c0a2b7d8fe08e035af 100644 (file)
@@ -20,8 +20,8 @@ package org.apache.poi.hssf.record.formula.functions;
 import org.apache.poi.hssf.record.formula.eval.ErrorEval;
 import org.apache.poi.hssf.record.formula.eval.Eval;
 import org.apache.poi.hssf.record.formula.eval.ValueEval;
-import org.apache.poi.hssf.usermodel.HSSFSheet;
-import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
 
 /**
  * Implementation for Excel function INDIRECT<p/>
@@ -41,7 +41,7 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook;
  */
 public final class Indirect implements FreeRefFunction {
 
-       public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, HSSFWorkbook workbook, HSSFSheet sheet) {
+       public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, Workbook workbook, Sheet sheet) {
                // TODO - implement INDIRECT()
                return ErrorEval.FUNCTION_NOT_IMPLEMENTED;
        }
index 9497a5f21a908ae2baaed4d1cb81f57c19863340..8a10e6225388100cdae1c401e4c7c366b5dd9c81 100644 (file)
@@ -31,8 +31,8 @@ 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.usermodel.HSSFFormulaEvaluator;
-import org.apache.poi.hssf.usermodel.HSSFSheet;
-import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
 /**
  * Implementation for Excel function OFFSET()<p/>
  * 
@@ -201,7 +201,7 @@ public final class Offset implements FreeRefFunction {
 
        }
        
-       public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, HSSFWorkbook workbook, HSSFSheet sheet) {
+       public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, Workbook workbook, Sheet sheet) {
                
                if(args.length < 3 || args.length > 5) {
                        return ErrorEval.VALUE_INVALID;
@@ -235,7 +235,7 @@ public final class Offset implements FreeRefFunction {
 
        private static AreaEval createOffset(BaseRef baseRef, 
                        LinearOffsetRange rowOffsetRange, LinearOffsetRange colOffsetRange, 
-                       HSSFWorkbook workbook, HSSFSheet sheet) throws EvalEx {
+                       Workbook workbook, Sheet sheet) throws EvalEx {
 
                LinearOffsetRange rows = rowOffsetRange.normaliseAndTranslate(baseRef.getFirstRowIndex());
                LinearOffsetRange cols = colOffsetRange.normaliseAndTranslate(baseRef.getFirstColumnIndex());
diff --git a/src/java/org/apache/poi/hssf/usermodel/EvaluationCycleDetector.java b/src/java/org/apache/poi/hssf/usermodel/EvaluationCycleDetector.java
deleted file mode 100755 (executable)
index 90f5807..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-/* ====================================================================
-   Licensed to the Apache Software Foundation (ASF) under one or more
-   contributor license agreements.  See the NOTICE file distributed with
-   this work for additional information regarding copyright ownership.
-   The ASF licenses this file to You under the Apache License, Version 2.0
-   (the "License"); you may not use this file except in compliance with
-   the License.  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-==================================================================== */
-
-package org.apache.poi.hssf.usermodel;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Instances of this class keep track of multiple dependent cell evaluations due
- * to recursive calls to <tt>HSSFFormulaEvaluator.internalEvaluate()</tt>.
- * The main purpose of this class is to detect an attempt to evaluate a cell
- * that is already being evaluated. In other words, it detects circular
- * references in spreadsheet formulas.
- * 
- * @author Josh Micich
- */
-final class EvaluationCycleDetector {
-
-       /**
-        * Stores the parameters that identify the evaluation of one cell.<br/>
-        */
-       private static final class CellEvaluationFrame {
-
-               private final HSSFWorkbook _workbook;
-               private final HSSFSheet _sheet;
-               private final int _srcRowNum;
-               private final int _srcColNum;
-
-               public CellEvaluationFrame(HSSFWorkbook workbook, HSSFSheet sheet, int srcRowNum, int srcColNum) {
-                       if (workbook == null) {
-                               throw new IllegalArgumentException("workbook must not be null");
-                       }
-                       if (sheet == null) {
-                               throw new IllegalArgumentException("sheet must not be null");
-                       }
-                       _workbook = workbook;
-                       _sheet = sheet;
-                       _srcRowNum = srcRowNum;
-                       _srcColNum = srcColNum;
-               }
-
-               public boolean equals(Object obj) {
-                       CellEvaluationFrame other = (CellEvaluationFrame) obj;
-                       if (_workbook != other._workbook) {
-                               return false;
-                       }
-                       if (_sheet != other._sheet) {
-                               return false;
-                       }
-                       if (_srcRowNum != other._srcRowNum) {
-                               return false;
-                       }
-                       if (_srcColNum != other._srcColNum) {
-                               return false;
-                       }
-                       return true;
-               }
-
-               /**
-                * @return human readable string for debug purposes
-                */
-               public String formatAsString() {
-                       return "R=" + _srcRowNum + " C=" + _srcColNum + " ShIx=" + _workbook.getSheetIndex(_sheet);
-               }
-
-               public String toString() {
-                       StringBuffer sb = new StringBuffer(64);
-                       sb.append(getClass().getName()).append(" [");
-                       sb.append(formatAsString());
-                       sb.append("]");
-                       return sb.toString();
-               }
-       }
-
-       private final List _evaluationFrames;
-
-       public EvaluationCycleDetector() {
-               _evaluationFrames = new ArrayList();
-       }
-
-       /**
-        * Notifies this evaluation tracker that evaluation of the specified cell is
-        * about to start.<br/>
-        * 
-        * In the case of a <code>true</code> return code, the caller should
-        * continue evaluation of the specified cell, and also be sure to call
-        * <tt>endEvaluate()</tt> when complete.<br/>
-        * 
-        * In the case of a <code>false</code> return code, the caller should
-        * return an evaluation result of
-        * <tt>ErrorEval.CIRCULAR_REF_ERROR<tt>, and not call <tt>endEvaluate()</tt>.  
-        * <br/>
-        * @return <code>true</code> if the specified cell has not been visited yet in the current 
-        * evaluation. <code>false</code> if the specified cell is already being evaluated.
-        */
-       public boolean startEvaluate(HSSFWorkbook workbook, HSSFSheet sheet, int srcRowNum, int srcColNum) {
-               CellEvaluationFrame cef = new CellEvaluationFrame(workbook, sheet, srcRowNum, srcColNum);
-               if (_evaluationFrames.contains(cef)) {
-                       return false;
-               }
-               _evaluationFrames.add(cef);
-               return true;
-       }
-
-       /**
-        * Notifies this evaluation tracker that the evaluation of the specified
-        * cell is complete. <p/>
-        * 
-        * Every successful call to <tt>startEvaluate</tt> must be followed by a
-        * call to <tt>endEvaluate</tt> (recommended in a finally block) to enable
-        * proper tracking of which cells are being evaluated at any point in time.<p/>
-        * 
-        * Assuming a well behaved client, parameters to this method would not be
-        * required. However, they have been included to assert correct behaviour,
-        * and form more meaningful error messages.
-        */
-       public void endEvaluate(HSSFWorkbook workbook, HSSFSheet sheet, int srcRowNum, int srcColNum) {
-               int nFrames = _evaluationFrames.size();
-               if (nFrames < 1) {
-                       throw new IllegalStateException("Call to endEvaluate without matching call to startEvaluate");
-               }
-
-               nFrames--;
-               CellEvaluationFrame cefExpected = (CellEvaluationFrame) _evaluationFrames.get(nFrames);
-               CellEvaluationFrame cefActual = new CellEvaluationFrame(workbook, sheet, srcRowNum, srcColNum);
-               if (!cefActual.equals(cefExpected)) {
-                       throw new RuntimeException("Wrong cell specified. "
-                                       + "Corresponding startEvaluate() call was for cell {"
-                                       + cefExpected.formatAsString() + "} this endEvaluate() call is for cell {"
-                                       + cefActual.formatAsString() + "}");
-               }
-               // else - no problems so pop current frame 
-               _evaluationFrames.remove(nFrames);
-       }
-}
diff --git a/src/java/org/apache/poi/hssf/usermodel/EvaluationCycleDetectorManager.java b/src/java/org/apache/poi/hssf/usermodel/EvaluationCycleDetectorManager.java
deleted file mode 100755 (executable)
index a06cd20..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/* ====================================================================
-   Licensed to the Apache Software Foundation (ASF) under one or more
-   contributor license agreements.  See the NOTICE file distributed with
-   this work for additional information regarding copyright ownership.
-   The ASF licenses this file to You under the Apache License, Version 2.0
-   (the "License"); you may not use this file except in compliance with
-   the License.  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-==================================================================== */
-
-package org.apache.poi.hssf.usermodel;
-
-/**
- * This class makes an <tt>EvaluationCycleDetector</tt> instance available to
- * each thread via a <tt>ThreadLocal</tt> in order to avoid adding a parameter
- * to a few protected methods within <tt>HSSFFormulaEvaluator</tt>.
- * 
- * @author Josh Micich
- */
-final class EvaluationCycleDetectorManager {
-
-       ThreadLocal tl = null;
-       private static ThreadLocal _tlEvaluationTracker = new ThreadLocal() {
-               protected synchronized Object initialValue() {
-                       return new EvaluationCycleDetector();
-               }
-       };
-
-       /**
-        * @return
-        */
-       public static EvaluationCycleDetector getTracker() {
-               return (EvaluationCycleDetector) _tlEvaluationTracker.get();
-       }
-
-       private EvaluationCycleDetectorManager() {
-               // no instances of this class
-       }
-}
index bb16fdfadd7bc5d9fc326a69ed8500ae84e01125..3da5f968829cf9eb8371bc9958e90f71621d27b1 100644 (file)
 
 package org.apache.poi.hssf.usermodel;
 
-import java.lang.reflect.Constructor;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Stack;
-
 import org.apache.poi.hssf.model.FormulaParser;
-import org.apache.poi.hssf.model.Workbook;
-import org.apache.poi.hssf.record.formula.Area3DPtg;
-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.IntPtg;
-import org.apache.poi.hssf.record.formula.MemErrPtg;
-import org.apache.poi.hssf.record.formula.MissingArgPtg;
-import org.apache.poi.hssf.record.formula.NamePtg;
-import org.apache.poi.hssf.record.formula.NameXPtg;
-import org.apache.poi.hssf.record.formula.NumberPtg;
 import org.apache.poi.hssf.record.formula.OperationPtg;
-import org.apache.poi.hssf.record.formula.ParenthesisPtg;
 import org.apache.poi.hssf.record.formula.Ptg;
-import org.apache.poi.hssf.record.formula.Ref3DPtg;
-import org.apache.poi.hssf.record.formula.ReferencePtg;
-import org.apache.poi.hssf.record.formula.StringPtg;
-import org.apache.poi.hssf.record.formula.UnionPtg;
-import org.apache.poi.hssf.record.formula.UnknownPtg;
-import org.apache.poi.hssf.record.formula.eval.Area2DEval;
-import org.apache.poi.hssf.record.formula.eval.Area3DEval;
-import org.apache.poi.hssf.record.formula.eval.AreaEval;
-import org.apache.poi.hssf.record.formula.eval.BlankEval;
-import org.apache.poi.hssf.record.formula.eval.BoolEval;
-import org.apache.poi.hssf.record.formula.eval.ErrorEval;
-import org.apache.poi.hssf.record.formula.eval.Eval;
-import org.apache.poi.hssf.record.formula.eval.FunctionEval;
-import org.apache.poi.hssf.record.formula.eval.NameEval;
-import org.apache.poi.hssf.record.formula.eval.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.OperationEval;
-import org.apache.poi.hssf.record.formula.eval.Ref2DEval;
-import org.apache.poi.hssf.record.formula.eval.Ref3DEval;
-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.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CreationHelper;
+import org.apache.poi.ss.usermodel.FormulaEvaluator;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
 
 /**
  * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
  * 
  */
-public class HSSFFormulaEvaluator {
-                
-    // params to lookup the right constructor using reflection
-    private static final Class[] VALUE_CONTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class };
-
-    private static final Class[] AREA3D_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class, ValueEval[].class };
-
-    private static final Class[] REFERENCE_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class, ValueEval.class };
-
-    private static final Class[] REF3D_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class, ValueEval.class };
-
-    // Maps for mapping *Eval to *Ptg
-    private static final Map VALUE_EVALS_MAP = new HashMap();
-
-    /*
-     * Following is the mapping between the Ptg tokens returned 
-     * by the FormulaParser and the *Eval classes that are used 
-     * by the FormulaEvaluator
-     */
-    static {
-        VALUE_EVALS_MAP.put(BoolPtg.class, BoolEval.class);
-        VALUE_EVALS_MAP.put(IntPtg.class, NumberEval.class);
-        VALUE_EVALS_MAP.put(NumberPtg.class, NumberEval.class);
-        VALUE_EVALS_MAP.put(StringPtg.class, StringEval.class);
-
-    }
-
-    
-    protected HSSFRow row;
-    protected HSSFSheet sheet;
-    protected HSSFWorkbook workbook;
-    
+public class HSSFFormulaEvaluator extends FormulaEvaluator {
     public HSSFFormulaEvaluator(HSSFSheet sheet, HSSFWorkbook workbook) {
-        this.sheet = sheet;
-        this.workbook = workbook;
-    }
-    
-    public void setCurrentRow(HSSFRow row) {
-        this.row = row;
+       super(sheet, workbook);
     }
 
-    
     /**
      * Returns an underlying FormulaParser, for the specified
      *  Formula String and HSSFWorkbook.
@@ -118,639 +47,6 @@ public class HSSFFormulaEvaluator {
         return new FormulaParser(formula, workbook.getWorkbook());
     }
     
-    /**
-     * If cell contains a formula, the formula is evaluated and returned,
-     * else the CellValue simply copies the appropriate cell value from
-     * the cell and also its cell type. This method should be preferred over
-     * evaluateInCell() when the call should not modify the contents of the
-     * original cell. 
-     * @param cell
-     */
-    public CellValue evaluate(HSSFCell cell) {
-        CellValue retval = null;
-        if (cell != null) {
-            switch (cell.getCellType()) {
-            case HSSFCell.CELL_TYPE_BLANK:
-                retval = new CellValue(HSSFCell.CELL_TYPE_BLANK);
-                break;
-            case HSSFCell.CELL_TYPE_BOOLEAN:
-                retval = new CellValue(HSSFCell.CELL_TYPE_BOOLEAN);
-                retval.setBooleanValue(cell.getBooleanCellValue());
-                break;
-            case HSSFCell.CELL_TYPE_ERROR:
-                retval = new CellValue(HSSFCell.CELL_TYPE_ERROR);
-                retval.setErrorValue(cell.getErrorCellValue());
-                break;
-            case HSSFCell.CELL_TYPE_FORMULA:
-                retval = getCellValueForEval(internalEvaluate(cell, row, sheet, workbook));
-                break;
-            case HSSFCell.CELL_TYPE_NUMERIC:
-                retval = new CellValue(HSSFCell.CELL_TYPE_NUMERIC);
-                retval.setNumberValue(cell.getNumericCellValue());
-                break;
-            case HSSFCell.CELL_TYPE_STRING:
-                retval = new CellValue(HSSFCell.CELL_TYPE_STRING);
-                retval.setRichTextStringValue(cell.getRichStringCellValue());
-                break;
-            }
-        }
-        return retval;
-    }
-    
-    
-    /**
-     * If cell contains formula, it evaluates the formula,
-     *  and saves the result of the formula. The cell
-     *  remains as a formula cell.
-     * Else if cell does not contain formula, this method leaves
-     *  the cell unchanged. 
-     * Note that the type of the formula result is returned,
-     *  so you know what kind of value is also stored with
-     *  the formula. 
-     * <pre>
-     * int evaluatedCellType = evaluator.evaluateFormulaCell(cell);
-     * </pre>
-     * Be aware that your cell will hold both the formula,
-     *  and the result. If you want the cell replaced with
-     *  the result of the formula, use {@link #evaluateInCell(HSSFCell)}
-     * @param cell The cell to evaluate
-     * @return The type of the formula result (the cell's type remains as HSSFCell.CELL_TYPE_FORMULA however)
-     */
-    public int evaluateFormulaCell(HSSFCell cell) {
-        if (cell != null) {
-            switch (cell.getCellType()) {
-            case HSSFCell.CELL_TYPE_FORMULA:
-                CellValue cv = getCellValueForEval(internalEvaluate(cell, row, sheet, workbook));
-                switch (cv.getCellType()) {
-                case HSSFCell.CELL_TYPE_BOOLEAN:
-                    cell.setCellValue(cv.getBooleanValue());
-                    break;
-                case HSSFCell.CELL_TYPE_ERROR:
-                    cell.setCellValue(cv.getErrorValue());
-                    break;
-                case HSSFCell.CELL_TYPE_NUMERIC:
-                    cell.setCellValue(cv.getNumberValue());
-                    break;
-                case HSSFCell.CELL_TYPE_STRING:
-                    cell.setCellValue(cv.getRichTextStringValue());
-                    break;
-                case HSSFCell.CELL_TYPE_BLANK:
-                    break;
-                case HSSFCell.CELL_TYPE_FORMULA: // this will never happen, we have already evaluated the formula
-                    break;
-                }
-                return cv.getCellType();
-            }
-        }
-        return -1;
-    }
-        
-    /**
-     * If cell contains formula, it evaluates the formula, and
-     *  puts the formula result back into the cell, in place
-     *  of the old formula.
-     * Else if cell does not contain formula, this method leaves
-     *  the cell unchanged. 
-     * Note that the same instance of HSSFCell is returned to 
-     * allow chained calls like:
-     * <pre>
-     * int evaluatedCellType = evaluator.evaluateInCell(cell).getCellType();
-     * </pre>
-     * Be aware that your cell value will be changed to hold the
-     *  result of the formula. If you simply want the formula
-     *  value computed for you, use {@link #evaluateFormulaCell(HSSFCell)}
-     * @param cell
-     */
-    public HSSFCell evaluateInCell(HSSFCell cell) {
-        if (cell != null) {
-            switch (cell.getCellType()) {
-            case HSSFCell.CELL_TYPE_FORMULA:
-                CellValue cv = getCellValueForEval(internalEvaluate(cell, row, sheet, workbook));
-                switch (cv.getCellType()) {
-                case HSSFCell.CELL_TYPE_BOOLEAN:
-                    cell.setCellType(HSSFCell.CELL_TYPE_BOOLEAN);
-                    cell.setCellValue(cv.getBooleanValue());
-                    break;
-                case HSSFCell.CELL_TYPE_ERROR:
-                    cell.setCellType(HSSFCell.CELL_TYPE_ERROR);
-                    cell.setCellValue(cv.getErrorValue());
-                    break;
-                case HSSFCell.CELL_TYPE_NUMERIC:
-                    cell.setCellType(HSSFCell.CELL_TYPE_NUMERIC);
-                    cell.setCellValue(cv.getNumberValue());
-                    break;
-                case HSSFCell.CELL_TYPE_STRING:
-                    cell.setCellType(HSSFCell.CELL_TYPE_STRING);
-                    cell.setCellValue(cv.getRichTextStringValue());
-                    break;
-                case HSSFCell.CELL_TYPE_BLANK:
-                    break;
-                case HSSFCell.CELL_TYPE_FORMULA: // this will never happen, we have already evaluated the formula
-                    break;
-                }
-            }
-        }
-        return cell;
-    }
-    
-    /**
-     * Loops over all cells in all sheets of the supplied
-     *  workbook.
-     * For cells that contain formulas, their formulas are
-     *  evaluated, and the results are saved. These cells
-     *  remain as formula cells.
-     * For cells that do not contain formulas, no changes
-     *  are made.
-     * This is a helpful wrapper around looping over all 
-     *  cells, and calling evaluateFormulaCell on each one.
-     */
-       public static void evaluateAllFormulaCells(HSSFWorkbook wb) {
-               for(int i=0; i<wb.getNumberOfSheets(); i++) {
-                       HSSFSheet sheet = wb.getSheetAt(i);
-                       HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb);
-
-                       for (Iterator rit = sheet.rowIterator(); rit.hasNext();) {
-                               HSSFRow r = (HSSFRow)rit.next();
-                               evaluator.setCurrentRow(r);
-
-                               for (Iterator cit = r.cellIterator(); cit.hasNext();) {
-                                       HSSFCell c = (HSSFCell)cit.next();
-                                       if (c.getCellType() == HSSFCell.CELL_TYPE_FORMULA)
-                                               evaluator.evaluateFormulaCell(c);
-                               }
-                       }
-               }
-       }
-        
-    
-    /**
-     * Returns a CellValue wrapper around the supplied ValueEval instance.
-     * @param eval
-     */
-    protected static CellValue getCellValueForEval(ValueEval eval) {
-        CellValue retval = null;
-        if (eval != null) {
-            if (eval instanceof NumberEval) {
-                NumberEval ne = (NumberEval) eval;
-                retval = new CellValue(HSSFCell.CELL_TYPE_NUMERIC);
-                retval.setNumberValue(ne.getNumberValue());
-            }
-            else if (eval instanceof BoolEval) {
-                BoolEval be = (BoolEval) eval;
-                retval = new CellValue(HSSFCell.CELL_TYPE_BOOLEAN);
-                retval.setBooleanValue(be.getBooleanValue());
-            }
-            else if (eval instanceof StringEval) {
-                StringEval ne = (StringEval) eval;
-                retval = new CellValue(HSSFCell.CELL_TYPE_STRING);
-                retval.setStringValue(ne.getStringValue());
-            }
-            else if (eval instanceof BlankEval) {
-                retval = new CellValue(HSSFCell.CELL_TYPE_BLANK);
-            }
-            else if (eval instanceof ErrorEval) {
-                retval = new CellValue(HSSFCell.CELL_TYPE_ERROR);
-                retval.setErrorValue((byte)((ErrorEval)eval).getErrorCode());
-//                retval.setRichTextStringValue(new HSSFRichTextString("#An error occurred. check cell.getErrorCode()"));
-            }
-            else {
-                retval = new CellValue(HSSFCell.CELL_TYPE_ERROR);
-            }
-        }
-        return retval;
-    }
-    
-    /**
-     * Dev. Note: Internal evaluate must be passed only a formula cell 
-     * else a runtime exception will be thrown somewhere inside the method.
-     * (Hence this is a private method.)
-     */
-    private static ValueEval internalEvaluate(HSSFCell srcCell, HSSFRow srcRow, HSSFSheet sheet, HSSFWorkbook workbook) {
-        int srcRowNum = srcRow.getRowNum();
-        short srcColNum = srcCell.getCellNum();
-        
-        
-        EvaluationCycleDetector tracker = EvaluationCycleDetectorManager.getTracker();
-        
-        if(!tracker.startEvaluate(workbook, sheet, srcRowNum, srcColNum)) {
-            return ErrorEval.CIRCULAR_REF_ERROR;
-        }
-        try {
-            return evaluateCell(workbook, sheet, srcRowNum, srcColNum, srcCell.getCellFormula());
-        } finally {
-            tracker.endEvaluate(workbook, sheet, srcRowNum, srcColNum);
-        }
-    }
-    private static ValueEval evaluateCell(HSSFWorkbook workbook, HSSFSheet sheet, 
-            int srcRowNum, short srcColNum, String cellFormulaText) {
-        FormulaParser parser = new FormulaParser(cellFormulaText, workbook.getWorkbook());
-        parser.parse();
-        Ptg[] ptgs = parser.getRPNPtg();
-        // -- parsing over --
-        
-
-        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 ControlPtg) { continue; }
-            if (ptg instanceof MemErrPtg) { continue; }
-            if (ptg instanceof MissingArgPtg) { continue; }
-            if (ptg instanceof NamePtg) { 
-               // named ranges, macro functions
-                NamePtg namePtg = (NamePtg) ptg;
-                stack.push(new NameEval(namePtg.getIndex()));
-                continue; 
-            }
-            if (ptg instanceof NameXPtg) {
-               // TODO - external functions
-                continue;
-            }
-            if (ptg instanceof UnknownPtg) { continue; }
-
-            if (ptg instanceof OperationPtg) {
-                OperationPtg optg = (OperationPtg) ptg;
-
-                // parens can be ignored since we have RPN tokens
-                if (optg instanceof ParenthesisPtg) { continue; }
-                if (optg instanceof AttrPtg) { continue; }
-                if (optg instanceof UnionPtg) { continue; }
-
-                OperationEval operation = OperationEvaluatorFactory.create(optg);
-
-                int numops = operation.getNumberOfOperands();
-                Eval[] ops = new Eval[numops];
-
-                // storing the ops in reverse order since they are popping
-                for (int j = numops - 1; j >= 0; j--) {
-                    Eval p = (Eval) stack.pop();
-                    ops[j] = p;
-                }
-                Eval opresult = invokeOperation(operation, ops, srcRowNum, srcColNum, workbook, sheet);
-                stack.push(opresult);
-            }
-            else if (ptg instanceof ReferencePtg) {
-                ReferencePtg refPtg = (ReferencePtg) ptg;
-                int colIx = refPtg.getColumn();
-                int rowIx = refPtg.getRow();
-                HSSFRow row = sheet.getRow(rowIx);
-                HSSFCell cell = (row != null) ? row.getCell(colIx) : null;
-                stack.push(createRef2DEval(refPtg, cell, row, sheet, workbook));
-            }
-            else if (ptg instanceof Ref3DPtg) {
-                Ref3DPtg refPtg = (Ref3DPtg) ptg;
-                int colIx = refPtg.getColumn();
-                int rowIx = refPtg.getRow();
-                Workbook wb = workbook.getWorkbook();
-                HSSFSheet xsheet = workbook.getSheetAt(wb.getSheetIndexFromExternSheetIndex(refPtg.getExternSheetIndex()));
-                HSSFRow row = xsheet.getRow(rowIx);
-                HSSFCell cell = (row != null) ? row.getCell(colIx) : null;
-                stack.push(createRef3DEval(refPtg, cell, row, xsheet, workbook));
-            }
-            else if (ptg instanceof AreaPtg) {
-                AreaPtg ap = (AreaPtg) ptg;
-                AreaEval ae = evaluateAreaPtg(sheet, workbook, ap);
-                stack.push(ae);
-            }
-            else if (ptg instanceof Area3DPtg) {
-                Area3DPtg a3dp = (Area3DPtg) ptg;
-                AreaEval ae = evaluateArea3dPtg(workbook, a3dp);
-                stack.push(ae);
-            }
-            else {
-                Eval ptgEval = getEvalForPtg(ptg);
-                stack.push(ptgEval);
-            }
-        }
-
-        ValueEval value = ((ValueEval) stack.pop());
-        if (!stack.isEmpty()) {
-            throw new IllegalStateException("evaluation stack not empty");
-        }
-        value = dereferenceValue(value, srcRowNum, srcColNum);
-        if (value instanceof BlankEval) {
-               // Note Excel behaviour here. A blank final final value is converted to zero.  
-            return NumberEval.ZERO;
-            // Formulas _never_ evaluate to blank.  If a formula appears to have evaluated to 
-            // blank, the actual value is empty string. This can be verified with ISBLANK().
-        }
-        return value;
-    }
-
-    /**
-     * Dereferences a single value from any AreaEval or RefEval evaluation result.
-     * If the supplied evaluationResult is just a plain value, it is returned as-is.
-        * @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) {
-        if (evaluationResult instanceof RefEval) {
-            RefEval rv = (RefEval) evaluationResult;
-            return rv.getInnerValueEval();
-        }
-        if (evaluationResult instanceof AreaEval) {
-            AreaEval ae = (AreaEval) evaluationResult;
-            if (ae.isRow()) {
-                if(ae.isColumn()) {
-                    return ae.getValues()[0];
-                }
-                return ae.getValueAt(ae.getFirstRow(), srcColNum);
-            }
-            if (ae.isColumn()) {
-                return ae.getValueAt(srcRowNum, ae.getFirstColumn());
-            }
-            return ErrorEval.VALUE_INVALID;
-        }
-        return evaluationResult;
-    }
-
-    private static Eval invokeOperation(OperationEval operation, Eval[] ops, int srcRowNum, short srcColNum,
-            HSSFWorkbook workbook, HSSFSheet sheet) {
-
-        if(operation instanceof FunctionEval) {
-            FunctionEval fe = (FunctionEval) operation;
-            if(fe.isFreeRefFunction()) {
-                return fe.getFreeRefFunction().evaluate(ops, srcRowNum, srcColNum, workbook, sheet);
-            }
-        }
-        return operation.evaluate(ops, srcRowNum, srcColNum);
-    }
-    
-    public static AreaEval evaluateAreaPtg(HSSFSheet sheet, HSSFWorkbook workbook, AreaPtg ap) {
-        int row0 = ap.getFirstRow();
-        int col0 = ap.getFirstColumn();
-        int row1 = ap.getLastRow();
-        int col1 = ap.getLastColumn();
-        
-        // If the last row is -1, then the
-        //  reference is for the rest of the column
-        // (eg C:C)
-        // TODO: Handle whole column ranges properly
-        if(row1 == -1 && row0 >= 0) {
-            row1 = (short)sheet.getLastRowNum();
-        }
-        ValueEval[] values = evalArea(workbook, sheet, row0, col0, row1, col1);
-        return new Area2DEval(ap, values);
-    }
-
-    public static AreaEval evaluateArea3dPtg(HSSFWorkbook workbook, Area3DPtg a3dp) {
-       int row0 = a3dp.getFirstRow();
-       int col0 = a3dp.getFirstColumn();
-       int row1 = a3dp.getLastRow();
-       int col1 = a3dp.getLastColumn();
-        Workbook wb = workbook.getWorkbook();
-        HSSFSheet xsheet = workbook.getSheetAt(wb.getSheetIndexFromExternSheetIndex(a3dp.getExternSheetIndex()));
-        
-        // If the last row is -1, then the
-        //  reference is for the rest of the column
-        // (eg C:C)
-        // TODO: Handle whole column ranges properly
-        if(row1 == -1 && row0 >= 0) {
-            row1 = (short)xsheet.getLastRowNum();
-        }
-        
-        ValueEval[] values = evalArea(workbook, xsheet, row0, col0, row1, col1);
-        return new Area3DEval(a3dp, values);
-    }
-    
-    private static ValueEval[] evalArea(HSSFWorkbook workbook, HSSFSheet sheet, 
-               int row0, int col0, int row1, int col1) {
-        ValueEval[] values = new ValueEval[(row1 - row0 + 1) * (col1 - col0 + 1)];
-        for (int x = row0; sheet != null && x < row1 + 1; x++) {
-            HSSFRow row = sheet.getRow(x);
-            for (int y = col0; y < col1 + 1; y++) {
-                ValueEval cellEval;
-                if(row == null) {
-                       cellEval = BlankEval.INSTANCE;
-                } else {
-                       cellEval = getEvalForCell(row.getCell(y), row, sheet, workbook);
-                }
-                               values[(x - row0) * (col1 - col0 + 1) + (y - col0)] = cellEval;
-            }
-        }
-        return values;
-    }
-
-    /**
-     * returns an appropriate Eval impl instance for the Ptg. The Ptg must be
-     * one of: Area3DPtg, AreaPtg, ReferencePtg, Ref3DPtg, IntPtg, NumberPtg,
-     * StringPtg, BoolPtg <br/>special Note: OperationPtg subtypes cannot be
-     * passed here!
-     * 
-     * @param ptg
-     */
-    protected static Eval getEvalForPtg(Ptg ptg) {
-        Eval retval = null;
-
-        Class clazz = (Class) VALUE_EVALS_MAP.get(ptg.getClass());
-        try {
-            if (ptg instanceof Area3DPtg) {
-                Constructor constructor = clazz.getConstructor(AREA3D_CONSTRUCTOR_CLASS_ARRAY);
-                retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg });
-            }
-            else if (ptg instanceof AreaPtg) {
-                Constructor constructor = clazz.getConstructor(AREA3D_CONSTRUCTOR_CLASS_ARRAY);
-                retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg });
-            }
-            else if (ptg instanceof ReferencePtg) {
-                Constructor constructor = clazz.getConstructor(REFERENCE_CONSTRUCTOR_CLASS_ARRAY);
-                retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg });
-            }
-            else if (ptg instanceof Ref3DPtg) {
-                Constructor constructor = clazz.getConstructor(REF3D_CONSTRUCTOR_CLASS_ARRAY);
-                retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg });
-            }
-            else {
-                if (ptg instanceof IntPtg || ptg instanceof NumberPtg || ptg instanceof StringPtg
-                        || ptg instanceof BoolPtg) {
-                    Constructor constructor = clazz.getConstructor(VALUE_CONTRUCTOR_CLASS_ARRAY);
-                    retval = (ValueEval) constructor.newInstance(new Ptg[] { ptg });
-                }
-            }
-        }
-        catch (Exception e) {
-            throw new RuntimeException("Fatal Error: ", e);
-        }
-        return retval;
-
-    }
-
-    /**
-     * Given a cell, find its type and from that create an appropriate ValueEval
-     * impl instance and return that. Since the cell could be an external
-     * reference, we need the sheet that this belongs to.
-     * Non existent cells are treated as empty.
-     * @param cell
-     * @param sheet
-     * @param workbook
-     */
-    protected static ValueEval getEvalForCell(HSSFCell cell, HSSFRow row, HSSFSheet sheet, HSSFWorkbook workbook) {
-
-        if (cell == null) {
-            return BlankEval.INSTANCE;
-        }
-        switch (cell.getCellType()) {
-            case HSSFCell.CELL_TYPE_NUMERIC:
-                return new NumberEval(cell.getNumericCellValue());
-            case HSSFCell.CELL_TYPE_STRING:
-                return new StringEval(cell.getRichStringCellValue().getString());
-            case HSSFCell.CELL_TYPE_FORMULA:
-                return internalEvaluate(cell, row, sheet, workbook);
-            case HSSFCell.CELL_TYPE_BOOLEAN:
-                return BoolEval.valueOf(cell.getBooleanCellValue());
-            case HSSFCell.CELL_TYPE_BLANK:
-                return BlankEval.INSTANCE;
-            case HSSFCell.CELL_TYPE_ERROR:
-                return ErrorEval.valueOf(cell.getErrorCellValue());
-        }
-        throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
-    }
-
-    /**
-     * Creates a Ref2DEval for ReferencePtg.
-     * Non existent cells are treated as RefEvals containing BlankEval.
-     */
-    private static Ref2DEval createRef2DEval(ReferencePtg ptg, HSSFCell cell, 
-            HSSFRow row, HSSFSheet sheet, HSSFWorkbook workbook) {
-        if (cell == null) {
-            return new Ref2DEval(ptg, BlankEval.INSTANCE);
-        }
-        
-        switch (cell.getCellType()) {
-            case HSSFCell.CELL_TYPE_NUMERIC:
-                return new Ref2DEval(ptg, new NumberEval(cell.getNumericCellValue()));
-            case HSSFCell.CELL_TYPE_STRING:
-                return new Ref2DEval(ptg, new StringEval(cell.getRichStringCellValue().getString()));
-            case HSSFCell.CELL_TYPE_FORMULA:
-                return new Ref2DEval(ptg, internalEvaluate(cell, row, sheet, workbook));
-            case HSSFCell.CELL_TYPE_BOOLEAN:
-                return new Ref2DEval(ptg, BoolEval.valueOf(cell.getBooleanCellValue()));
-            case HSSFCell.CELL_TYPE_BLANK:
-                return new Ref2DEval(ptg, BlankEval.INSTANCE);
-            case HSSFCell.CELL_TYPE_ERROR:
-                return new  Ref2DEval(ptg, ErrorEval.valueOf(cell.getErrorCellValue()));
-        }
-        throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
-    }
-
-    /**
-     * create a Ref3DEval for Ref3DPtg.
-     */
-    private static Ref3DEval createRef3DEval(Ref3DPtg ptg, HSSFCell cell, 
-            HSSFRow row, HSSFSheet sheet, HSSFWorkbook workbook) {
-        if (cell == null) {
-            return new Ref3DEval(ptg, BlankEval.INSTANCE);
-        }
-        switch (cell.getCellType()) {
-            case HSSFCell.CELL_TYPE_NUMERIC:
-                return new Ref3DEval(ptg, new NumberEval(cell.getNumericCellValue()));
-            case HSSFCell.CELL_TYPE_STRING:
-                return new Ref3DEval(ptg, new StringEval(cell.getRichStringCellValue().getString()));
-            case HSSFCell.CELL_TYPE_FORMULA:
-                return new Ref3DEval(ptg, internalEvaluate(cell, row, sheet, workbook));
-            case HSSFCell.CELL_TYPE_BOOLEAN:
-                return new Ref3DEval(ptg, BoolEval.valueOf(cell.getBooleanCellValue()));
-            case HSSFCell.CELL_TYPE_BLANK:
-                return new Ref3DEval(ptg, BlankEval.INSTANCE);
-            case HSSFCell.CELL_TYPE_ERROR:
-                return new Ref3DEval(ptg, ErrorEval.valueOf(cell.getErrorCellValue()));
-        }
-        throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
-    }
-    
-    /**
-     * Mimics the 'data view' of a cell. This allows formula evaluator 
-     * to return a CellValue instead of precasting the value to String
-     * or Number or boolean type.
-     * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
-     */
-    public static final class CellValue {
-        private int cellType;
-        private HSSFRichTextString richTextStringValue;
-        private double numberValue;
-        private boolean booleanValue;
-        private byte errorValue;
-        
-        /**
-         * CellType should be one of the types defined in HSSFCell
-         * @param cellType
-         */
-        public CellValue(int cellType) {
-            super();
-            this.cellType = cellType;
-        }
-        /**
-         * @return Returns the booleanValue.
-         */
-        public boolean getBooleanValue() {
-            return booleanValue;
-        }
-        /**
-         * @param booleanValue The booleanValue to set.
-         */
-        public void setBooleanValue(boolean booleanValue) {
-            this.booleanValue = booleanValue;
-        }
-        /**
-         * @return Returns the numberValue.
-         */
-        public double getNumberValue() {
-            return numberValue;
-        }
-        /**
-         * @param numberValue The numberValue to set.
-         */
-        public void setNumberValue(double numberValue) {
-            this.numberValue = numberValue;
-        }
-        /**
-         * @return Returns the stringValue. This method is deprecated, use
-         * getRichTextStringValue instead
-         * @deprecated
-         */
-        public String getStringValue() {
-            return richTextStringValue.getString();
-        }
-        /**
-         * @param stringValue The stringValue to set. This method is deprecated, use
-         * getRichTextStringValue instead.
-         * @deprecated
-         */
-        public void setStringValue(String stringValue) {
-            this.richTextStringValue = new HSSFRichTextString(stringValue);
-        }
-        /**
-         * @return Returns the cellType.
-         */
-        public int getCellType() {
-            return cellType;
-        }
-        /**
-         * @return Returns the errorValue.
-         */
-        public byte getErrorValue() {
-            return errorValue;
-        }
-        /**
-         * @param errorValue The errorValue to set.
-         */
-        public void setErrorValue(byte errorValue) {
-            this.errorValue = errorValue;
-        }
-        /**
-         * @return Returns the richTextStringValue.
-         */
-        public HSSFRichTextString getRichTextStringValue() {
-            return richTextStringValue;
-        }
-        /**
-         * @param richTextStringValue The richTextStringValue to set.
-         */
-        public void setRichTextStringValue(HSSFRichTextString richTextStringValue) {
-            this.richTextStringValue = richTextStringValue;
-        }
-    }
 
     /**
      * debug method
@@ -760,7 +56,8 @@ public class HSSFFormulaEvaluator {
      * @param workbook
      */
     void inspectPtgs(String formula) {
-        FormulaParser fp = new FormulaParser(formula, workbook.getWorkbook());
+       HSSFWorkbook hssfWb = (HSSFWorkbook)workbook;
+        FormulaParser fp = new FormulaParser(formula, hssfWb.getWorkbook());
         fp.parse();
         Ptg[] ptgs = fp.getRPNPtg();
         System.out.println("<ptg-group>");
@@ -775,4 +72,13 @@ public class HSSFFormulaEvaluator {
         System.out.println("</ptg-group>");
     }
 
+       /**
+     * Compatibility class.
+     * Seems to do more harm than good though
+     */
+//    public static class CellValue extends FormulaEvaluator.CellValue {
+//             public CellValue(int cellType, CreationHelper creationHelper) {
+//                     super(cellType, creationHelper);
+//             }
+//    }
 }
index e70b7713672ec74444688b0c8bddcad1fb013644..51499a99e47759b2dad301116159d6522921b79a 100644 (file)
@@ -610,7 +610,11 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
         return sheets.size();
     }
 
-    /**
+    public int getSheetIndexFromExternSheetIndex(int externSheetNumber) {
+       return workbook.getSheetIndexFromExternSheetIndex(externSheetNumber);
+       }
+
+       /**
      * Get the HSSFSheet object at the given index.
      * @param index of the sheet number (0-based physical & logical)
      * @return HSSFSheet at the provided index
diff --git a/src/java/org/apache/poi/hssf/usermodel/OperationEvaluatorFactory.java b/src/java/org/apache/poi/hssf/usermodel/OperationEvaluatorFactory.java
deleted file mode 100755 (executable)
index 1292009..0000000
+++ /dev/null
@@ -1,165 +0,0 @@
-/* ====================================================================
-   Licensed to the Apache Software Foundation (ASF) under one or more
-   contributor license agreements.  See the NOTICE file distributed with
-   this work for additional information regarding copyright ownership.
-   The ASF licenses this file to You under the Apache License, Version 2.0
-   (the "License"); you may not use this file except in compliance with
-   the License.  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-==================================================================== */
-
-package org.apache.poi.hssf.usermodel;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Modifier;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.poi.hssf.record.formula.AddPtg;
-import org.apache.poi.hssf.record.formula.ConcatPtg;
-import org.apache.poi.hssf.record.formula.DividePtg;
-import org.apache.poi.hssf.record.formula.EqualPtg;
-import org.apache.poi.hssf.record.formula.ExpPtg;
-import org.apache.poi.hssf.record.formula.FuncPtg;
-import org.apache.poi.hssf.record.formula.FuncVarPtg;
-import org.apache.poi.hssf.record.formula.GreaterEqualPtg;
-import org.apache.poi.hssf.record.formula.GreaterThanPtg;
-import org.apache.poi.hssf.record.formula.LessEqualPtg;
-import org.apache.poi.hssf.record.formula.LessThanPtg;
-import org.apache.poi.hssf.record.formula.MultiplyPtg;
-import org.apache.poi.hssf.record.formula.NotEqualPtg;
-import org.apache.poi.hssf.record.formula.OperationPtg;
-import org.apache.poi.hssf.record.formula.PercentPtg;
-import org.apache.poi.hssf.record.formula.PowerPtg;
-import org.apache.poi.hssf.record.formula.Ptg;
-import org.apache.poi.hssf.record.formula.SubtractPtg;
-import org.apache.poi.hssf.record.formula.UnaryMinusPtg;
-import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
-import org.apache.poi.hssf.record.formula.eval.AddEval;
-import org.apache.poi.hssf.record.formula.eval.ConcatEval;
-import org.apache.poi.hssf.record.formula.eval.DivideEval;
-import org.apache.poi.hssf.record.formula.eval.EqualEval;
-import org.apache.poi.hssf.record.formula.eval.FuncVarEval;
-import org.apache.poi.hssf.record.formula.eval.GreaterEqualEval;
-import org.apache.poi.hssf.record.formula.eval.GreaterThanEval;
-import org.apache.poi.hssf.record.formula.eval.LessEqualEval;
-import org.apache.poi.hssf.record.formula.eval.LessThanEval;
-import org.apache.poi.hssf.record.formula.eval.MultiplyEval;
-import org.apache.poi.hssf.record.formula.eval.NotEqualEval;
-import org.apache.poi.hssf.record.formula.eval.OperationEval;
-import org.apache.poi.hssf.record.formula.eval.PercentEval;
-import org.apache.poi.hssf.record.formula.eval.PowerEval;
-import org.apache.poi.hssf.record.formula.eval.SubtractEval;
-import org.apache.poi.hssf.record.formula.eval.UnaryMinusEval;
-import org.apache.poi.hssf.record.formula.eval.UnaryPlusEval;
-
-/**
- * This class creates <tt>OperationEval</tt> instances to help evaluate <tt>OperationPtg</tt>
- * formula tokens.
- * 
- * @author Josh Micich
- */
-final class OperationEvaluatorFactory {
-       private static final Class[] OPERATION_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class };
-       
-       private static final Map _constructorsByPtgClass = initialiseConstructorsMap();
-       
-       private OperationEvaluatorFactory() {
-               // no instances of this class
-       }
-       
-       private static Map initialiseConstructorsMap() {
-               Map m = new HashMap(32);
-               add(m, AddPtg.class, AddEval.class);
-               add(m, ConcatPtg.class, ConcatEval.class);
-               add(m, DividePtg.class, DivideEval.class);
-               add(m, EqualPtg.class, EqualEval.class);
-               add(m, FuncPtg.class, FuncVarEval.class);
-               add(m, FuncVarPtg.class, FuncVarEval.class);
-               add(m, GreaterEqualPtg.class, GreaterEqualEval.class);
-               add(m, GreaterThanPtg.class, GreaterThanEval.class);
-               add(m, LessEqualPtg.class, LessEqualEval.class);
-               add(m, LessThanPtg.class, LessThanEval.class);
-               add(m, MultiplyPtg.class, MultiplyEval.class);
-               add(m, NotEqualPtg.class, NotEqualEval.class);
-               add(m, PercentPtg.class, PercentEval.class);
-               add(m, PowerPtg.class, PowerEval.class);
-               add(m, SubtractPtg.class, SubtractEval.class);
-               add(m, UnaryMinusPtg.class, UnaryMinusEval.class);
-               add(m, UnaryPlusPtg.class, UnaryPlusEval.class);
-               return m;
-       }
-
-       private static void add(Map m, Class ptgClass, Class evalClass) {
-               
-               // perform some validation now, to keep later exception handlers simple
-               if(!Ptg.class.isAssignableFrom(ptgClass)) {
-                       throw new IllegalArgumentException("Expected Ptg subclass");
-               }
-               if(!OperationEval.class.isAssignableFrom(evalClass)) {
-                       throw new IllegalArgumentException("Expected OperationEval subclass");
-               }
-               if (!Modifier.isPublic(evalClass.getModifiers())) {
-                       throw new RuntimeException("Eval class must be public");
-               }
-               if (Modifier.isAbstract(evalClass.getModifiers())) {
-                       throw new RuntimeException("Eval class must not be abstract");
-               }
-               
-               Constructor constructor;
-               try {
-                       constructor = evalClass.getDeclaredConstructor(OPERATION_CONSTRUCTOR_CLASS_ARRAY);
-               } catch (NoSuchMethodException e) {
-                       throw new RuntimeException("Missing constructor");
-               }
-               if (!Modifier.isPublic(constructor.getModifiers())) {
-                       throw new RuntimeException("Eval constructor must be public");
-               }
-               m.put(ptgClass, constructor);
-       }
-       
-       /**
-        * returns the OperationEval concrete impl instance corresponding
-        * to the supplied operationPtg
-        */
-       public static OperationEval create(OperationPtg ptg) {
-               if(ptg == null) {
-                       throw new IllegalArgumentException("ptg must not be null");
-               }
-               
-               Class ptgClass = ptg.getClass();
-               
-               Constructor constructor = (Constructor) _constructorsByPtgClass.get(ptgClass);
-               if(constructor == null) {
-                       if(ptgClass == ExpPtg.class) {
-                               // ExpPtg is used for array formulas and shared formulas.
-                               // it is currently unsupported, and may not even get implemented here
-                               throw new RuntimeException("ExpPtg currently not supported");
-                       }
-                       throw new RuntimeException("Unexpected operation ptg class (" + ptgClass.getName() + ")");
-               }
-               
-               Object result;
-               Object[] initargs = { ptg };
-               try {
-                       result = constructor.newInstance(initargs);
-               } catch (IllegalArgumentException e) {
-                       throw new RuntimeException(e);
-               } catch (InstantiationException e) {
-                       throw new RuntimeException(e);
-               } catch (IllegalAccessException e) {
-                       throw new RuntimeException(e);
-               } catch (InvocationTargetException e) {
-                       throw new RuntimeException(e);
-               }
-               return (OperationEval) result;
-       }
-}
diff --git a/src/java/org/apache/poi/ss/usermodel/EvaluationCycleDetector.java b/src/java/org/apache/poi/ss/usermodel/EvaluationCycleDetector.java
new file mode 100755 (executable)
index 0000000..9cf3aa9
--- /dev/null
@@ -0,0 +1,153 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.ss.usermodel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+
+/**
+ * Instances of this class keep track of multiple dependent cell evaluations due
+ * to recursive calls to <tt>HSSFFormulaEvaluator.internalEvaluate()</tt>.
+ * The main purpose of this class is to detect an attempt to evaluate a cell
+ * that is already being evaluated. In other words, it detects circular
+ * references in spreadsheet formulas.
+ * 
+ * @author Josh Micich
+ */
+final class EvaluationCycleDetector {
+
+       /**
+        * Stores the parameters that identify the evaluation of one cell.<br/>
+        */
+       private static final class CellEvaluationFrame {
+
+               private final Workbook _workbook;
+               private final Sheet _sheet;
+               private final int _srcRowNum;
+               private final int _srcColNum;
+
+               public CellEvaluationFrame(Workbook workbook, Sheet sheet, int srcRowNum, int srcColNum) {
+                       if (workbook == null) {
+                               throw new IllegalArgumentException("workbook must not be null");
+                       }
+                       if (sheet == null) {
+                               throw new IllegalArgumentException("sheet must not be null");
+                       }
+                       _workbook = workbook;
+                       _sheet = sheet;
+                       _srcRowNum = srcRowNum;
+                       _srcColNum = srcColNum;
+               }
+
+               public boolean equals(Object obj) {
+                       CellEvaluationFrame other = (CellEvaluationFrame) obj;
+                       if (_workbook != other._workbook) {
+                               return false;
+                       }
+                       if (_sheet != other._sheet) {
+                               return false;
+                       }
+                       if (_srcRowNum != other._srcRowNum) {
+                               return false;
+                       }
+                       if (_srcColNum != other._srcColNum) {
+                               return false;
+                       }
+                       return true;
+               }
+
+               /**
+                * @return human readable string for debug purposes
+                */
+               public String formatAsString() {
+                       return "R=" + _srcRowNum + " C=" + _srcColNum + " ShIx=" + _workbook.getSheetIndex(_sheet);
+               }
+
+               public String toString() {
+                       StringBuffer sb = new StringBuffer(64);
+                       sb.append(getClass().getName()).append(" [");
+                       sb.append(formatAsString());
+                       sb.append("]");
+                       return sb.toString();
+               }
+       }
+
+       private final List _evaluationFrames;
+
+       public EvaluationCycleDetector() {
+               _evaluationFrames = new ArrayList();
+       }
+
+       /**
+        * Notifies this evaluation tracker that evaluation of the specified cell is
+        * about to start.<br/>
+        * 
+        * In the case of a <code>true</code> return code, the caller should
+        * continue evaluation of the specified cell, and also be sure to call
+        * <tt>endEvaluate()</tt> when complete.<br/>
+        * 
+        * In the case of a <code>false</code> return code, the caller should
+        * return an evaluation result of
+        * <tt>ErrorEval.CIRCULAR_REF_ERROR<tt>, and not call <tt>endEvaluate()</tt>.  
+        * <br/>
+        * @return <code>true</code> if the specified cell has not been visited yet in the current 
+        * evaluation. <code>false</code> if the specified cell is already being evaluated.
+        */
+       public boolean startEvaluate(Workbook workbook, Sheet sheet, int srcRowNum, int srcColNum) {
+               CellEvaluationFrame cef = new CellEvaluationFrame(workbook, sheet, srcRowNum, srcColNum);
+               if (_evaluationFrames.contains(cef)) {
+                       return false;
+               }
+               _evaluationFrames.add(cef);
+               return true;
+       }
+
+       /**
+        * Notifies this evaluation tracker that the evaluation of the specified
+        * cell is complete. <p/>
+        * 
+        * Every successful call to <tt>startEvaluate</tt> must be followed by a
+        * call to <tt>endEvaluate</tt> (recommended in a finally block) to enable
+        * proper tracking of which cells are being evaluated at any point in time.<p/>
+        * 
+        * Assuming a well behaved client, parameters to this method would not be
+        * required. However, they have been included to assert correct behaviour,
+        * and form more meaningful error messages.
+        */
+       public void endEvaluate(Workbook workbook, Sheet sheet, int srcRowNum, int srcColNum) {
+               int nFrames = _evaluationFrames.size();
+               if (nFrames < 1) {
+                       throw new IllegalStateException("Call to endEvaluate without matching call to startEvaluate");
+               }
+
+               nFrames--;
+               CellEvaluationFrame cefExpected = (CellEvaluationFrame) _evaluationFrames.get(nFrames);
+               CellEvaluationFrame cefActual = new CellEvaluationFrame(workbook, sheet, srcRowNum, srcColNum);
+               if (!cefActual.equals(cefExpected)) {
+                       throw new RuntimeException("Wrong cell specified. "
+                                       + "Corresponding startEvaluate() call was for cell {"
+                                       + cefExpected.formatAsString() + "} this endEvaluate() call is for cell {"
+                                       + cefActual.formatAsString() + "}");
+               }
+               // else - no problems so pop current frame 
+               _evaluationFrames.remove(nFrames);
+       }
+}
diff --git a/src/java/org/apache/poi/ss/usermodel/EvaluationCycleDetectorManager.java b/src/java/org/apache/poi/ss/usermodel/EvaluationCycleDetectorManager.java
new file mode 100755 (executable)
index 0000000..d83d187
--- /dev/null
@@ -0,0 +1,46 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.ss.usermodel;
+
+/**
+ * This class makes an <tt>EvaluationCycleDetector</tt> instance available to
+ * each thread via a <tt>ThreadLocal</tt> in order to avoid adding a parameter
+ * to a few protected methods within <tt>HSSFFormulaEvaluator</tt>.
+ * 
+ * @author Josh Micich
+ */
+final class EvaluationCycleDetectorManager {
+
+       ThreadLocal tl = null;
+       private static ThreadLocal _tlEvaluationTracker = new ThreadLocal() {
+               protected synchronized Object initialValue() {
+                       return new EvaluationCycleDetector();
+               }
+       };
+
+       /**
+        * @return
+        */
+       public static EvaluationCycleDetector getTracker() {
+               return (EvaluationCycleDetector) _tlEvaluationTracker.get();
+       }
+
+       private EvaluationCycleDetectorManager() {
+               // no instances of this class
+       }
+}
diff --git a/src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java b/src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java
new file mode 100644 (file)
index 0000000..80287f5
--- /dev/null
@@ -0,0 +1,759 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package org.apache.poi.ss.usermodel;
+
+import java.lang.reflect.Constructor;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Stack;
+
+import org.apache.poi.hssf.model.FormulaParser;
+import org.apache.poi.hssf.record.formula.Area3DPtg;
+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.IntPtg;
+import org.apache.poi.hssf.record.formula.MemErrPtg;
+import org.apache.poi.hssf.record.formula.MissingArgPtg;
+import org.apache.poi.hssf.record.formula.NamePtg;
+import org.apache.poi.hssf.record.formula.NameXPtg;
+import org.apache.poi.hssf.record.formula.NumberPtg;
+import org.apache.poi.hssf.record.formula.OperationPtg;
+import org.apache.poi.hssf.record.formula.ParenthesisPtg;
+import org.apache.poi.hssf.record.formula.Ptg;
+import org.apache.poi.hssf.record.formula.Ref3DPtg;
+import org.apache.poi.hssf.record.formula.ReferencePtg;
+import org.apache.poi.hssf.record.formula.StringPtg;
+import org.apache.poi.hssf.record.formula.UnionPtg;
+import org.apache.poi.hssf.record.formula.UnknownPtg;
+import org.apache.poi.hssf.record.formula.eval.Area2DEval;
+import org.apache.poi.hssf.record.formula.eval.Area3DEval;
+import org.apache.poi.hssf.record.formula.eval.AreaEval;
+import org.apache.poi.hssf.record.formula.eval.BlankEval;
+import org.apache.poi.hssf.record.formula.eval.BoolEval;
+import org.apache.poi.hssf.record.formula.eval.ErrorEval;
+import org.apache.poi.hssf.record.formula.eval.Eval;
+import org.apache.poi.hssf.record.formula.eval.FunctionEval;
+import org.apache.poi.hssf.record.formula.eval.NameEval;
+import org.apache.poi.hssf.record.formula.eval.NumberEval;
+import org.apache.poi.hssf.record.formula.eval.OperationEval;
+import org.apache.poi.hssf.record.formula.eval.Ref2DEval;
+import org.apache.poi.hssf.record.formula.eval.Ref3DEval;
+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.usermodel.HSSFFormulaEvaluator;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+
+/**
+ * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
+ * 
+ */
+public class FormulaEvaluator {
+                
+    // params to lookup the right constructor using reflection
+    private static final Class[] VALUE_CONTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class };
+
+    private static final Class[] AREA3D_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class, ValueEval[].class };
+
+    private static final Class[] REFERENCE_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class, ValueEval.class };
+
+    private static final Class[] REF3D_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class, ValueEval.class };
+
+    // Maps for mapping *Eval to *Ptg
+    private static final Map VALUE_EVALS_MAP = new HashMap();
+
+    /*
+     * Following is the mapping between the Ptg tokens returned 
+     * by the FormulaParser and the *Eval classes that are used 
+     * by the FormulaEvaluator
+     */
+    static {
+        VALUE_EVALS_MAP.put(BoolPtg.class, BoolEval.class);
+        VALUE_EVALS_MAP.put(IntPtg.class, NumberEval.class);
+        VALUE_EVALS_MAP.put(NumberPtg.class, NumberEval.class);
+        VALUE_EVALS_MAP.put(StringPtg.class, StringEval.class);
+
+    }
+
+    
+    protected Row row;
+    protected Sheet sheet;
+    protected Workbook workbook;
+    
+    public FormulaEvaluator(Sheet sheet, Workbook workbook) {
+        this.sheet = sheet;
+        this.workbook = workbook;
+    }
+    
+    public void setCurrentRow(Row row) {
+        this.row = row;
+    }
+
+    /**
+     * If cell contains a formula, the formula is evaluated and returned,
+     * else the CellValue simply copies the appropriate cell value from
+     * the cell and also its cell type. This method should be preferred over
+     * evaluateInCell() when the call should not modify the contents of the
+     * original cell. 
+     * @param cell
+     */
+    public CellValue evaluate(Cell cell) {
+        CellValue retval = null;
+        if (cell != null) {
+            switch (cell.getCellType()) {
+            case Cell.CELL_TYPE_BLANK:
+                retval = new CellValue(Cell.CELL_TYPE_BLANK, workbook.getCreationHelper());
+                break;
+            case Cell.CELL_TYPE_BOOLEAN:
+                retval = new CellValue(Cell.CELL_TYPE_BOOLEAN, workbook.getCreationHelper());
+                retval.setBooleanValue(cell.getBooleanCellValue());
+                break;
+            case Cell.CELL_TYPE_ERROR:
+                retval = new CellValue(Cell.CELL_TYPE_ERROR, workbook.getCreationHelper());
+                retval.setErrorValue(cell.getErrorCellValue());
+                break;
+            case Cell.CELL_TYPE_FORMULA:
+                retval = getCellValueForEval(internalEvaluate(cell, row, sheet, workbook), workbook.getCreationHelper());
+                break;
+            case Cell.CELL_TYPE_NUMERIC:
+                retval = new CellValue(Cell.CELL_TYPE_NUMERIC, workbook.getCreationHelper());
+                retval.setNumberValue(cell.getNumericCellValue());
+                break;
+            case Cell.CELL_TYPE_STRING:
+                retval = new CellValue(Cell.CELL_TYPE_STRING, workbook.getCreationHelper());
+                retval.setRichTextStringValue(cell.getRichStringCellValue());
+                break;
+            }
+        }
+        return retval;
+    }
+    
+    
+    /**
+     * If cell contains formula, it evaluates the formula,
+     *  and saves the result of the formula. The cell
+     *  remains as a formula cell.
+     * Else if cell does not contain formula, this method leaves
+     *  the cell unchanged. 
+     * Note that the type of the formula result is returned,
+     *  so you know what kind of value is also stored with
+     *  the formula. 
+     * <pre>
+     * int evaluatedCellType = evaluator.evaluateFormulaCell(cell);
+     * </pre>
+     * Be aware that your cell will hold both the formula,
+     *  and the result. If you want the cell replaced with
+     *  the result of the formula, use {@link #evaluateInCell(HSSFCell)}
+     * @param cell The cell to evaluate
+     * @return The type of the formula result (the cell's type remains as HSSFCell.CELL_TYPE_FORMULA however)
+     */
+    public int evaluateFormulaCell(Cell cell) {
+        if (cell != null) {
+            switch (cell.getCellType()) {
+            case Cell.CELL_TYPE_FORMULA:
+                CellValue cv = getCellValueForEval(internalEvaluate(cell, row, sheet, workbook), workbook.getCreationHelper());
+                switch (cv.getCellType()) {
+                case Cell.CELL_TYPE_BOOLEAN:
+                    cell.setCellValue(cv.getBooleanValue());
+                    break;
+                case Cell.CELL_TYPE_ERROR:
+                    cell.setCellValue(cv.getErrorValue());
+                    break;
+                case Cell.CELL_TYPE_NUMERIC:
+                    cell.setCellValue(cv.getNumberValue());
+                    break;
+                case Cell.CELL_TYPE_STRING:
+                    cell.setCellValue(cv.getRichTextStringValue());
+                    break;
+                case Cell.CELL_TYPE_BLANK:
+                    break;
+                case Cell.CELL_TYPE_FORMULA: // this will never happen, we have already evaluated the formula
+                    break;
+                }
+                return cv.getCellType();
+            }
+        }
+        return -1;
+    }
+        
+    /**
+     * If cell contains formula, it evaluates the formula, and
+     *  puts the formula result back into the cell, in place
+     *  of the old formula.
+     * Else if cell does not contain formula, this method leaves
+     *  the cell unchanged. 
+     * Note that the same instance of HSSFCell is returned to 
+     * allow chained calls like:
+     * <pre>
+     * int evaluatedCellType = evaluator.evaluateInCell(cell).getCellType();
+     * </pre>
+     * Be aware that your cell value will be changed to hold the
+     *  result of the formula. If you simply want the formula
+     *  value computed for you, use {@link #evaluateFormulaCell(HSSFCell)}
+     * @param cell
+     */
+    public Cell evaluateInCell(Cell cell) {
+        if (cell != null) {
+            switch (cell.getCellType()) {
+            case Cell.CELL_TYPE_FORMULA:
+                CellValue cv = getCellValueForEval(internalEvaluate(cell, row, sheet, workbook), workbook.getCreationHelper());
+                switch (cv.getCellType()) {
+                case Cell.CELL_TYPE_BOOLEAN:
+                    cell.setCellType(Cell.CELL_TYPE_BOOLEAN);
+                    cell.setCellValue(cv.getBooleanValue());
+                    break;
+                case Cell.CELL_TYPE_ERROR:
+                    cell.setCellType(Cell.CELL_TYPE_ERROR);
+                    cell.setCellValue(cv.getErrorValue());
+                    break;
+                case Cell.CELL_TYPE_NUMERIC:
+                    cell.setCellType(Cell.CELL_TYPE_NUMERIC);
+                    cell.setCellValue(cv.getNumberValue());
+                    break;
+                case Cell.CELL_TYPE_STRING:
+                    cell.setCellType(Cell.CELL_TYPE_STRING);
+                    cell.setCellValue(cv.getRichTextStringValue());
+                    break;
+                case Cell.CELL_TYPE_BLANK:
+                    break;
+                case Cell.CELL_TYPE_FORMULA: // this will never happen, we have already evaluated the formula
+                    break;
+                }
+            }
+        }
+        return cell;
+    }
+    
+    /**
+     * Loops over all cells in all sheets of the supplied
+     *  workbook.
+     * For cells that contain formulas, their formulas are
+     *  evaluated, and the results are saved. These cells
+     *  remain as formula cells.
+     * For cells that do not contain formulas, no changes
+     *  are made.
+     * This is a helpful wrapper around looping over all 
+     *  cells, and calling evaluateFormulaCell on each one.
+     */
+       public static void evaluateAllFormulaCells(Workbook wb) {
+               for(int i=0; i<wb.getNumberOfSheets(); i++) {
+                       Sheet sheet = wb.getSheetAt(i);
+                       FormulaEvaluator evaluator = new FormulaEvaluator(sheet, wb);
+
+                       for (Iterator rit = sheet.rowIterator(); rit.hasNext();) {
+                               Row r = (Row)rit.next();
+                               evaluator.setCurrentRow(r);
+
+                               for (Iterator cit = r.cellIterator(); cit.hasNext();) {
+                                       Cell c = (Cell)cit.next();
+                                       if (c.getCellType() == Cell.CELL_TYPE_FORMULA)
+                                               evaluator.evaluateFormulaCell(c);
+                               }
+                       }
+               }
+       }
+        
+    
+    /**
+     * Returns a CellValue wrapper around the supplied ValueEval instance.
+     * @param eval
+     */
+    protected static CellValue getCellValueForEval(ValueEval eval, CreationHelper cHelper) {
+        CellValue retval = null;
+        if (eval != null) {
+            if (eval instanceof NumberEval) {
+                NumberEval ne = (NumberEval) eval;
+                retval = new CellValue(Cell.CELL_TYPE_NUMERIC, cHelper);
+                retval.setNumberValue(ne.getNumberValue());
+            }
+            else if (eval instanceof BoolEval) {
+                BoolEval be = (BoolEval) eval;
+                retval = new CellValue(Cell.CELL_TYPE_BOOLEAN, cHelper);
+                retval.setBooleanValue(be.getBooleanValue());
+            }
+            else if (eval instanceof StringEval) {
+                StringEval ne = (StringEval) eval;
+                retval = new CellValue(Cell.CELL_TYPE_STRING, cHelper);
+                retval.setStringValue(ne.getStringValue());
+            }
+            else if (eval instanceof BlankEval) {
+                retval = new CellValue(Cell.CELL_TYPE_BLANK, cHelper);
+            }
+            else if (eval instanceof ErrorEval) {
+                retval = new CellValue(Cell.CELL_TYPE_ERROR, cHelper);
+                retval.setErrorValue((byte)((ErrorEval)eval).getErrorCode());
+//                retval.setRichTextStringValue(new HSSFRichTextString("#An error occurred. check cell.getErrorCode()"));
+            }
+            else {
+                retval = new CellValue(Cell.CELL_TYPE_ERROR, cHelper);
+            }
+        }
+        return retval;
+    }
+    
+    /**
+     * Dev. Note: Internal evaluate must be passed only a formula cell 
+     * else a runtime exception will be thrown somewhere inside the method.
+     * (Hence this is a private method.)
+     */
+    private static ValueEval internalEvaluate(Cell srcCell, Row srcRow, Sheet sheet, Workbook workbook) {
+        int srcRowNum = srcRow.getRowNum();
+        short srcColNum = srcCell.getCellNum();
+        
+        
+        EvaluationCycleDetector tracker = EvaluationCycleDetectorManager.getTracker();
+        
+        if(!tracker.startEvaluate(workbook, sheet, srcRowNum, srcColNum)) {
+            return ErrorEval.CIRCULAR_REF_ERROR;
+        }
+        try {
+            return evaluateCell(workbook, sheet, srcRowNum, srcColNum, srcCell.getCellFormula());
+        } finally {
+            tracker.endEvaluate(workbook, sheet, srcRowNum, srcColNum);
+        }
+    }
+    private static ValueEval evaluateCell(Workbook workbook, Sheet sheet, 
+            int srcRowNum, short srcColNum, String cellFormulaText) {
+       
+       FormulaParser parser;
+       if(workbook instanceof HSSFWorkbook) {
+               parser = HSSFFormulaEvaluator.getUnderlyingParser(
+                               (HSSFWorkbook)workbook,
+                               cellFormulaText
+               );
+       } else {
+               // Hope for the best...
+               parser = new FormulaParser(cellFormulaText, null);
+       }
+       
+        parser.parse();
+        Ptg[] ptgs = parser.getRPNPtg();
+        // -- parsing over --
+        
+
+        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 ControlPtg) { continue; }
+            if (ptg instanceof MemErrPtg) { continue; }
+            if (ptg instanceof MissingArgPtg) { continue; }
+            if (ptg instanceof NamePtg) { 
+               // named ranges, macro functions
+                NamePtg namePtg = (NamePtg) ptg;
+                stack.push(new NameEval(namePtg.getIndex()));
+                continue; 
+            }
+            if (ptg instanceof NameXPtg) {
+               // TODO - external functions
+                continue;
+            }
+            if (ptg instanceof UnknownPtg) { continue; }
+
+            if (ptg instanceof OperationPtg) {
+                OperationPtg optg = (OperationPtg) ptg;
+
+                // parens can be ignored since we have RPN tokens
+                if (optg instanceof ParenthesisPtg) { continue; }
+                if (optg instanceof AttrPtg) { continue; }
+                if (optg instanceof UnionPtg) { continue; }
+
+                OperationEval operation = OperationEvaluatorFactory.create(optg);
+
+                int numops = operation.getNumberOfOperands();
+                Eval[] ops = new Eval[numops];
+
+                // storing the ops in reverse order since they are popping
+                for (int j = numops - 1; j >= 0; j--) {
+                    Eval p = (Eval) stack.pop();
+                    ops[j] = p;
+                }
+                Eval opresult = invokeOperation(operation, ops, srcRowNum, srcColNum, workbook, sheet);
+                stack.push(opresult);
+            }
+            else if (ptg instanceof ReferencePtg) {
+                ReferencePtg refPtg = (ReferencePtg) ptg;
+                int colIx = refPtg.getColumn();
+                int rowIx = refPtg.getRow();
+                Row row = sheet.getRow(rowIx);
+                Cell cell = (row != null) ? row.getCell(colIx) : null;
+                stack.push(createRef2DEval(refPtg, cell, row, sheet, workbook));
+            }
+            else if (ptg instanceof Ref3DPtg) {
+                Ref3DPtg refPtg = (Ref3DPtg) ptg;
+                int colIx = refPtg.getColumn();
+                int rowIx = refPtg.getRow();
+                Sheet xsheet = workbook.getSheetAt(
+                               workbook.getSheetIndexFromExternSheetIndex(refPtg.getExternSheetIndex())
+                );
+                Row row = xsheet.getRow(rowIx);
+                Cell cell = (row != null) ? row.getCell(colIx) : null;
+                stack.push(createRef3DEval(refPtg, cell, row, xsheet, workbook));
+            }
+            else if (ptg instanceof AreaPtg) {
+                AreaPtg ap = (AreaPtg) ptg;
+                AreaEval ae = evaluateAreaPtg(sheet, workbook, ap);
+                stack.push(ae);
+            }
+            else if (ptg instanceof Area3DPtg) {
+                Area3DPtg a3dp = (Area3DPtg) ptg;
+                AreaEval ae = evaluateArea3dPtg(workbook, a3dp);
+                stack.push(ae);
+            }
+            else {
+                Eval ptgEval = getEvalForPtg(ptg);
+                stack.push(ptgEval);
+            }
+        }
+
+        ValueEval value = ((ValueEval) stack.pop());
+        if (!stack.isEmpty()) {
+            throw new IllegalStateException("evaluation stack not empty");
+        }
+        value = dereferenceValue(value, srcRowNum, srcColNum);
+        if (value instanceof BlankEval) {
+               // Note Excel behaviour here. A blank final final value is converted to zero.  
+            return NumberEval.ZERO;
+            // Formulas _never_ evaluate to blank.  If a formula appears to have evaluated to 
+            // blank, the actual value is empty string. This can be verified with ISBLANK().
+        }
+        return value;
+    }
+
+    /**
+     * Dereferences a single value from any AreaEval or RefEval evaluation result.
+     * If the supplied evaluationResult is just a plain value, it is returned as-is.
+        * @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) {
+        if (evaluationResult instanceof RefEval) {
+            RefEval rv = (RefEval) evaluationResult;
+            return rv.getInnerValueEval();
+        }
+        if (evaluationResult instanceof AreaEval) {
+            AreaEval ae = (AreaEval) evaluationResult;
+            if (ae.isRow()) {
+                if(ae.isColumn()) {
+                    return ae.getValues()[0];
+                }
+                return ae.getValueAt(ae.getFirstRow(), srcColNum);
+            }
+            if (ae.isColumn()) {
+                return ae.getValueAt(srcRowNum, ae.getFirstColumn());
+            }
+            return ErrorEval.VALUE_INVALID;
+        }
+        return evaluationResult;
+    }
+
+    private static Eval invokeOperation(OperationEval operation, Eval[] ops, int srcRowNum, short srcColNum,
+            Workbook workbook, Sheet sheet) {
+
+        if(operation instanceof FunctionEval) {
+            FunctionEval fe = (FunctionEval) operation;
+            if(fe.isFreeRefFunction()) {
+                return fe.getFreeRefFunction().evaluate(ops, srcRowNum, srcColNum, workbook, sheet);
+            }
+        }
+        return operation.evaluate(ops, srcRowNum, srcColNum);
+    }
+    
+    public static AreaEval evaluateAreaPtg(Sheet sheet, Workbook workbook, AreaPtg ap) {
+        int row0 = ap.getFirstRow();
+        int col0 = ap.getFirstColumn();
+        int row1 = ap.getLastRow();
+        int col1 = ap.getLastColumn();
+        
+        // If the last row is -1, then the
+        //  reference is for the rest of the column
+        // (eg C:C)
+        // TODO: Handle whole column ranges properly
+        if(row1 == -1 && row0 >= 0) {
+            row1 = (short)sheet.getLastRowNum();
+        }
+        ValueEval[] values = evalArea(workbook, sheet, row0, col0, row1, col1);
+        return new Area2DEval(ap, values);
+    }
+
+    public static AreaEval evaluateArea3dPtg(Workbook workbook, Area3DPtg a3dp) {
+       int row0 = a3dp.getFirstRow();
+       int col0 = a3dp.getFirstColumn();
+       int row1 = a3dp.getLastRow();
+       int col1 = a3dp.getLastColumn();
+        Sheet xsheet = workbook.getSheetAt(
+                       workbook.getSheetIndexFromExternSheetIndex(a3dp.getExternSheetIndex())
+        );
+        
+        // If the last row is -1, then the
+        //  reference is for the rest of the column
+        // (eg C:C)
+        // TODO: Handle whole column ranges properly
+        if(row1 == -1 && row0 >= 0) {
+            row1 = (short)xsheet.getLastRowNum();
+        }
+        
+        ValueEval[] values = evalArea(workbook, xsheet, row0, col0, row1, col1);
+        return new Area3DEval(a3dp, values);
+    }
+    
+    private static ValueEval[] evalArea(Workbook workbook, Sheet sheet, 
+               int row0, int col0, int row1, int col1) {
+        ValueEval[] values = new ValueEval[(row1 - row0 + 1) * (col1 - col0 + 1)];
+        for (int x = row0; sheet != null && x < row1 + 1; x++) {
+            Row row = sheet.getRow(x);
+            for (int y = col0; y < col1 + 1; y++) {
+                ValueEval cellEval;
+                if(row == null) {
+                       cellEval = BlankEval.INSTANCE;
+                } else {
+                       cellEval = getEvalForCell(row.getCell(y), row, sheet, workbook);
+                }
+                               values[(x - row0) * (col1 - col0 + 1) + (y - col0)] = cellEval;
+            }
+        }
+        return values;
+    }
+
+    /**
+     * returns an appropriate Eval impl instance for the Ptg. The Ptg must be
+     * one of: Area3DPtg, AreaPtg, ReferencePtg, Ref3DPtg, IntPtg, NumberPtg,
+     * StringPtg, BoolPtg <br/>special Note: OperationPtg subtypes cannot be
+     * passed here!
+     * 
+     * @param ptg
+     */
+    protected static Eval getEvalForPtg(Ptg ptg) {
+        Eval retval = null;
+
+        Class clazz = (Class) VALUE_EVALS_MAP.get(ptg.getClass());
+        try {
+            if (ptg instanceof Area3DPtg) {
+                Constructor constructor = clazz.getConstructor(AREA3D_CONSTRUCTOR_CLASS_ARRAY);
+                retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg });
+            }
+            else if (ptg instanceof AreaPtg) {
+                Constructor constructor = clazz.getConstructor(AREA3D_CONSTRUCTOR_CLASS_ARRAY);
+                retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg });
+            }
+            else if (ptg instanceof ReferencePtg) {
+                Constructor constructor = clazz.getConstructor(REFERENCE_CONSTRUCTOR_CLASS_ARRAY);
+                retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg });
+            }
+            else if (ptg instanceof Ref3DPtg) {
+                Constructor constructor = clazz.getConstructor(REF3D_CONSTRUCTOR_CLASS_ARRAY);
+                retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg });
+            }
+            else {
+                if (ptg instanceof IntPtg || ptg instanceof NumberPtg || ptg instanceof StringPtg
+                        || ptg instanceof BoolPtg) {
+                    Constructor constructor = clazz.getConstructor(VALUE_CONTRUCTOR_CLASS_ARRAY);
+                    retval = (ValueEval) constructor.newInstance(new Ptg[] { ptg });
+                }
+            }
+        }
+        catch (Exception e) {
+            throw new RuntimeException("Fatal Error: ", e);
+        }
+        return retval;
+
+    }
+
+    /**
+     * Given a cell, find its type and from that create an appropriate ValueEval
+     * impl instance and return that. Since the cell could be an external
+     * reference, we need the sheet that this belongs to.
+     * Non existent cells are treated as empty.
+     * @param cell
+     * @param sheet
+     * @param workbook
+     */
+    protected static ValueEval getEvalForCell(Cell cell, Row row, Sheet sheet, Workbook workbook) {
+
+        if (cell == null) {
+            return BlankEval.INSTANCE;
+        }
+        switch (cell.getCellType()) {
+            case Cell.CELL_TYPE_NUMERIC:
+                return new NumberEval(cell.getNumericCellValue());
+            case Cell.CELL_TYPE_STRING:
+                return new StringEval(cell.getRichStringCellValue().getString());
+            case Cell.CELL_TYPE_FORMULA:
+                return internalEvaluate(cell, row, sheet, workbook);
+            case Cell.CELL_TYPE_BOOLEAN:
+                return BoolEval.valueOf(cell.getBooleanCellValue());
+            case Cell.CELL_TYPE_BLANK:
+                return BlankEval.INSTANCE;
+            case Cell.CELL_TYPE_ERROR:
+                return ErrorEval.valueOf(cell.getErrorCellValue());
+        }
+        throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
+    }
+
+    /**
+     * Creates a Ref2DEval for ReferencePtg.
+     * Non existent cells are treated as RefEvals containing BlankEval.
+     */
+    private static Ref2DEval createRef2DEval(ReferencePtg ptg, Cell cell, 
+            Row row, Sheet sheet, Workbook workbook) {
+        if (cell == null) {
+            return new Ref2DEval(ptg, BlankEval.INSTANCE);
+        }
+        
+        switch (cell.getCellType()) {
+            case Cell.CELL_TYPE_NUMERIC:
+                return new Ref2DEval(ptg, new NumberEval(cell.getNumericCellValue()));
+            case Cell.CELL_TYPE_STRING:
+                return new Ref2DEval(ptg, new StringEval(cell.getRichStringCellValue().getString()));
+            case Cell.CELL_TYPE_FORMULA:
+                return new Ref2DEval(ptg, internalEvaluate(cell, row, sheet, workbook));
+            case Cell.CELL_TYPE_BOOLEAN:
+                return new Ref2DEval(ptg, BoolEval.valueOf(cell.getBooleanCellValue()));
+            case Cell.CELL_TYPE_BLANK:
+                return new Ref2DEval(ptg, BlankEval.INSTANCE);
+            case Cell.CELL_TYPE_ERROR:
+                return new  Ref2DEval(ptg, ErrorEval.valueOf(cell.getErrorCellValue()));
+        }
+        throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
+    }
+
+    /**
+     * create a Ref3DEval for Ref3DPtg.
+     */
+    private static Ref3DEval createRef3DEval(Ref3DPtg ptg, Cell cell, 
+            Row row, Sheet sheet, Workbook workbook) {
+        if (cell == null) {
+            return new Ref3DEval(ptg, BlankEval.INSTANCE);
+        }
+        switch (cell.getCellType()) {
+            case Cell.CELL_TYPE_NUMERIC:
+                return new Ref3DEval(ptg, new NumberEval(cell.getNumericCellValue()));
+            case Cell.CELL_TYPE_STRING:
+                return new Ref3DEval(ptg, new StringEval(cell.getRichStringCellValue().getString()));
+            case Cell.CELL_TYPE_FORMULA:
+                return new Ref3DEval(ptg, internalEvaluate(cell, row, sheet, workbook));
+            case Cell.CELL_TYPE_BOOLEAN:
+                return new Ref3DEval(ptg, BoolEval.valueOf(cell.getBooleanCellValue()));
+            case Cell.CELL_TYPE_BLANK:
+                return new Ref3DEval(ptg, BlankEval.INSTANCE);
+            case Cell.CELL_TYPE_ERROR:
+                return new Ref3DEval(ptg, ErrorEval.valueOf(cell.getErrorCellValue()));
+        }
+        throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
+    }
+    
+    /**
+     * Mimics the 'data view' of a cell. This allows formula evaluator 
+     * to return a CellValue instead of precasting the value to String
+     * or Number or boolean type.
+     * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
+     */
+    public static class CellValue {
+       private CreationHelper creationHelper;
+        private int cellType;
+        private RichTextString richTextStringValue;
+        private double numberValue;
+        private boolean booleanValue;
+        private byte errorValue;
+        
+        /**
+         * CellType should be one of the types defined in HSSFCell
+         * @param cellType
+         */
+        public CellValue(int cellType, CreationHelper creationHelper) {
+            super();
+            this.creationHelper = creationHelper;
+            this.cellType = cellType;
+        }
+        /**
+         * @return Returns the booleanValue.
+         */
+        public boolean getBooleanValue() {
+            return booleanValue;
+        }
+        /**
+         * @param booleanValue The booleanValue to set.
+         */
+        public void setBooleanValue(boolean booleanValue) {
+            this.booleanValue = booleanValue;
+        }
+        /**
+         * @return Returns the numberValue.
+         */
+        public double getNumberValue() {
+            return numberValue;
+        }
+        /**
+         * @param numberValue The numberValue to set.
+         */
+        public void setNumberValue(double numberValue) {
+            this.numberValue = numberValue;
+        }
+        /**
+         * @return Returns the stringValue. This method is deprecated, use
+         * getRichTextStringValue instead
+         * @deprecated
+         */
+        public String getStringValue() {
+            return richTextStringValue.getString();
+        }
+        /**
+         * @param stringValue The stringValue to set. This method is deprecated, use
+         * getRichTextStringValue instead.
+         * @deprecated
+         */
+        public void setStringValue(String stringValue) {
+            this.richTextStringValue =
+               creationHelper.createRichTextString(stringValue);
+        }
+        /**
+         * @return Returns the cellType.
+         */
+        public int getCellType() {
+            return cellType;
+        }
+        /**
+         * @return Returns the errorValue.
+         */
+        public byte getErrorValue() {
+            return errorValue;
+        }
+        /**
+         * @param errorValue The errorValue to set.
+         */
+        public void setErrorValue(byte errorValue) {
+            this.errorValue = errorValue;
+        }
+        /**
+         * @return Returns the richTextStringValue.
+         */
+        public RichTextString getRichTextStringValue() {
+            return richTextStringValue;
+        }
+        /**
+         * @param richTextStringValue The richTextStringValue to set.
+         */
+        public void setRichTextStringValue(RichTextString richTextStringValue) {
+            this.richTextStringValue = richTextStringValue;
+        }
+    }
+}
diff --git a/src/java/org/apache/poi/ss/usermodel/OperationEvaluatorFactory.java b/src/java/org/apache/poi/ss/usermodel/OperationEvaluatorFactory.java
new file mode 100755 (executable)
index 0000000..ed136e0
--- /dev/null
@@ -0,0 +1,165 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.ss.usermodel;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.poi.hssf.record.formula.AddPtg;
+import org.apache.poi.hssf.record.formula.ConcatPtg;
+import org.apache.poi.hssf.record.formula.DividePtg;
+import org.apache.poi.hssf.record.formula.EqualPtg;
+import org.apache.poi.hssf.record.formula.ExpPtg;
+import org.apache.poi.hssf.record.formula.FuncPtg;
+import org.apache.poi.hssf.record.formula.FuncVarPtg;
+import org.apache.poi.hssf.record.formula.GreaterEqualPtg;
+import org.apache.poi.hssf.record.formula.GreaterThanPtg;
+import org.apache.poi.hssf.record.formula.LessEqualPtg;
+import org.apache.poi.hssf.record.formula.LessThanPtg;
+import org.apache.poi.hssf.record.formula.MultiplyPtg;
+import org.apache.poi.hssf.record.formula.NotEqualPtg;
+import org.apache.poi.hssf.record.formula.OperationPtg;
+import org.apache.poi.hssf.record.formula.PercentPtg;
+import org.apache.poi.hssf.record.formula.PowerPtg;
+import org.apache.poi.hssf.record.formula.Ptg;
+import org.apache.poi.hssf.record.formula.SubtractPtg;
+import org.apache.poi.hssf.record.formula.UnaryMinusPtg;
+import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
+import org.apache.poi.hssf.record.formula.eval.AddEval;
+import org.apache.poi.hssf.record.formula.eval.ConcatEval;
+import org.apache.poi.hssf.record.formula.eval.DivideEval;
+import org.apache.poi.hssf.record.formula.eval.EqualEval;
+import org.apache.poi.hssf.record.formula.eval.FuncVarEval;
+import org.apache.poi.hssf.record.formula.eval.GreaterEqualEval;
+import org.apache.poi.hssf.record.formula.eval.GreaterThanEval;
+import org.apache.poi.hssf.record.formula.eval.LessEqualEval;
+import org.apache.poi.hssf.record.formula.eval.LessThanEval;
+import org.apache.poi.hssf.record.formula.eval.MultiplyEval;
+import org.apache.poi.hssf.record.formula.eval.NotEqualEval;
+import org.apache.poi.hssf.record.formula.eval.OperationEval;
+import org.apache.poi.hssf.record.formula.eval.PercentEval;
+import org.apache.poi.hssf.record.formula.eval.PowerEval;
+import org.apache.poi.hssf.record.formula.eval.SubtractEval;
+import org.apache.poi.hssf.record.formula.eval.UnaryMinusEval;
+import org.apache.poi.hssf.record.formula.eval.UnaryPlusEval;
+
+/**
+ * This class creates <tt>OperationEval</tt> instances to help evaluate <tt>OperationPtg</tt>
+ * formula tokens.
+ * 
+ * @author Josh Micich
+ */
+final class OperationEvaluatorFactory {
+       private static final Class[] OPERATION_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class };
+       
+       private static final Map _constructorsByPtgClass = initialiseConstructorsMap();
+       
+       private OperationEvaluatorFactory() {
+               // no instances of this class
+       }
+       
+       private static Map initialiseConstructorsMap() {
+               Map m = new HashMap(32);
+               add(m, AddPtg.class, AddEval.class);
+               add(m, ConcatPtg.class, ConcatEval.class);
+               add(m, DividePtg.class, DivideEval.class);
+               add(m, EqualPtg.class, EqualEval.class);
+               add(m, FuncPtg.class, FuncVarEval.class);
+               add(m, FuncVarPtg.class, FuncVarEval.class);
+               add(m, GreaterEqualPtg.class, GreaterEqualEval.class);
+               add(m, GreaterThanPtg.class, GreaterThanEval.class);
+               add(m, LessEqualPtg.class, LessEqualEval.class);
+               add(m, LessThanPtg.class, LessThanEval.class);
+               add(m, MultiplyPtg.class, MultiplyEval.class);
+               add(m, NotEqualPtg.class, NotEqualEval.class);
+               add(m, PercentPtg.class, PercentEval.class);
+               add(m, PowerPtg.class, PowerEval.class);
+               add(m, SubtractPtg.class, SubtractEval.class);
+               add(m, UnaryMinusPtg.class, UnaryMinusEval.class);
+               add(m, UnaryPlusPtg.class, UnaryPlusEval.class);
+               return m;
+       }
+
+       private static void add(Map m, Class ptgClass, Class evalClass) {
+               
+               // perform some validation now, to keep later exception handlers simple
+               if(!Ptg.class.isAssignableFrom(ptgClass)) {
+                       throw new IllegalArgumentException("Expected Ptg subclass");
+               }
+               if(!OperationEval.class.isAssignableFrom(evalClass)) {
+                       throw new IllegalArgumentException("Expected OperationEval subclass");
+               }
+               if (!Modifier.isPublic(evalClass.getModifiers())) {
+                       throw new RuntimeException("Eval class must be public");
+               }
+               if (Modifier.isAbstract(evalClass.getModifiers())) {
+                       throw new RuntimeException("Eval class must not be abstract");
+               }
+               
+               Constructor constructor;
+               try {
+                       constructor = evalClass.getDeclaredConstructor(OPERATION_CONSTRUCTOR_CLASS_ARRAY);
+               } catch (NoSuchMethodException e) {
+                       throw new RuntimeException("Missing constructor");
+               }
+               if (!Modifier.isPublic(constructor.getModifiers())) {
+                       throw new RuntimeException("Eval constructor must be public");
+               }
+               m.put(ptgClass, constructor);
+       }
+       
+       /**
+        * returns the OperationEval concrete impl instance corresponding
+        * to the supplied operationPtg
+        */
+       public static OperationEval create(OperationPtg ptg) {
+               if(ptg == null) {
+                       throw new IllegalArgumentException("ptg must not be null");
+               }
+               
+               Class ptgClass = ptg.getClass();
+               
+               Constructor constructor = (Constructor) _constructorsByPtgClass.get(ptgClass);
+               if(constructor == null) {
+                       if(ptgClass == ExpPtg.class) {
+                               // ExpPtg is used for array formulas and shared formulas.
+                               // it is currently unsupported, and may not even get implemented here
+                               throw new RuntimeException("ExpPtg currently not supported");
+                       }
+                       throw new RuntimeException("Unexpected operation ptg class (" + ptgClass.getName() + ")");
+               }
+               
+               Object result;
+               Object[] initargs = { ptg };
+               try {
+                       result = constructor.newInstance(initargs);
+               } catch (IllegalArgumentException e) {
+                       throw new RuntimeException(e);
+               } catch (InstantiationException e) {
+                       throw new RuntimeException(e);
+               } catch (IllegalAccessException e) {
+                       throw new RuntimeException(e);
+               } catch (InvocationTargetException e) {
+                       throw new RuntimeException(e);
+               }
+               return (OperationEval) result;
+       }
+}
index e66cf0e1d7839bb7c68fa8d4f0d15852eb776eb1..5605752d0acbd6cdf95f3bcb2bbd2881f5343fb5 100644 (file)
@@ -174,6 +174,14 @@ public interface Workbook {
      */
 
     int getNumberOfSheets();
+    
+    /**
+     * Finds the sheet index for a particular external sheet number.
+     * @param externSheetNumber The external sheet number to convert
+     * @return  The index to the sheet found.
+     */
+    int getSheetIndexFromExternSheetIndex(int externSheetNumber);
+
 
     /**
      * Get the HSSFSheet object at the given index.
index d2f0d36080666df2d85e214d427a63dfeb975fbb..dd6e5eb2c9856af45072f92cd6c36c5062c97007 100644 (file)
@@ -472,8 +472,17 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
         }
         return -1;
     }
+    
+    /**
+     * Doesn't do anything - returns the same index
+     * TODO - figure out if this is a ole2 specific thing, or
+     *  if we need to do something proper here too!
+     */
+    public int getSheetIndexFromExternSheetIndex(int externSheetNumber) {
+               return externSheetNumber;
+       }
 
-    public Sheet getSheet(String name) {
+       public Sheet getSheet(String name) {
         CTSheet[] sheets = this.workbook.getSheets().getSheetArray();  
         for (int i = 0 ; i < sheets.length ; ++i) {
             if (name.equals(sheets[i].getName())) {
index 74197345358b616909eb28fb856e128fcaa170a9..b200515ff9317ca473ce23ee8a6abdd9a2e03a2d 100644 (file)
@@ -29,7 +29,7 @@ import org.apache.poi.hssf.usermodel.HSSFName;
 import org.apache.poi.hssf.usermodel.HSSFRow;
 import org.apache.poi.hssf.usermodel.HSSFSheet;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
-import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue;
+import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
 
 /**
  * Test the low level formula parser functionality,
index 72db658f7748aa7afeb7396c9de5b5160b384c8f..97ddfdf220e068590753e3449212dd1ba0129d08 100755 (executable)
@@ -25,7 +25,7 @@ import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
 import org.apache.poi.hssf.usermodel.HSSFRow;
 import org.apache.poi.hssf.usermodel.HSSFSheet;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
-import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue;
+import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
 /**
  * Tests HSSFFormulaEvaluator for its handling of cell formula circular references.
  * 
index 27e3338652b13bbad7671831fc205f4689e2b2af..647b6330f29705c821f440e105c813accbff1d8e 100755 (executable)
@@ -25,7 +25,7 @@ import org.apache.poi.hssf.usermodel.HSSFName;
 import org.apache.poi.hssf.usermodel.HSSFRow;
 import org.apache.poi.hssf.usermodel.HSSFSheet;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
-import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue;
+import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
 /**
  * 
  * @author Josh Micich
index 617f5d0d4e48bdbed5867977f0a4743a88cc5a5d..a833b77c0d185ee0071fd748a9c0ec17616b2a58 100755 (executable)
@@ -32,7 +32,7 @@ import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
 import org.apache.poi.hssf.usermodel.HSSFRow;
 import org.apache.poi.hssf.usermodel.HSSFSheet;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
-import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue;
+import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
 
 /**
  * Miscellaneous tests for bugzilla entries.<p/> The test name contains the
index 2d5408c76a6a602817750d2976d35bae2146fc71..4b079bcce6cef8ec13148a8b5a64e6e65e594f53 100644 (file)
@@ -30,6 +30,8 @@ import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
 import org.apache.poi.hssf.usermodel.HSSFRow;
 import org.apache.poi.hssf.usermodel.HSSFSheet;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.FormulaEvaluator;
 
 /**
  * Tests formulas and operators as loaded from a test data spreadsheet.<p/>
@@ -104,7 +106,7 @@ public final class TestFormulasFromSpreadsheet extends TestCase {
        }
 
 
-       private static void confirmExpectedResult(String msg, HSSFCell expected, HSSFFormulaEvaluator.CellValue actual) {
+       private static void confirmExpectedResult(String msg, Cell expected, FormulaEvaluator.CellValue actual) {
                if (expected == null) {
                        throw new AssertionFailedError(msg + " - Bad setup data expected value is null");
                }
@@ -249,7 +251,7 @@ public final class TestFormulasFromSpreadsheet extends TestCase {
                                continue;
                        }
 
-                       HSSFFormulaEvaluator.CellValue actualValue = evaluator.evaluate(c);
+                       FormulaEvaluator.CellValue actualValue = evaluator.evaluate(c);
 
                        HSSFCell expectedValueCell = getExpectedValueCell(expectedValuesRow, colnum);
                        try {
index be8cef13fa64fcf32c944a20e8007586857811c8..c1daa094ccbc716b19d661ba37d72512ccca1f8b 100755 (executable)
@@ -27,7 +27,7 @@ import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
 import org.apache.poi.hssf.usermodel.HSSFRow;
 import org.apache.poi.hssf.usermodel.HSSFSheet;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
-import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue;
+import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
 
 /**
  * Test for percent operator evaluator.
index 7ce2bd245b098c221a583d18c832d003d198bcf0..90389eeaa83abe3a927a9a5547fa21e1d65e6f51 100755 (executable)
@@ -24,7 +24,7 @@ import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
 import org.apache.poi.hssf.usermodel.HSSFRow;
 import org.apache.poi.hssf.usermodel.HSSFSheet;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
-import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue;
+import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
 /**
  * Tests for Excel function ISBLANK()
  * 
index 071ca0f7d89f612ad935f7e8320fdf006e0f3968..958654b060108a0644e332faa6d89408dd4ab2b2 100644 (file)
@@ -32,7 +32,7 @@ import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
 import org.apache.poi.hssf.usermodel.HSSFRow;
 import org.apache.poi.hssf.usermodel.HSSFSheet;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
-import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue;
+import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
 import org.apache.poi.hssf.util.CellReference;
 
 /**
@@ -90,7 +90,7 @@ public final class TestLookupFunctionsFromSpreadsheet extends TestCase {
 
 
 
-    private static void confirmExpectedResult(String msg, HSSFCell expected, HSSFFormulaEvaluator.CellValue actual) {
+    private static void confirmExpectedResult(String msg, HSSFCell expected, CellValue actual) {
         if (expected == null) {
                        throw new AssertionFailedError(msg + " - Bad setup data expected value is null");
                }
index c849fd4369211fac513871d29646a6ddda4b141b..f137ef6e9a8d07d06f13a025a622129324b18d64 100644 (file)
@@ -25,7 +25,7 @@ import junit.framework.TestCase;
 
 import org.apache.poi.hssf.record.FormulaRecord;
 import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
-import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue;
+import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
 import org.apache.poi.hssf.util.CellReference;
 
 public final class TestBug42464 extends TestCase {