]> source.dussan.org Git - poi.git/commitdiff
Partitioning common formula logic. Introduced FormulaParsingWorkbook and EvaluationW...
authorJosh Micich <josh@apache.org>
Fri, 19 Sep 2008 02:19:58 +0000 (02:19 +0000)
committerJosh Micich <josh@apache.org>
Fri, 19 Sep 2008 02:19:58 +0000 (02:19 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@696898 13f79535-47bb-0310-9956-ffa450edef68

58 files changed:
src/java/org/apache/poi/hssf/dev/FormulaViewer.java
src/java/org/apache/poi/hssf/eventusermodel/EventWorkbookBuilder.java
src/java/org/apache/poi/hssf/extractor/EventBasedExcelExtractor.java
src/java/org/apache/poi/hssf/model/FormulaParser.java [deleted file]
src/java/org/apache/poi/hssf/model/HSSFFormulaParser.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/model/OperandClassTransformer.java [deleted file]
src/java/org/apache/poi/hssf/model/ParseNode.java [deleted file]
src/java/org/apache/poi/hssf/record/CFRuleRecord.java
src/java/org/apache/poi/hssf/record/NameRecord.java
src/java/org/apache/poi/hssf/record/formula/atp/AnalysisToolPak.java
src/java/org/apache/poi/hssf/record/formula/atp/ParityFunction.java
src/java/org/apache/poi/hssf/record/formula/atp/YearFrac.java
src/java/org/apache/poi/hssf/record/formula/eval/ExternalFunction.java
src/java/org/apache/poi/hssf/record/formula/eval/NameXEval.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/usermodel/DVConstraint.java
src/java/org/apache/poi/hssf/usermodel/EvaluationCache.java [deleted file]
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/HSSFCell.java
src/java/org/apache/poi/hssf/usermodel/HSSFConditionalFormattingRule.java
src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java
src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java
src/java/org/apache/poi/hssf/usermodel/HSSFName.java
src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
src/java/org/apache/poi/hssf/usermodel/LazyAreaEval.java [deleted file]
src/java/org/apache/poi/hssf/usermodel/LazyRefEval.java [deleted file]
src/java/org/apache/poi/hssf/usermodel/OperationEvaluatorFactory.java [deleted file]
src/java/org/apache/poi/ss/formula/CellEvaluationFrame.java [new file with mode: 0644]
src/java/org/apache/poi/ss/formula/CellEvaluator.java [new file with mode: 0644]
src/java/org/apache/poi/ss/formula/EvaluationCache.java [new file with mode: 0644]
src/java/org/apache/poi/ss/formula/EvaluationName.java [new file with mode: 0644]
src/java/org/apache/poi/ss/formula/EvaluationTracker.java [new file with mode: 0755]
src/java/org/apache/poi/ss/formula/EvaluationWorkbook.java [new file with mode: 0644]
src/java/org/apache/poi/ss/formula/FormulaParser.java [new file with mode: 0644]
src/java/org/apache/poi/ss/formula/FormulaParsingWorkbook.java [new file with mode: 0644]
src/java/org/apache/poi/ss/formula/FormulaRenderer.java
src/java/org/apache/poi/ss/formula/FormulaRenderingWorkbook.java
src/java/org/apache/poi/ss/formula/FormulaType.java [new file with mode: 0644]
src/java/org/apache/poi/ss/formula/LazyAreaEval.java [new file with mode: 0644]
src/java/org/apache/poi/ss/formula/LazyRefEval.java [new file with mode: 0644]
src/java/org/apache/poi/ss/formula/OperandClassTransformer.java [new file with mode: 0644]
src/java/org/apache/poi/ss/formula/OperationEvaluatorFactory.java [new file with mode: 0755]
src/java/org/apache/poi/ss/formula/ParseNode.java [new file with mode: 0644]
src/java/org/apache/poi/ss/formula/WorkbookDependentFormula.java
src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/eventusermodel/TestEventWorkbookBuilder.java
src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java
src/testcases/org/apache/poi/hssf/model/TestFormulaParserEval.java
src/testcases/org/apache/poi/hssf/model/TestOperandClassTransformer.java
src/testcases/org/apache/poi/hssf/model/TestRVA.java
src/testcases/org/apache/poi/hssf/record/formula/TestAreaPtg.java
src/testcases/org/apache/poi/hssf/record/formula/TestExternalFunctionFormulas.java
src/testcases/org/apache/poi/hssf/record/formula/TestFuncVarPtg.java
src/testcases/org/apache/poi/hssf/record/formula/function/TestParseMissingBuiltInFuncs.java
src/testcases/org/apache/poi/hssf/record/formula/functions/TestDate.java
src/testcases/org/apache/poi/ss/formula/FormulaParserTestHelper.java [new file with mode: 0644]

index 167eaba53265853f1fbf65ab91810956e22a35fe..d42cda71a353c68e7205f51440aeb3cd8d3cb4dc 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.poi.hssf.dev;
 import java.io.FileInputStream;
 import java.util.List;
 
-import org.apache.poi.hssf.model.FormulaParser;
+import org.apache.poi.hssf.model.HSSFFormulaParser;
 import org.apache.poi.hssf.record.FormulaRecord;
 import org.apache.poi.hssf.record.Record;
 import org.apache.poi.hssf.record.RecordFactory;
@@ -181,7 +181,7 @@ public class FormulaViewer
     
     private static String composeFormula(FormulaRecord record)
     {
-       return  FormulaParser.toFormulaString((HSSFWorkbook)null, record.getParsedExpression());
+       return  HSSFFormulaParser.toFormulaString((HSSFWorkbook)null, record.getParsedExpression());
     }
 
     /**
index 3c767dc25a4b8b6f38c51b3a7e3bbcd25be47eb5..c7a1471a7d958acf38a16ce3930355691b7ecd70 100644 (file)
@@ -19,7 +19,7 @@ package org.apache.poi.hssf.eventusermodel;
 import java.util.ArrayList;
 import java.util.List;
 
-import org.apache.poi.hssf.model.FormulaParser;
+import org.apache.poi.hssf.model.HSSFFormulaParser;
 import org.apache.poi.hssf.model.Workbook;
 import org.apache.poi.hssf.record.BoundSheetRecord;
 import org.apache.poi.hssf.record.EOFRecord;
@@ -33,7 +33,7 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook;
  * When working with the EventUserModel, if you want to 
  *  process formulas, you need an instance of
  *  {@link Workbook} to pass to a {@link HSSFWorkbook},
- *  to finally give to {@link FormulaParser}, 
+ *  to finally give to {@link HSSFFormulaParser}, 
  *  and this will build you stub ones.
  * Since you're working with the EventUserModel, you
  *  wouldn't want to get a full {@link Workbook} and
@@ -41,7 +41,7 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook;
  *  Instead, you should collect a few key records as they
  *  go past, then call this once you have them to build a
  *  stub {@link Workbook}, and from that a stub
- *  {@link HSSFWorkbook}, to use with the {@link FormulaParser}.
+ *  {@link HSSFWorkbook}, to use with the {@link HSSFFormulaParser}.
  * 
  * The records you should collect are:
  *  * {@link ExternSheetRecord}
@@ -56,7 +56,7 @@ public class EventWorkbookBuilder {
        /**
         * Wraps up your stub {@link Workbook} as a stub
         *  {@link HSSFWorkbook}, ready for passing to
-        *  {@link FormulaParser}
+        *  {@link HSSFFormulaParser}
         * @param workbook A stub {@link Workbook}
         */
        public static HSSFWorkbook createStubHSSFWorkbook(Workbook workbook) {
@@ -65,11 +65,11 @@ public class EventWorkbookBuilder {
        
        /**
         * Creates a stub Workbook from the supplied records,
-        *  suitable for use with the {@link FormulaParser}
+        *  suitable for use with the {@link HSSFFormulaParser}
         * @param externs The ExternSheetRecords in your file
         * @param bounds The BoundSheetRecords in your file
         * @param sst The SSTRecord in your file.
-        * @return A stub Workbook suitable for use with {@link FormulaParser}
+        * @return A stub Workbook suitable for use with {@link HSSFFormulaParser}
         */
        public static Workbook createStubWorkbook(ExternSheetRecord[] externs,
                        BoundSheetRecord[] bounds, SSTRecord sst) {
@@ -103,10 +103,10 @@ public class EventWorkbookBuilder {
        
        /**
         * Creates a stub workbook from the supplied records,
-        *  suitable for use with the {@link FormulaParser}
+        *  suitable for use with the {@link HSSFFormulaParser}
         * @param externs The ExternSheetRecords in your file
         * @param bounds The BoundSheetRecords in your file
-        * @return A stub Workbook suitable for use with {@link FormulaParser}
+        * @return A stub Workbook suitable for use with {@link HSSFFormulaParser}
         */
        public static Workbook createStubWorkbook(ExternSheetRecord[] externs,
                        BoundSheetRecord[] bounds) {
index 2ea35c773ed8a969490c8679035fbd15670f262a..430dcd4758b9ad17a4acd19e4c6d723b5284da9a 100644 (file)
@@ -32,7 +32,7 @@ import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener;
 import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
 import org.apache.poi.hssf.eventusermodel.HSSFListener;
 import org.apache.poi.hssf.eventusermodel.HSSFRequest;
-import org.apache.poi.hssf.model.FormulaParser;
+import org.apache.poi.hssf.model.HSSFFormulaParser;
 import org.apache.poi.hssf.record.BOFRecord;
 import org.apache.poi.hssf.record.BoundSheetRecord;
 import org.apache.poi.hssf.record.CellValueRecordInterface;
@@ -45,6 +45,7 @@ import org.apache.poi.hssf.record.Record;
 import org.apache.poi.hssf.record.SSTRecord;
 import org.apache.poi.hssf.record.StringRecord;
 import org.apache.poi.hssf.usermodel.HSSFDateUtil;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 
 /**
@@ -177,7 +178,7 @@ public class EventBasedExcelExtractor extends POIOLE2TextExtractor {
                                thisRow = frec.getRow();
 
                                if(formulasNotResults) {
-                                       thisText = FormulaParser.toFormulaString(null, frec.getParsedExpression());
+                                       thisText = HSSFFormulaParser.toFormulaString((HSSFWorkbook)null, frec.getParsedExpression());
                                } else {
                                        if(frec.hasCachedResultString()) {
                                                // Formula result is a string
diff --git a/src/java/org/apache/poi/hssf/model/FormulaParser.java b/src/java/org/apache/poi/hssf/model/FormulaParser.java
deleted file mode 100644 (file)
index a24c7be..0000000
+++ /dev/null
@@ -1,1038 +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.model;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.poi.hssf.record.UnicodeString;
-import org.apache.poi.hssf.record.constant.ErrorConstant;
-import org.apache.poi.hssf.record.formula.AbstractFunctionPtg;
-import org.apache.poi.hssf.record.formula.AddPtg;
-import org.apache.poi.hssf.record.formula.Area3DPtg;
-import org.apache.poi.hssf.record.formula.AreaPtg;
-import org.apache.poi.hssf.record.formula.ArrayPtg;
-import org.apache.poi.hssf.record.formula.BoolPtg;
-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.ErrPtg;
-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.IntPtg;
-import org.apache.poi.hssf.record.formula.LessEqualPtg;
-import org.apache.poi.hssf.record.formula.LessThanPtg;
-import org.apache.poi.hssf.record.formula.MissingArgPtg;
-import org.apache.poi.hssf.record.formula.MultiplyPtg;
-import org.apache.poi.hssf.record.formula.NamePtg;
-import org.apache.poi.hssf.record.formula.NameXPtg;
-import org.apache.poi.hssf.record.formula.NotEqualPtg;
-import org.apache.poi.hssf.record.formula.NumberPtg;
-import org.apache.poi.hssf.record.formula.ParenthesisPtg;
-import org.apache.poi.hssf.record.formula.PercentPtg;
-import org.apache.poi.hssf.record.formula.PowerPtg;
-import org.apache.poi.hssf.record.formula.Ptg;
-import org.apache.poi.hssf.record.formula.Ref3DPtg;
-import org.apache.poi.hssf.record.formula.RefPtg;
-import org.apache.poi.hssf.record.formula.StringPtg;
-import org.apache.poi.hssf.record.formula.SubtractPtg;
-import org.apache.poi.hssf.record.formula.UnaryMinusPtg;
-import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
-import org.apache.poi.hssf.record.formula.function.FunctionMetadata;
-import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
-import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
-import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook;
-import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
-import org.apache.poi.hssf.usermodel.HSSFName;
-import org.apache.poi.hssf.usermodel.HSSFWorkbook;
-import org.apache.poi.hssf.util.AreaReference;
-import org.apache.poi.hssf.util.CellReference;
-import org.apache.poi.hssf.util.CellReference.NameType;
-import org.apache.poi.ss.formula.FormulaRenderer;
-
-/**
- * This class parses a formula string into a List of tokens in RPN order.
- * Inspired by
- *           Lets Build a Compiler, by Jack Crenshaw
- * BNF for the formula expression is :
- * <expression> ::= <term> [<addop> <term>]*
- * <term> ::= <factor>  [ <mulop> <factor> ]*
- * <factor> ::= <number> | (<expression>) | <cellRef> | <function>
- * <function> ::= <functionName> ([expression [, expression]*])
- *
- *  @author Avik Sengupta <avik at apache dot org>
- *  @author Andrew C. oliver (acoliver at apache dot org)
- *  @author Eric Ladner (eladner at goldinc dot com)
- *  @author Cameron Riley (criley at ekmail.com)
- *  @author Peter M. Murray (pete at quantrix dot com)
- *  @author Pavel Krupets (pkrupets at palmtreebusiness dot com)
- */
-public final class FormulaParser {
-
-    /**
-     * Specific exception thrown when a supplied formula does not parse properly.<br/>
-     * Primarily used by test cases when testing for specific parsing exceptions.</p>
-     *
-     */
-    static final class FormulaParseException extends RuntimeException {
-        // This class was given package scope until it would become clear that it is useful to
-        // general client code.
-        public FormulaParseException(String msg) {
-            super(msg);
-        }
-    }
-
-    public static final int FORMULA_TYPE_CELL = 0;
-    public static final int FORMULA_TYPE_SHARED = 1;
-    public static final int FORMULA_TYPE_ARRAY =2;
-    public static final int FORMULA_TYPE_CONDFORMAT = 3;
-    public static final int FORMULA_TYPE_NAMEDRANGE = 4;
-    // this constant is currently very specific.  The exact differences from general data
-    // validation formulas or conditional format formulas is not known yet
-    public static final int FORMULA_TYPE_DATAVALIDATION_LIST = 5;
-
-    private final String formulaString;
-    private final int formulaLength;
-    private int pointer;
-
-    private ParseNode _rootNode;
-
-    private static char TAB = '\t';
-
-    /**
-     * Lookahead Character.
-     * gets value '\0' when the input string is exhausted
-     */
-    private char look;
-
-    private HSSFWorkbook book;
-
-
-    /**
-     * Create the formula parser, with the string that is to be
-     *  parsed against the supplied workbook.
-     * A later call the parse() method to return ptg list in
-     *  rpn order, then call the getRPNPtg() to retrive the
-     *  parse results.
-     * This class is recommended only for single threaded use.
-     *
-     * If you only have a usermodel.HSSFWorkbook, and not a
-     *  model.Workbook, then use the convenience method on
-     *  usermodel.HSSFFormulaEvaluator
-     */
-    public FormulaParser(String formula, HSSFWorkbook book){
-        formulaString = formula;
-        pointer=0;
-        this.book = book;
-        formulaLength = formulaString.length();
-    }
-
-    public static Ptg[] parse(String formula, HSSFWorkbook book) {
-        return parse(formula, book, FORMULA_TYPE_CELL);
-    }
-
-    public static Ptg[] parse(String formula, HSSFWorkbook workbook, int formulaType) {
-        FormulaParser fp = HSSFFormulaEvaluator.getUnderlyingParser(workbook, formula);
-        fp.parse();
-        return fp.getRPNPtg(formulaType);
-    }
-
-    /** Read New Character From Input Stream */
-    private void GetChar() {
-        // Check to see if we've walked off the end of the string.
-        if (pointer > formulaLength) {
-            throw new RuntimeException("too far");
-        }
-        if (pointer < formulaLength) {
-            look=formulaString.charAt(pointer);
-        } else {
-            // Just return if so and reset 'look' to something to keep
-            // SkipWhitespace from spinning
-            look = (char)0;
-        }
-        pointer++;
-        //System.out.println("Got char: "+ look);
-    }
-
-    /** Report What Was Expected */
-    private RuntimeException expected(String s) {
-        String msg;
-
-        if (look == '=' && formulaString.substring(0, pointer-1).trim().length() < 1) {
-            msg = "The specified formula '" + formulaString
-                + "' starts with an equals sign which is not allowed.";
-        } else {
-            msg = "Parse error near char " + (pointer-1) + " '" + look + "'"
-                + " in specified formula '" + formulaString + "'. Expected "
-                + s;
-        }
-        return new FormulaParseException(msg);
-    }
-
-    /** Recognize an Alpha Character */
-    private boolean IsAlpha(char c) {
-        return Character.isLetter(c) || c == '$' || c=='_';
-    }
-
-    /** Recognize a Decimal Digit */
-    private boolean IsDigit(char c) {
-        return Character.isDigit(c);
-    }
-
-    /** Recognize an Alphanumeric */
-    private boolean  IsAlNum(char c) {
-        return  (IsAlpha(c) || IsDigit(c));
-    }
-
-    /** Recognize White Space */
-    private boolean IsWhite( char c) {
-        return  (c ==' ' || c== TAB);
-    }
-
-    /** Skip Over Leading White Space */
-    private void SkipWhite() {
-        while (IsWhite(look)) {
-            GetChar();
-        }
-    }
-
-    /**
-     *  Consumes the next input character if it is equal to the one specified otherwise throws an
-     *  unchecked exception. This method does <b>not</b> consume whitespace (before or after the
-     *  matched character).
-     */
-    private void Match(char x) {
-        if (look != x) {
-            throw expected("'" + x + "'");
-        }
-        GetChar();
-    }
-
-    /**
-     * Parses a sheet name, named range name, or simple cell reference.<br/>
-     * Note - identifiers in Excel can contain dots, so this method may return a String
-     * which may need to be converted to an area reference.  For example, this method
-     * may return a value like "A1..B2", in which case the caller must convert it to
-     * an area reference like "A1:B2"
-     */
-    private String parseIdentifier() {
-        StringBuffer Token = new StringBuffer();
-        if (!IsAlpha(look) && look != '\'') {
-            throw expected("Name");
-        }
-        if(look == '\'')
-        {
-            Match('\'');
-            boolean done = look == '\'';
-            while(!done)
-            {
-                Token.append(look);
-                GetChar();
-                if(look == '\'')
-                {
-                    Match('\'');
-                    done = look != '\'';
-                }
-            }
-        }
-        else
-        {
-            // allow for any sequence of dots and identifier chars
-            // special case of two consecutive dots is best treated in the calling code
-            while (IsAlNum(look) || look == '.') {
-                Token.append(look);
-                GetChar();
-            }
-        }
-        return Token.toString();
-    }
-
-    /** Get a Number */
-    private String GetNum() {
-        StringBuffer value = new StringBuffer();
-
-        while (IsDigit(this.look)){
-            value.append(this.look);
-            GetChar();
-        }
-        return value.length() == 0 ? null : value.toString();
-    }
-
-    private ParseNode parseFunctionReferenceOrName() {
-        String name = parseIdentifier();
-        if (look == '('){
-            //This is a function
-            return function(name);
-        }
-        return new ParseNode(parseNameOrReference(name));
-    }
-
-    private Ptg parseNameOrReference(String name) {
-
-        AreaReference areaRef = parseArea(name);
-        if (areaRef != null) {
-            // will happen if dots are used instead of colon
-            return new AreaPtg(areaRef.formatAsString());
-        }
-
-        if (look == ':' || look == '.') { // this is a AreaReference
-            GetChar();
-
-            while (look == '.') { // formulas can have . or .. or ... instead of :
-                GetChar();
-            }
-
-            String first = name;
-            String second = parseIdentifier();
-            return new AreaPtg(first+":"+second);
-        }
-
-        if (look == '!') {
-            Match('!');
-            String sheetName = name;
-            String first = parseIdentifier();
-            short externIdx = book.getExternalSheetIndex(book.getSheetIndex(sheetName));
-            areaRef = parseArea(name);
-            if (areaRef != null) {
-                // will happen if dots are used instead of colon
-                return new Area3DPtg(areaRef.formatAsString(), externIdx);
-            }
-            if (look == ':') {
-                Match(':');
-                String second=parseIdentifier();
-                if (look == '!') {
-                    //The sheet name was included in both of the areas. Only really
-                    //need it once
-                    Match('!');
-                    String third=parseIdentifier();
-
-                    if (!sheetName.equals(second))
-                        throw new RuntimeException("Unhandled double sheet reference.");
-
-                    return new Area3DPtg(first+":"+third,externIdx);
-                }
-                return new Area3DPtg(first+":"+second,externIdx);
-            }
-            return new Ref3DPtg(first,externIdx);
-        }
-        if (name.equalsIgnoreCase("TRUE") || name.equalsIgnoreCase("FALSE")) {
-            return new BoolPtg(name.toUpperCase());
-        }
-
-        // This can be either a cell ref or a named range
-        // Try to spot which it is
-        int nameType = CellReference.classifyCellReference(name);
-        if (nameType == NameType.CELL) {
-            return new RefPtg(name);
-        }
-        if (nameType != NameType.NAMED_RANGE) {
-            new FormulaParseException("Name '" + name
-                + "' does not look like a cell reference or named range");
-        }
-
-        for(int i = 0; i < book.getNumberOfNames(); i++) {
-            // named range name matching is case insensitive
-            if(book.getNameAt(i).getNameName().equalsIgnoreCase(name)) {
-                return new NamePtg(i);
-            }
-        }
-        throw new FormulaParseException("Specified named range '"
-                    + name + "' does not exist in the current workbook.");
-    }
-
-    /**
-     * @param name an 'identifier' like string (i.e. contains alphanums, and dots)
-     * @return <code>null</code> if name cannot be split at a dot
-     */
-    private AreaReference parseArea(String name) {
-        int dotPos = name.indexOf('.');
-        if (dotPos < 0) {
-            return null;
-        }
-        int dotCount = 1;
-        while (dotCount<name.length() && name.charAt(dotPos+dotCount) == '.') {
-            dotCount++;
-            if (dotCount>3) {
-                // four or more consecutive dots does not convert to ':'
-                return null;
-            }
-        }
-        // This expression is only valid as an area ref, if the LHS and RHS of the dot(s) are both
-        // cell refs.  Otherwise, this expression must be a named range name
-        String partA = name.substring(0, dotPos);
-        if (!isValidCellReference(partA)) {
-            return null;
-        }
-        String partB = name.substring(dotPos+dotCount);
-        if (!isValidCellReference(partB)) {
-            return null;
-        }
-        CellReference topLeft = new CellReference(partA);
-        CellReference bottomRight = new CellReference(partB);
-        return new AreaReference(topLeft, bottomRight);
-    }
-
-    /**
-     * @return <code>true</code> if the specified name is a valid cell reference
-     */
-    private static boolean isValidCellReference(String str) {
-        return CellReference.classifyCellReference(str) == NameType.CELL;
-    }
-
-
-    /**
-     * Note - Excel function names are 'case aware but not case sensitive'.  This method may end
-     * up creating a defined name record in the workbook if the specified name is not an internal
-     * Excel function, and has not been encountered before.
-     *
-     * @param name case preserved function name (as it was entered/appeared in the formula).
-     */
-    private ParseNode function(String name) {
-        Ptg nameToken = null;
-        if(!AbstractFunctionPtg.isBuiltInFunctionName(name)) {
-            // user defined function
-            // in the token tree, the name is more or less the first argument
-
-
-            int nameIndex = book.getNameIndex(name);
-            if (nameIndex >= 0) {
-                HSSFName hName = book.getNameAt(nameIndex);
-                if (!hName.isFunctionName()) {
-                    throw new FormulaParseException("Attempt to use name '" + name
-                            + "' as a function, but defined name in workbook does not refer to a function");
-                }
-
-                // calls to user-defined functions within the workbook
-                // get a Name token which points to a defined name record
-                nameToken = new NamePtg(nameIndex);
-            } else {
-
-                nameToken = book.getNameXPtg(name);
-                if (nameToken == null) {
-                    throw new FormulaParseException("Name '" + name
-                            + "' is completely unknown in the current workbook");
-                }
-            }
-        }
-
-        Match('(');
-        ParseNode[] args = Arguments();
-        Match(')');
-
-        return getFunction(name, nameToken, args);
-    }
-
-    /**
-     * Generates the variable function ptg for the formula.
-     * <p>
-     * For IF Formulas, additional PTGs are added to the tokens
-     * @param name a {@link NamePtg} or {@link NameXPtg} or <code>null</code>
-     * @param numArgs
-     * @return Ptg a null is returned if we're in an IF formula, it needs extreme manipulation and is handled in this function
-     */
-    private ParseNode getFunction(String name, Ptg namePtg, ParseNode[] args) {
-
-        FunctionMetadata fm = FunctionMetadataRegistry.getFunctionByName(name.toUpperCase());
-        int numArgs = args.length;
-        if(fm == null) {
-            if (namePtg == null) {
-                throw new IllegalStateException("NamePtg must be supplied for external functions");
-            }
-            // must be external function
-            ParseNode[] allArgs = new ParseNode[numArgs+1];
-            allArgs[0] = new ParseNode(namePtg);
-            System.arraycopy(args, 0, allArgs, 1, numArgs);
-            return new ParseNode(new FuncVarPtg(name, (byte)(numArgs+1)), allArgs);
-        }
-
-        if (namePtg != null) {
-            throw new IllegalStateException("NamePtg no applicable to internal functions");
-        }
-        boolean isVarArgs = !fm.hasFixedArgsLength();
-        int funcIx = fm.getIndex();
-        validateNumArgs(args.length, fm);
-
-        AbstractFunctionPtg retval;
-        if(isVarArgs) {
-            retval = new FuncVarPtg(name, (byte)numArgs);
-        } else {
-            retval = new FuncPtg(funcIx);
-        }
-        return new ParseNode(retval, args);
-    }
-
-    private void validateNumArgs(int numArgs, FunctionMetadata fm) {
-        if(numArgs < fm.getMinParams()) {
-            String msg = "Too few arguments to function '" + fm.getName() + "'. ";
-            if(fm.hasFixedArgsLength()) {
-                msg += "Expected " + fm.getMinParams();
-            } else {
-                msg += "At least " + fm.getMinParams() + " were expected";
-            }
-            msg += " but got " + numArgs + ".";
-            throw new FormulaParseException(msg);
-         }
-        if(numArgs > fm.getMaxParams()) {
-            String msg = "Too many arguments to function '" + fm.getName() + "'. ";
-            if(fm.hasFixedArgsLength()) {
-                msg += "Expected " + fm.getMaxParams();
-            } else {
-                msg += "At most " + fm.getMaxParams() + " were expected";
-            }
-            msg += " but got " + numArgs + ".";
-            throw new FormulaParseException(msg);
-       }
-    }
-
-    private static boolean isArgumentDelimiter(char ch) {
-        return ch ==  ',' || ch == ')';
-    }
-
-    /** get arguments to a function */
-    private ParseNode[] Arguments() {
-        //average 2 args per function
-        List temp = new ArrayList(2);
-        SkipWhite();
-        if(look == ')') {
-            return ParseNode.EMPTY_ARRAY;
-        }
-
-        boolean missedPrevArg = true;
-        int numArgs = 0;
-        while (true) {
-            SkipWhite();
-            if (isArgumentDelimiter(look)) {
-                if (missedPrevArg) {
-                    temp.add(new ParseNode(MissingArgPtg.instance));
-                    numArgs++;
-                }
-                if (look == ')') {
-                    break;
-                }
-                Match(',');
-                missedPrevArg = true;
-                continue;
-            }
-            temp.add(comparisonExpression());
-            numArgs++;
-            missedPrevArg = false;
-            SkipWhite();
-            if (!isArgumentDelimiter(look)) {
-                throw expected("',' or ')'");
-            }
-        }
-        ParseNode[] result = new ParseNode[temp.size()];
-        temp.toArray(result);
-        return result;
-    }
-
-   /** Parse and Translate a Math Factor  */
-    private ParseNode powerFactor() {
-        ParseNode result = percentFactor();
-        while(true) {
-            SkipWhite();
-            if(look != '^') {
-                return result;
-            }
-            Match('^');
-            ParseNode other = percentFactor();
-            result = new ParseNode(PowerPtg.instance, result, other);
-        }
-    }
-
-    private ParseNode percentFactor() {
-        ParseNode result = parseSimpleFactor();
-        while(true) {
-            SkipWhite();
-            if(look != '%') {
-                return result;
-            }
-            Match('%');
-            result = new ParseNode(PercentPtg.instance, result);
-        }
-    }
-
-
-    /**
-     * factors (without ^ or % )
-     */
-    private ParseNode parseSimpleFactor() {
-        SkipWhite();
-        switch(look) {
-            case '#':
-                return new ParseNode(ErrPtg.valueOf(parseErrorLiteral()));
-            case '-':
-                Match('-');
-                return new ParseNode(UnaryMinusPtg.instance, powerFactor());
-            case '+':
-                Match('+');
-                return new ParseNode(UnaryPlusPtg.instance, powerFactor());
-            case '(':
-                Match('(');
-                ParseNode inside = comparisonExpression();
-                Match(')');
-                return new ParseNode(ParenthesisPtg.instance, inside);
-            case '"':
-                return new ParseNode(new StringPtg(parseStringLiteral()));
-            case '{':
-                Match('{');
-                ParseNode arrayNode = parseArray();
-                Match('}');
-                return arrayNode;
-        }
-        if (IsAlpha(look) || look == '\''){
-            return parseFunctionReferenceOrName();
-        }
-        // else - assume number
-        return new ParseNode(parseNumber());
-    }
-
-
-    private ParseNode parseArray() {
-        List rowsData = new ArrayList();
-        while(true) {
-            Object[] singleRowData = parseArrayRow();
-            rowsData.add(singleRowData);
-            if (look == '}') {
-                break;
-            }
-            if (look != ';') {
-                throw expected("'}' or ';'");
-            }
-            Match(';');
-        }
-        int nRows = rowsData.size();
-        Object[][] values2d = new Object[nRows][];
-        rowsData.toArray(values2d);
-        int nColumns = values2d[0].length;
-        checkRowLengths(values2d, nColumns);
-
-        return new ParseNode(new ArrayPtg(values2d));
-    }
-    private void checkRowLengths(Object[][] values2d, int nColumns) {
-        for (int i = 0; i < values2d.length; i++) {
-            int rowLen = values2d[i].length;
-            if (rowLen != nColumns) {
-                throw new FormulaParseException("Array row " + i + " has length " + rowLen
-                        + " but row 0 has length " + nColumns);
-            }
-        }
-    }
-
-    private Object[] parseArrayRow() {
-        List temp = new ArrayList();
-        while (true) {
-            temp.add(parseArrayItem());
-            SkipWhite();
-            switch(look) {
-                case '}':
-                case ';':
-                    break;
-                case ',':
-                    Match(',');
-                    continue;
-                default:
-                    throw expected("'}' or ','");
-
-            }
-            break;
-        }
-
-        Object[] result = new Object[temp.size()];
-        temp.toArray(result);
-        return result;
-    }
-
-    private Object parseArrayItem() {
-        SkipWhite();
-        switch(look) {
-            case '"': return new UnicodeString(parseStringLiteral());
-            case '#': return ErrorConstant.valueOf(parseErrorLiteral());
-            case 'F': case 'f':
-            case 'T': case 't':
-                return parseBooleanLiteral();
-        }
-        // else assume number
-        return convertArrayNumber(parseNumber());
-    }
-
-    private Boolean parseBooleanLiteral() {
-        String iden = parseIdentifier();
-        if ("TRUE".equalsIgnoreCase(iden)) {
-            return Boolean.TRUE;
-        }
-        if ("FALSE".equalsIgnoreCase(iden)) {
-            return Boolean.FALSE;
-        }
-        throw expected("'TRUE' or 'FALSE'");
-    }
-
-    private static Double convertArrayNumber(Ptg ptg) {
-        if (ptg instanceof IntPtg) {
-            return new Double(((IntPtg)ptg).getValue());
-        }
-        if (ptg instanceof NumberPtg) {
-            return new Double(((NumberPtg)ptg).getValue());
-        }
-        throw new RuntimeException("Unexpected ptg (" + ptg.getClass().getName() + ")");
-    }
-
-    private Ptg parseNumber() {
-        String number2 = null;
-        String exponent = null;
-        String number1 = GetNum();
-
-        if (look == '.') {
-            GetChar();
-            number2 = GetNum();
-        }
-
-        if (look == 'E') {
-            GetChar();
-
-            String sign = "";
-            if (look == '+') {
-                GetChar();
-            } else if (look == '-') {
-                GetChar();
-                sign = "-";
-            }
-
-            String number = GetNum();
-            if (number == null) {
-                throw expected("Integer");
-            }
-            exponent = sign + number;
-        }
-
-        if (number1 == null && number2 == null) {
-            throw expected("Integer");
-        }
-
-        return getNumberPtgFromString(number1, number2, exponent);
-    }
-
-
-    private int parseErrorLiteral() {
-        Match('#');
-        String part1 = parseIdentifier().toUpperCase();
-
-        switch(part1.charAt(0)) {
-            case 'V':
-                if(part1.equals("VALUE")) {
-                    Match('!');
-                    return HSSFErrorConstants.ERROR_VALUE;
-                }
-                throw expected("#VALUE!");
-            case 'R':
-                if(part1.equals("REF")) {
-                    Match('!');
-                    return HSSFErrorConstants.ERROR_REF;
-                }
-                throw expected("#REF!");
-            case 'D':
-                if(part1.equals("DIV")) {
-                    Match('/');
-                    Match('0');
-                    Match('!');
-                    return HSSFErrorConstants.ERROR_DIV_0;
-                }
-                throw expected("#DIV/0!");
-            case 'N':
-                if(part1.equals("NAME")) {
-                    Match('?');  // only one that ends in '?'
-                    return HSSFErrorConstants.ERROR_NAME;
-                }
-                if(part1.equals("NUM")) {
-                    Match('!');
-                    return HSSFErrorConstants.ERROR_NUM;
-                }
-                if(part1.equals("NULL")) {
-                    Match('!');
-                    return HSSFErrorConstants.ERROR_NULL;
-                }
-                if(part1.equals("N")) {
-                    Match('/');
-                    if(look != 'A' && look != 'a') {
-                        throw expected("#N/A");
-                    }
-                    Match(look);
-                    // Note - no '!' or '?' suffix
-                    return HSSFErrorConstants.ERROR_NA;
-                }
-                throw expected("#NAME?, #NUM!, #NULL! or #N/A");
-
-        }
-        throw expected("#VALUE!, #REF!, #DIV/0!, #NAME?, #NUM!, #NULL! or #N/A");
-    }
-
-
-    /**
-     * Get a PTG for an integer from its string representation.
-     * return Int or Number Ptg based on size of input
-     */
-    private static Ptg getNumberPtgFromString(String number1, String number2, String exponent) {
-        StringBuffer number = new StringBuffer();
-
-        if (number2 == null) {
-            number.append(number1);
-
-            if (exponent != null) {
-                number.append('E');
-                number.append(exponent);
-            }
-
-            String numberStr = number.toString();
-            int intVal;
-            try {
-                intVal = Integer.parseInt(numberStr);
-            } catch (NumberFormatException e) {
-                return new NumberPtg(numberStr);
-            }
-            if (IntPtg.isInRange(intVal)) {
-                return new IntPtg(intVal);
-            }
-            return new NumberPtg(numberStr);
-        }
-
-        if (number1 != null) {
-            number.append(number1);
-        }
-
-        number.append('.');
-        number.append(number2);
-
-        if (exponent != null) {
-            number.append('E');
-            number.append(exponent);
-        }
-
-        return new NumberPtg(number.toString());
-    }
-
-
-    private String parseStringLiteral() {
-        Match('"');
-
-        StringBuffer token = new StringBuffer();
-        while (true) {
-            if (look == '"') {
-                GetChar();
-                if (look != '"') {
-                    break;
-                }
-             }
-            token.append(look);
-            GetChar();
-        }
-        return token.toString();
-    }
-
-    /** Parse and Translate a Math Term */
-    private ParseNode  Term() {
-        ParseNode result = powerFactor();
-        while(true) {
-            SkipWhite();
-            Ptg operator;
-            switch(look) {
-                case '*':
-                    Match('*');
-                    operator = MultiplyPtg.instance;
-                    break;
-                case '/':
-                    Match('/');
-                    operator = DividePtg.instance;
-                    break;
-                default:
-                    return result; // finished with Term
-            }
-            ParseNode other = powerFactor();
-            result = new ParseNode(operator, result, other);
-        }
-    }
-
-    private ParseNode comparisonExpression() {
-        ParseNode result = concatExpression();
-        while (true) {
-            SkipWhite();
-            switch(look) {
-                case '=':
-                case '>':
-                case '<':
-                    Ptg comparisonToken = getComparisonToken();
-                    ParseNode other = concatExpression();
-                    result = new ParseNode(comparisonToken, result, other);
-                    continue;
-            }
-            return result; // finished with predicate expression
-        }
-    }
-
-    private Ptg getComparisonToken() {
-        if(look == '=') {
-            Match(look);
-            return EqualPtg.instance;
-        }
-        boolean isGreater = look == '>';
-        Match(look);
-        if(isGreater) {
-            if(look == '=') {
-                Match('=');
-                return GreaterEqualPtg.instance;
-            }
-            return GreaterThanPtg.instance;
-        }
-        switch(look) {
-            case '=':
-                Match('=');
-                return LessEqualPtg.instance;
-            case '>':
-                Match('>');
-                return NotEqualPtg.instance;
-        }
-        return LessThanPtg.instance;
-    }
-
-
-    private ParseNode concatExpression() {
-        ParseNode result = additiveExpression();
-        while (true) {
-            SkipWhite();
-            if(look != '&') {
-                break; // finished with concat expression
-            }
-            Match('&');
-            ParseNode other = additiveExpression();
-            result = new ParseNode(ConcatPtg.instance, result, other);
-        }
-        return result;
-    }
-
-
-    /** Parse and Translate an Expression */
-    private ParseNode additiveExpression() {
-        ParseNode result = Term();
-        while (true) {
-            SkipWhite();
-            Ptg operator;
-            switch(look) {
-                case '+':
-                    Match('+');
-                    operator = AddPtg.instance;
-                    break;
-                case '-':
-                    Match('-');
-                    operator = SubtractPtg.instance;
-                    break;
-                default:
-                    return result; // finished with additive expression
-            }
-            ParseNode other = Term();
-            result = new ParseNode(operator, result, other);
-        }
-    }
-
-    //{--------------------------------------------------------------}
-    //{ Parse and Translate an Assignment Statement }
-    /**
-procedure Assignment;
-var Name: string[8];
-begin
-   Name := GetName;
-   Match('=');
-   Expression;
-
-end;
-     **/
-
-
-    /**
-     *  API call to execute the parsing of the formula
-     * @deprecated use {@link #parse(String, HSSFWorkbook)} directly
-     */
-    public void parse() {
-        pointer=0;
-        GetChar();
-        _rootNode = comparisonExpression();
-
-        if(pointer <= formulaLength) {
-            String msg = "Unused input [" + formulaString.substring(pointer-1)
-                + "] after attempting to parse the formula [" + formulaString + "]";
-            throw new FormulaParseException(msg);
-        }
-    }
-
-
-    /*********************************
-     * PARSER IMPLEMENTATION ENDS HERE
-     * EXCEL SPECIFIC METHODS BELOW
-     *******************************/
-
-    /** API call to retrive the array of Ptgs created as
-     * a result of the parsing
-     */
-    public Ptg[] getRPNPtg() {
-        return getRPNPtg(FORMULA_TYPE_CELL);
-    }
-
-    public Ptg[] getRPNPtg(int formulaType) {
-        OperandClassTransformer oct = new OperandClassTransformer(formulaType);
-        // RVA is for 'operand class': 'reference', 'value', 'array'
-        oct.transformFormula(_rootNode);
-        return ParseNode.toTokenArray(_rootNode);
-    }
-
-    /**
-     * Convenience method which takes in a list then passes it to the
-     *  other toFormulaString signature.
-     * @param book   workbook for 3D and named references
-     * @param lptgs  list of Ptg, can be null or empty
-     * @return a human readable String
-     */
-    public static String toFormulaString(HSSFWorkbook book, List lptgs) {
-        String retval = null;
-        if (lptgs == null || lptgs.size() == 0) return "#NAME";
-        Ptg[] ptgs = new Ptg[lptgs.size()];
-        ptgs = (Ptg[])lptgs.toArray(ptgs);
-        retval = toFormulaString(book, ptgs);
-        return retval;
-    }
-    /**
-     * Convenience method which takes in a list then passes it to the
-     *  other toFormulaString signature. Works on the current
-     *  workbook for 3D and named references
-     * @param lptgs  list of Ptg, can be null or empty
-     * @return a human readable String
-     */
-    public String toFormulaString(List lptgs) {
-        return toFormulaString(book, lptgs);
-    }
-
-    /**
-     * Static method to convert an array of Ptgs in RPN order
-     * to a human readable string format in infix mode.
-     * @param book  workbook for named and 3D references
-     * @param ptgs  array of Ptg, can be null or empty
-     * @return a human readable String
-     */
-    public static String toFormulaString(HSSFWorkbook book, Ptg[] ptgs) {
-        return FormulaRenderer.toFormulaString(HSSFEvaluationWorkbook.create(book), ptgs);
-    }
-}
diff --git a/src/java/org/apache/poi/hssf/model/HSSFFormulaParser.java b/src/java/org/apache/poi/hssf/model/HSSFFormulaParser.java
new file mode 100644 (file)
index 0000000..c492c5e
--- /dev/null
@@ -0,0 +1,72 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.hssf.model;\r
+\r
+import java.util.List;\r
+\r
+import org.apache.poi.hssf.record.formula.Ptg;\r
+import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook;\r
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;\r
+import org.apache.poi.ss.formula.FormulaParser;\r
+import org.apache.poi.ss.formula.FormulaParsingWorkbook;\r
+import org.apache.poi.ss.formula.FormulaRenderer;\r
+import org.apache.poi.ss.formula.FormulaRenderingWorkbook;\r
+\r
+/**\r
+ * HSSF wrapper for the {@link FormulaParser}\r
+ * \r
+ * @author Josh Micich\r
+ */\r
+public final class HSSFFormulaParser {\r
+\r
+       private static FormulaParsingWorkbook createParsingWorkbook(HSSFWorkbook book) {\r
+               return HSSFEvaluationWorkbook.create(book);\r
+       }\r
+\r
+       private HSSFFormulaParser() {\r
+               // no instances of this class\r
+       }\r
+\r
+       public static Ptg[] parse(String formula, HSSFWorkbook workbook) {\r
+               return FormulaParser.parse(formula, createParsingWorkbook(workbook));\r
+       }\r
+\r
+       public static Ptg[] parse(String formula, HSSFWorkbook workbook, int formulaType) {\r
+               return FormulaParser.parse(formula, createParsingWorkbook(workbook), formulaType);\r
+       }\r
+\r
+       public static String toFormulaString(HSSFWorkbook book, List lptgs) {\r
+               return toFormulaString(HSSFEvaluationWorkbook.create(book), lptgs);\r
+       }\r
+       /**\r
+        * Convenience method which takes in a list then passes it to the\r
+        *  other toFormulaString signature.\r
+        * @param book   workbook for 3D and named references\r
+        * @param lptgs  list of Ptg, can be null or empty\r
+        * @return a human readable String\r
+        */\r
+       public static String toFormulaString(FormulaRenderingWorkbook book, List lptgs) {\r
+               Ptg[] ptgs = new Ptg[lptgs.size()];\r
+               lptgs.toArray(ptgs);\r
+               return FormulaRenderer.toFormulaString(book, ptgs);\r
+       }\r
+    \r
+       public static String toFormulaString(HSSFWorkbook book, Ptg[] ptgs) {\r
+               return FormulaRenderer.toFormulaString(HSSFEvaluationWorkbook.create(book), ptgs);\r
+       }\r
+}\r
diff --git a/src/java/org/apache/poi/hssf/model/OperandClassTransformer.java b/src/java/org/apache/poi/hssf/model/OperandClassTransformer.java
deleted file mode 100644 (file)
index 8b7b566..0000000
+++ /dev/null
@@ -1,259 +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.model;
-
-import org.apache.poi.hssf.record.formula.AbstractFunctionPtg;
-import org.apache.poi.hssf.record.formula.ControlPtg;
-import org.apache.poi.hssf.record.formula.ValueOperatorPtg;
-import org.apache.poi.hssf.record.formula.Ptg;
-
-/**
- * This class performs 'operand class' transformation. Non-base tokens are classified into three 
- * operand classes:
- * <ul>
- * <li>reference</li> 
- * <li>value</li> 
- * <li>array</li> 
- * </ul>
- * <p/>
- * 
- * The final operand class chosen for each token depends on the formula type and the token's place
- * in the formula. If POI gets the operand class wrong, Excel <em>may</em> interpret the formula
- * incorrectly.  This condition is typically manifested as a formula cell that displays as '#VALUE!',
- * but resolves correctly when the user presses F2, enter.<p/>
- * 
- * The logic implemented here was partially inspired by the description in
- * "OpenOffice.org's Documentation of the Microsoft Excel File Format".  The model presented there
- * seems to be inconsistent with observed Excel behaviour (These differences have not been fully
- * investigated). The implementation in this class has been heavily modified in order to satisfy
- * concrete examples of how Excel performs the same logic (see TestRVA).<p/>
- * 
- * Hopefully, as additional important test cases are identified and added to the test suite, 
- * patterns might become more obvious in this code and allow for simplification.
- * 
- * @author Josh Micich
- */
-final class OperandClassTransformer {
-
-       private final int _formulaType;
-
-       public OperandClassTransformer(int formulaType) {
-               _formulaType = formulaType;
-       }
-
-       /**
-        * Traverses the supplied formula parse tree, calling <tt>Ptg.setClass()</tt> for each non-base
-        * token to set its operand class.
-        */
-       public void transformFormula(ParseNode rootNode) {
-               byte rootNodeOperandClass;
-               switch (_formulaType) {
-                       case FormulaParser.FORMULA_TYPE_CELL:
-                               rootNodeOperandClass = Ptg.CLASS_VALUE;
-                               break;
-                       case FormulaParser.FORMULA_TYPE_DATAVALIDATION_LIST:
-                               rootNodeOperandClass = Ptg.CLASS_REF;
-                               break;
-                       default:
-                               throw new RuntimeException("Incomplete code - formula type (" 
-                                               + _formulaType + ") not supported yet");
-               
-               }
-               transformNode(rootNode, rootNodeOperandClass, false);
-       }
-
-       /**
-        * @param callerForceArrayFlag <code>true</code> if one of the current node's parents is a 
-        * function Ptg which has been changed from default 'V' to 'A' type (due to requirements on
-        * the function return value).
-        */
-       private void transformNode(ParseNode node, byte desiredOperandClass,
-                       boolean callerForceArrayFlag) {
-               Ptg token = node.getToken();
-               ParseNode[] children = node.getChildren();
-               boolean isSimpleValueFunc = isSimpleValueFunction(token);
-               
-               if (isSimpleValueFunc) {
-                       boolean localForceArray = desiredOperandClass == Ptg.CLASS_ARRAY;
-                       for (int i = 0; i < children.length; i++) {
-                               transformNode(children[i], desiredOperandClass, localForceArray);
-                       }
-                       setSimpleValueFuncClass((AbstractFunctionPtg) token, desiredOperandClass, callerForceArrayFlag);
-                       return;
-               }
-               
-               if (token instanceof ValueOperatorPtg || token instanceof ControlPtg) {
-                       // Value Operator Ptgs and Control are base tokens, so token will be unchanged
-                       // but any child nodes are processed according to desiredOperandClass and callerForceArrayFlag
-                       
-                       // As per OOO documentation Sec 3.2.4 "Token Class Transformation", "Step 1"
-                       // All direct operands of value operators that are initially 'R' type will 
-                       // be converted to 'V' type.
-                       byte localDesiredOperandClass = desiredOperandClass == Ptg.CLASS_REF ? Ptg.CLASS_VALUE : desiredOperandClass;
-                       for (int i = 0; i < children.length; i++) {
-                               transformNode(children[i], localDesiredOperandClass, callerForceArrayFlag);
-                       }
-                       return;
-               }
-               if (token instanceof AbstractFunctionPtg) {
-                       transformFunctionNode((AbstractFunctionPtg) token, children, desiredOperandClass, callerForceArrayFlag);
-                       return;
-               }
-               if (children.length > 0) {
-                       throw new IllegalStateException("Node should not have any children");
-               }
-
-               if (token.isBaseToken()) {
-                       // nothing to do
-                       return;
-               }
-               token.setClass(transformClass(token.getPtgClass(), desiredOperandClass, callerForceArrayFlag));
-       }
-
-       private static boolean isSimpleValueFunction(Ptg token) {
-               if (token instanceof AbstractFunctionPtg) {
-                       AbstractFunctionPtg aptg = (AbstractFunctionPtg) token;
-                       if (aptg.getDefaultOperandClass() != Ptg.CLASS_VALUE) {
-                               return false;
-                       }
-                       int numberOfOperands = aptg.getNumberOfOperands();
-                       for (int i=numberOfOperands-1; i>=0; i--) {
-                               if (aptg.getParameterClass(i) != Ptg.CLASS_VALUE) {
-                                       return false;
-                               }
-                       }
-                       return true;
-               }
-               return false;
-       }
-
-       private byte transformClass(byte currentOperandClass, byte desiredOperandClass,
-                       boolean callerForceArrayFlag) {
-               switch (desiredOperandClass) {
-                       case Ptg.CLASS_VALUE:
-                               if (!callerForceArrayFlag) {
-                                       return Ptg.CLASS_VALUE;
-                               }
-                               // else fall through
-                       case Ptg.CLASS_ARRAY:
-                               return Ptg.CLASS_ARRAY; 
-                       case Ptg.CLASS_REF:
-                               if (!callerForceArrayFlag) {
-                                       return currentOperandClass;
-                               }
-                               return Ptg.CLASS_REF; 
-               }
-               throw new IllegalStateException("Unexpected operand class (" + desiredOperandClass + ")");
-       }
-
-       private void transformFunctionNode(AbstractFunctionPtg afp, ParseNode[] children,
-                       byte desiredOperandClass, boolean callerForceArrayFlag) {
-
-               boolean localForceArrayFlag;
-               byte defaultReturnOperandClass = afp.getDefaultOperandClass();
-
-               if (callerForceArrayFlag) {
-                       switch (defaultReturnOperandClass) {
-                               case Ptg.CLASS_REF:
-                                       if (desiredOperandClass == Ptg.CLASS_REF) {
-                                               afp.setClass(Ptg.CLASS_REF);
-                                       } else {
-                                               afp.setClass(Ptg.CLASS_ARRAY);
-                                       }
-                                       localForceArrayFlag = false;
-                                       break;
-                               case Ptg.CLASS_ARRAY:
-                                       afp.setClass(Ptg.CLASS_ARRAY);
-                                       localForceArrayFlag = false;
-                                       break;
-                               case Ptg.CLASS_VALUE:
-                                       afp.setClass(Ptg.CLASS_ARRAY);
-                                       localForceArrayFlag = true;
-                                       break;
-                               default:
-                                       throw new IllegalStateException("Unexpected operand class ("
-                                                       + defaultReturnOperandClass + ")");
-                       }
-               } else {
-                       if (defaultReturnOperandClass == desiredOperandClass) {
-                               localForceArrayFlag = false;
-                               // an alternative would have been to for non-base Ptgs to set their operand class 
-                               // from their default, but this would require the call in many subclasses because
-                               // the default OC is not known until the end of the constructor
-                               afp.setClass(defaultReturnOperandClass); 
-                       } else {
-                               switch (desiredOperandClass) {
-                                       case Ptg.CLASS_VALUE:
-                                               // always OK to set functions to return 'value'
-                                               afp.setClass(Ptg.CLASS_VALUE); 
-                                               localForceArrayFlag = false;
-                                               break;
-                                       case Ptg.CLASS_ARRAY:
-                                               switch (defaultReturnOperandClass) {
-                                                       case Ptg.CLASS_REF:
-                                                               afp.setClass(Ptg.CLASS_REF);
-//                                                             afp.setClass(Ptg.CLASS_ARRAY);
-                                                               break;
-                                                       case Ptg.CLASS_VALUE:
-                                                               afp.setClass(Ptg.CLASS_ARRAY);
-                                                               break;
-                                                       default:
-                                                               throw new IllegalStateException("Unexpected operand class ("
-                                                                               + defaultReturnOperandClass + ")");
-                                               }
-                                               localForceArrayFlag = (defaultReturnOperandClass == Ptg.CLASS_VALUE);
-                                               break;
-                                       case Ptg.CLASS_REF:
-                                               switch (defaultReturnOperandClass) {
-                                                       case Ptg.CLASS_ARRAY:
-                                                               afp.setClass(Ptg.CLASS_ARRAY);
-                                                               break;
-                                                       case Ptg.CLASS_VALUE:
-                                                               afp.setClass(Ptg.CLASS_VALUE);
-                                                               break;
-                                                       default:
-                                                               throw new IllegalStateException("Unexpected operand class ("
-                                                                               + defaultReturnOperandClass + ")");
-                                               }
-                                               localForceArrayFlag = false;
-                                               break;
-                                       default:
-                                               throw new IllegalStateException("Unexpected operand class ("
-                                                               + desiredOperandClass + ")");
-                               }
-
-                       }
-               }
-
-               for (int i = 0; i < children.length; i++) {
-                       ParseNode child = children[i];
-                       byte paramOperandClass = afp.getParameterClass(i);
-                       transformNode(child, paramOperandClass, localForceArrayFlag);
-               }
-       }
-
-       private void setSimpleValueFuncClass(AbstractFunctionPtg afp,
-                       byte desiredOperandClass, boolean callerForceArrayFlag) {
-
-               if (callerForceArrayFlag  || desiredOperandClass == Ptg.CLASS_ARRAY) {
-                       afp.setClass(Ptg.CLASS_ARRAY);
-               } else {
-                       afp.setClass(Ptg.CLASS_VALUE); 
-               }
-       }
-}
diff --git a/src/java/org/apache/poi/hssf/model/ParseNode.java b/src/java/org/apache/poi/hssf/model/ParseNode.java
deleted file mode 100644 (file)
index acd8cb1..0000000
+++ /dev/null
@@ -1,201 +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.model;
-
-import org.apache.poi.hssf.record.formula.AttrPtg;
-import org.apache.poi.hssf.record.formula.FuncVarPtg;
-import org.apache.poi.hssf.record.formula.Ptg;
-import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
-/**
- * Represents a syntactic element from a formula by encapsulating the corresponding <tt>Ptg</tt>
- * token.  Each <tt>ParseNode</tt> may have child <tt>ParseNode</tt>s in the case when the wrapped
- * <tt>Ptg</tt> is non-atomic.
- * 
- * @author Josh Micich
- */
-final class ParseNode {
-
-       public static final ParseNode[] EMPTY_ARRAY = { };
-       private final Ptg _token;
-       private final ParseNode[] _children;
-       private boolean _isIf;
-       private final int _tokenCount;
-
-       public ParseNode(Ptg token, ParseNode[] children) {
-               _token = token;
-               _children = children;
-               _isIf = isIf(token);
-               int tokenCount = 1;
-               for (int i = 0; i < children.length; i++) {
-                       tokenCount += children[i].getTokenCount();
-               }
-               if (_isIf) {
-                       // there will be 2 or 3 extra tAttr tokens according to whether the false param is present
-                       tokenCount += children.length;
-               }
-               _tokenCount = tokenCount;
-       }
-       public ParseNode(Ptg token) {
-               this(token, EMPTY_ARRAY);
-       }
-       public ParseNode(Ptg token, ParseNode child0) {
-               this(token, new ParseNode[] { child0, });
-       }
-       public ParseNode(Ptg token, ParseNode child0, ParseNode child1) {
-               this(token, new ParseNode[] { child0, child1, });
-       }
-       private int getTokenCount() {
-               return _tokenCount;
-       }
-
-       /**
-        * Collects the array of <tt>Ptg</tt> tokens for the specified tree.
-        */
-       public static Ptg[] toTokenArray(ParseNode rootNode) {
-               TokenCollector temp = new TokenCollector(rootNode.getTokenCount());
-               rootNode.collectPtgs(temp);
-               return temp.getResult();
-       }
-       private void collectPtgs(TokenCollector temp) {
-               if (isIf(getToken())) {
-                       collectIfPtgs(temp);
-                       return;
-               }
-               for (int i=0; i< getChildren().length; i++) {
-                       getChildren()[i].collectPtgs(temp);
-               }
-               temp.add(getToken());
-       }
-       /**
-        * The IF() function gets marked up with two or three tAttr tokens.
-        * Similar logic will be required for CHOOSE() when it is supported
-        * 
-        * See excelfileformat.pdf sec 3.10.5 "tAttr (19H)
-        */
-       private void collectIfPtgs(TokenCollector temp) {
-
-               // condition goes first
-               getChildren()[0].collectPtgs(temp);
-               
-               // placeholder for tAttrIf
-               int ifAttrIndex = temp.createPlaceholder();
-               
-               // true parameter
-               getChildren()[1].collectPtgs(temp);
-               
-               // placeholder for first skip attr
-               int skipAfterTrueParamIndex = temp.createPlaceholder();
-               int trueParamSize = temp.sumTokenSizes(ifAttrIndex+1, skipAfterTrueParamIndex);
-
-               AttrPtg attrIf = new AttrPtg();
-               attrIf.setOptimizedIf(true);
-               AttrPtg attrSkipAfterTrue = new AttrPtg();
-               attrSkipAfterTrue.setGoto(true);
-               
-               if (getChildren().length > 2) {
-                       // false param present
-                       
-                       // false parameter
-                       getChildren()[2].collectPtgs(temp);
-                       
-                       int skipAfterFalseParamIndex = temp.createPlaceholder();
-
-                       AttrPtg attrSkipAfterFalse = new AttrPtg();
-                       attrSkipAfterFalse.setGoto(true);
-
-                       int falseParamSize =  temp.sumTokenSizes(skipAfterTrueParamIndex+1, skipAfterFalseParamIndex);
-                       
-                       attrIf.setData((short)(trueParamSize + 4)); // distance to start of false parameter. +4 for skip after true
-                       attrSkipAfterTrue.setData((short)(falseParamSize + 4 + 4 - 1)); // 1 less than distance to end of if FuncVar(size=4). +4 for attr skip before 
-                       attrSkipAfterFalse.setData((short)(4 - 1)); // 1 less than distance to end of if FuncVar(size=4). 
-
-                       temp.setPlaceholder(ifAttrIndex, attrIf);
-                       temp.setPlaceholder(skipAfterTrueParamIndex, attrSkipAfterTrue);
-                       temp.setPlaceholder(skipAfterFalseParamIndex, attrSkipAfterFalse);
-               } else {
-                       // false parameter not present
-                       attrIf.setData((short)(trueParamSize + 4)); // distance to start of FuncVar. +4 for skip after true
-                       attrSkipAfterTrue.setData((short)(4 - 1)); // 1 less than distance to end of if FuncVar(size=4). 
-                       
-                       temp.setPlaceholder(ifAttrIndex, attrIf);
-                       temp.setPlaceholder(skipAfterTrueParamIndex, attrSkipAfterTrue);
-               }
-               
-               temp.add(getToken());
-       }
-
-       private static boolean isIf(Ptg token) {
-               if (token instanceof FuncVarPtg) {
-                       FuncVarPtg func = (FuncVarPtg) token;
-                       if (FunctionMetadataRegistry.FUNCTION_NAME_IF.equals(func.getName())) {
-                               return true;
-                       }
-               }
-               return false;
-       }
-
-       public Ptg getToken() {
-               return _token;
-       }
-
-       public ParseNode[] getChildren() {
-               return _children;
-       }
-
-       private static final class TokenCollector {
-
-               private final Ptg[] _ptgs;
-               private int _offset;
-
-               public TokenCollector(int tokenCount) {
-                       _ptgs = new Ptg[tokenCount];
-                       _offset = 0;
-               }
-
-               public int sumTokenSizes(int fromIx, int toIx) {
-                       int result = 0;
-                       for (int i=fromIx; i<toIx; i++) {
-                               result += _ptgs[i].getSize();
-                       }
-                       return result;
-               }
-
-               public int createPlaceholder() {
-                       return _offset++;
-               }
-
-               public void add(Ptg token) {
-                       if (token == null) {
-                               throw new IllegalArgumentException("token must not be null");
-                       }
-                       _ptgs[_offset] = token;
-                       _offset++;
-               }
-
-               public void setPlaceholder(int index, Ptg token) {
-                       if (_ptgs[index] != null) {
-                               throw new IllegalStateException("Invalid placeholder index (" + index + ")");
-                       }
-                       _ptgs[index] = token;
-               }
-
-               public Ptg[] getResult() {
-                       return _ptgs;
-               }
-       }
-}
index 06256c5cc59679bbccaab682be02417124550b90..cca4e6c652f5290d5116272ca1b9b30149c9d5a0 100644 (file)
@@ -17,7 +17,7 @@
 
 package org.apache.poi.hssf.record;
 
-import org.apache.poi.hssf.model.FormulaParser;
+import org.apache.poi.hssf.model.HSSFFormulaParser;
 import org.apache.poi.hssf.record.cf.BorderFormatting;
 import org.apache.poi.hssf.record.cf.FontFormatting;
 import org.apache.poi.hssf.record.cf.PatternFormatting;
@@ -595,6 +595,6 @@ public final class CFRuleRecord extends Record {
                if(formula == null) {
                        return null;
                }
-               return FormulaParser.parse(formula, workbook);
+               return HSSFFormulaParser.parse(formula, workbook);
        }
 }
index d36124e7b29c3471dabb92380b0c09c9c662651c..dc301bca933e12d68e305692ffafac71d4ae910d 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.poi.hssf.record;
 import java.util.ArrayList;
 import java.util.List;
 
-import org.apache.poi.hssf.model.FormulaParser;
+import org.apache.poi.hssf.model.HSSFFormulaParser;
 import org.apache.poi.hssf.record.formula.Area3DPtg;
 import org.apache.poi.hssf.record.formula.Ptg;
 import org.apache.poi.hssf.record.formula.Ref3DPtg;
@@ -495,7 +495,7 @@ public final class NameRecord extends Record {
         * @return area reference
         */
        public String getAreaReference(HSSFWorkbook book){
-               return FormulaParser.toFormulaString(book, field_13_name_definition);
+               return HSSFFormulaParser.toFormulaString(book, field_13_name_definition);
        }
 
        /** sets the reference , the area only (range)
index f528fb67bd23089eb8cc2feb9b1d3e9131c1b2ec..ca9dd7d3e00a5a6896ae8f0bcc09f8c5f8d8f82f 100644 (file)
@@ -24,13 +24,13 @@ 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.record.formula.functions.FreeRefFunction;
-import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.formula.EvaluationWorkbook;
 
 public final class AnalysisToolPak {
 
        private static final FreeRefFunction NotImplemented = new FreeRefFunction() {
 
-               public ValueEval evaluate(Eval[] args, HSSFWorkbook workbook, int srcCellSheet,
+               public ValueEval evaluate(Eval[] args, EvaluationWorkbook workbook, int srcCellSheet,
                                int srcCellRow, int srcCellCol) {
                        return ErrorEval.FUNCTION_NOT_IMPLEMENTED;
                }
index a6dd10a846eb54f3de0a1537a74ead4a3da654e7..b907d89eabebcb2e3b2f416f44d9f99acb51265b 100644 (file)
@@ -17,7 +17,6 @@
 
 package org.apache.poi.hssf.record.formula.atp;
 
-import org.apache.poi.hssf.record.formula.eval.BlankEval;
 import org.apache.poi.hssf.record.formula.eval.BoolEval;
 import org.apache.poi.hssf.record.formula.eval.ErrorEval;
 import org.apache.poi.hssf.record.formula.eval.Eval;
@@ -25,8 +24,7 @@ import org.apache.poi.hssf.record.formula.eval.EvaluationException;
 import org.apache.poi.hssf.record.formula.eval.OperandResolver;
 import org.apache.poi.hssf.record.formula.eval.ValueEval;
 import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;
-import org.apache.poi.hssf.usermodel.HSSFSheet;
-import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.formula.EvaluationWorkbook;
 /**
  * Implementation of Excel 'Analysis ToolPak' function ISEVEN() ISODD()<br/>
  * 
@@ -42,7 +40,7 @@ final class ParityFunction implements FreeRefFunction {
                _desiredParity = desiredParity;
        }
 
-       public ValueEval evaluate(Eval[] args, HSSFWorkbook workbook, int srcCellSheet, int srcCellRow,
+       public ValueEval evaluate(Eval[] args, EvaluationWorkbook workbook, int srcCellSheet, int srcCellRow,
                        int srcCellCol) {
                if (args.length != 1) {
                        return ErrorEval.VALUE_INVALID;  
index f48eba8d5ef839abd51258c69ce3125fcc8e4e56..05c14f368c20c9b5acf97186c6a1f0665cf4b370 100644 (file)
@@ -21,7 +21,6 @@ import java.util.Calendar;
 import java.util.GregorianCalendar;
 import java.util.regex.Pattern;
 
-import org.apache.poi.hssf.record.formula.eval.BlankEval;
 import org.apache.poi.hssf.record.formula.eval.ErrorEval;
 import org.apache.poi.hssf.record.formula.eval.Eval;
 import org.apache.poi.hssf.record.formula.eval.EvaluationException;
@@ -31,8 +30,7 @@ import org.apache.poi.hssf.record.formula.eval.StringEval;
 import org.apache.poi.hssf.record.formula.eval.ValueEval;
 import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;
 import org.apache.poi.hssf.usermodel.HSSFDateUtil;
-import org.apache.poi.hssf.usermodel.HSSFSheet;
-import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.formula.EvaluationWorkbook;
 /**
  * Implementation of Excel 'Analysis ToolPak' function YEARFRAC()<br/>
  * 
@@ -61,7 +59,7 @@ final class YearFrac implements FreeRefFunction {
                // enforce singleton
        }
 
-       public ValueEval evaluate(Eval[] args, HSSFWorkbook workbook, int srcCellSheet, int srcCellRow,
+       public ValueEval evaluate(Eval[] args, EvaluationWorkbook workbook, int srcCellSheet, int srcCellRow,
                        int srcCellCol) {
 
                double result;
index 96cc64310aaf530c0aa2dbfb0ca7758e4d7a3bbf..8e8a9799e45052a01249e05d924dd5a85cdec463 100755 (executable)
@@ -19,7 +19,7 @@ package org.apache.poi.hssf.record.formula.eval;
 
 import org.apache.poi.hssf.record.formula.atp.AnalysisToolPak;
 import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;
-import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.formula.EvaluationWorkbook;
 /**
  * 
  * Common entry point for all user-defined (non-built-in) functions (where 
@@ -30,7 +30,7 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook;
  */
 final class ExternalFunction implements FreeRefFunction {
 
-       public ValueEval evaluate(Eval[] args, HSSFWorkbook workbook, 
+       public ValueEval evaluate(Eval[] args, EvaluationWorkbook workbook, 
                        int srcCellSheet, int srcCellRow,int srcCellCol) {
                
                int nIncomingArgs = args.length;
@@ -58,9 +58,9 @@ final class ExternalFunction implements FreeRefFunction {
                return targetFunc.evaluate(outGoingArgs, workbook, srcCellSheet, srcCellRow, srcCellCol);
        }
 
-       private FreeRefFunction findExternalUserDefinedFunction(HSSFWorkbook workbook,
+       private FreeRefFunction findExternalUserDefinedFunction(EvaluationWorkbook workbook,
                        NameXEval n) throws EvaluationException {
-               String functionName = workbook.resolveNameXText(n.getSheetRefIndex(), n.getNameNumber());
+               String functionName = workbook.resolveNameXText(n.getPtg());
 
                if(false) {
                        System.out.println("received call to external user defined function (" + functionName + ")");
index 12b6be38059bf6e195b4b3b6daff580b87b2c510..b67f4eb3d3ea1fc0ff4b5147b498e5b75c2eecbb 100644 (file)
 
 package org.apache.poi.hssf.record.formula.eval;
 
+import org.apache.poi.hssf.record.formula.NameXPtg;
+
 /**
  * @author Josh Micich
  */
 public final class NameXEval implements Eval {
 
-       /** index to REF entry in externsheet record */
-       private final int _sheetRefIndex;
-       /** index to defined name or externname table(1 based) */
-       private final int _nameNumber;
+       private final NameXPtg _ptg;
 
-       public NameXEval(int sheetRefIndex, int nameNumber) {
-               _sheetRefIndex = sheetRefIndex;
-               _nameNumber = nameNumber;
+       public NameXEval(NameXPtg ptg) {
+               _ptg = ptg;
        }
 
-       public int getSheetRefIndex() {
-               return _sheetRefIndex;
-       }
-       public int getNameNumber() {
-               return _nameNumber;
+       public NameXPtg getPtg() {
+               return _ptg;
        }
 
        public String toString() {
                StringBuffer sb = new StringBuffer(64);
                sb.append(getClass().getName()).append(" [");
-               sb.append(_sheetRefIndex).append(", ").append(_nameNumber);
+               sb.append(_ptg.getSheetRefIndex()).append(", ").append(_ptg.getNameIndex());
                sb.append("]");
                return sb.toString();
        }
index 977c1b363ea2f67518acb2b1aebb88440406c128..cdec045b40e2da35c9396efb251d7471bc34a78c 100755 (executable)
@@ -19,8 +19,7 @@ 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.formula.EvaluationWorkbook;
 
 
 /**
@@ -53,5 +52,5 @@ public interface FreeRefFunction {
         * a specified Excel error (Exceptions are never thrown to represent Excel errors).
         * 
         */
-       ValueEval evaluate(Eval[] args, HSSFWorkbook workbook, int srcCellSheet, int srcCellRow, int srcCellCol);
+       ValueEval evaluate(Eval[] args, EvaluationWorkbook workbook, int srcCellSheet, int srcCellRow, int srcCellCol);
 }
index f4036c107b47708aa702409569817aab998d52a5..567f29b2b1bba24323a4a081118bd0cfe8b92a65 100644 (file)
@@ -1,27 +1,26 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
 
 package org.apache.poi.hssf.record.formula.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.formula.EvaluationWorkbook;
 
 /**
  * Implementation for Excel function INDIRECT<p/>
@@ -41,7 +40,7 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook;
  */
 public final class Indirect implements FreeRefFunction {
 
-       public ValueEval evaluate(Eval[] args, HSSFWorkbook workbook, int srcCellSheet, int srcCellRow, int srcCellCol) {
+       public ValueEval evaluate(Eval[] args, EvaluationWorkbook workbook, int srcCellSheet, int srcCellRow, int srcCellCol) {
                // TODO - implement INDIRECT()
                return ErrorEval.FUNCTION_NOT_IMPLEMENTED;
        }
index a1027ccfaac170788f7435d4390bbbcfeed38822..5bc478f424ae591652841b9fb7e19efb7e0a45bf 100644 (file)
@@ -20,10 +20,12 @@ import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 
-import org.apache.poi.hssf.model.FormulaParser;
+import org.apache.poi.hssf.model.HSSFFormulaParser;
 import org.apache.poi.hssf.record.formula.NumberPtg;
 import org.apache.poi.hssf.record.formula.Ptg;
 import org.apache.poi.hssf.record.formula.StringPtg;
+import org.apache.poi.ss.formula.FormulaParser;
+import org.apache.poi.ss.formula.FormulaType;
 
 /**
  * 
@@ -339,7 +341,7 @@ public class DVConstraint {
 
                if (_explicitListValues == null) {
                        // formula is parsed with slightly different RVA rules: (root node type must be 'reference')
-                       return FormulaParser.parse(_formula1, workbook, FormulaParser.FORMULA_TYPE_DATAVALIDATION_LIST);
+                       return HSSFFormulaParser.parse(_formula1, workbook, FormulaType.DATAVALIDATION_LIST);
                        // To do: Excel places restrictions on the available operations within a list formula.
                        // Some things like union and intersection are not allowed.
                }
@@ -369,7 +371,7 @@ public class DVConstraint {
                if (value != null) {
                        throw new IllegalStateException("Both formula and value cannot be present");
                }
-               return FormulaParser.parse(formula, workbook);
+               return HSSFFormulaParser.parse(formula, workbook);
        }
        
        
diff --git a/src/java/org/apache/poi/hssf/usermodel/EvaluationCache.java b/src/java/org/apache/poi/hssf/usermodel/EvaluationCache.java
deleted file mode 100644 (file)
index 97c4d6e..0000000
+++ /dev/null
@@ -1,95 +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.HashMap;
-import java.util.Map;
-
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-
-/**
- * Performance optimisation for {@link HSSFFormulaEvaluator}. This class stores previously
- * calculated values of already visited cells, to avoid unnecessary re-calculation when the 
- * same cells are referenced multiple times
- * 
- * 
- * @author Josh Micich
- */
-final class EvaluationCache {
-       private static final class Key {
-
-               private final int _sheetIndex;
-               private final int _srcRowNum;
-               private final int _srcColNum;
-               private final int _hashCode;
-
-               public Key(int sheetIndex, int srcRowNum, int srcColNum) {
-                       _sheetIndex = sheetIndex;
-                       _srcRowNum = srcRowNum;
-                       _srcColNum = srcColNum;
-                       _hashCode = sheetIndex + srcRowNum + srcColNum;
-               }
-
-               public int hashCode() {
-                       return _hashCode;
-               }
-
-               public boolean equals(Object obj) {
-                       Key other = (Key) obj;
-                       if (_hashCode != other._hashCode) {
-                               return false;
-                       }
-                       if (_sheetIndex != other._sheetIndex) {
-                               return false;
-                       }
-                       if (_srcRowNum != other._srcRowNum) {
-                               return false;
-                       }
-                       if (_srcColNum != other._srcColNum) {
-                               return false;
-                       }
-                       return true;
-               }
-       }
-
-       private final Map _valuesByKey;
-
-       /* package */EvaluationCache() {
-               _valuesByKey = new HashMap();
-       }
-
-       public ValueEval getValue(int sheetIndex, int srcRowNum, int srcColNum) {
-               Key key = new Key(sheetIndex, srcRowNum, srcColNum);
-               return (ValueEval) _valuesByKey.get(key);
-       }
-
-       public void setValue(int sheetIndex, int srcRowNum, int srcColNum, ValueEval value) {
-               Key key = new Key(sheetIndex, srcRowNum, srcColNum);
-               if (_valuesByKey.containsKey(key)) {
-                       throw new RuntimeException("Already have cached value for this cell");
-               }
-               _valuesByKey.put(key, value);
-       }
-
-       /**
-        * Should be called whenever there are changes to input cells in the evaluated workbook.
-        */
-       public void clear() {
-               _valuesByKey.clear();
-       }
-}
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 0c168af..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 int _sheetIndex;
-               private final int _srcRowNum;
-               private final int _srcColNum;
-
-               public CellEvaluationFrame(HSSFWorkbook workbook, int sheetIndex, int srcRowNum, int srcColNum) {
-                       if (workbook == null) {
-                               throw new IllegalArgumentException("workbook must not be null");
-                       }
-                       if (sheetIndex < 0) {
-                               throw new IllegalArgumentException("sheetIndex must not be negative");
-                       }
-                       _workbook = workbook;
-                       _sheetIndex = sheetIndex;
-                       _srcRowNum = srcRowNum;
-                       _srcColNum = srcColNum;
-               }
-
-               public boolean equals(Object obj) {
-                       CellEvaluationFrame other = (CellEvaluationFrame) obj;
-                       if (_workbook != other._workbook) {
-                               return false;
-                       }
-                       if (_sheetIndex != other._sheetIndex) {
-                               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=" + _sheetIndex;
-               }
-
-               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, int sheetIndex, int srcRowNum, int srcColNum) {
-               CellEvaluationFrame cef = new CellEvaluationFrame(workbook, sheetIndex, 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, int sheetIndex, 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, sheetIndex, 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 9f12f4305efc059247060b30e011e6f9a03a0cce..3d2f99f41ed2e27c2fb1bd5fc3173d0f6edc68d3 100644 (file)
@@ -25,7 +25,7 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 
-import org.apache.poi.hssf.model.FormulaParser;
+import org.apache.poi.hssf.model.HSSFFormulaParser;
 import org.apache.poi.hssf.model.Sheet;
 import org.apache.poi.hssf.model.Workbook;
 import org.apache.poi.hssf.record.BlankRecord;
@@ -124,7 +124,7 @@ public final class HSSFCell {
         short xfindex = sheet.getSheet().getXFIndexForColAt(col);
         setCellType(CELL_TYPE_BLANK, false, row, col,xfindex);
     }
-    /* package */ HSSFSheet getSheet() {
+    public HSSFSheet getSheet() {
         return sheet;
     }
 
@@ -589,12 +589,12 @@ public final class HSSFCell {
         if (rec.getXFIndex() == (short)0) {
             rec.setXFIndex((short) 0x0f);
         }
-        Ptg[] ptgs = FormulaParser.parse(formula, book);
+        Ptg[] ptgs = HSSFFormulaParser.parse(formula, book);
         frec.setParsedExpression(ptgs);
     }
 
     public String getCellFormula() {
-        return FormulaParser.toFormulaString(book, ((FormulaRecordAggregate)record).getFormulaRecord().getParsedExpression());
+        return HSSFFormulaParser.toFormulaString(book, ((FormulaRecordAggregate)record).getFormulaRecord().getParsedExpression());
     }
 
     /**
index fc34b9da9c693ab7c8560f4511c022ac98c70868..29430b094fd53199c33ed73121e9633a9079af67 100644 (file)
@@ -17,7 +17,7 @@
 
 package org.apache.poi.hssf.usermodel;
 
-import org.apache.poi.hssf.model.FormulaParser;
+import org.apache.poi.hssf.model.HSSFFormulaParser;
 import org.apache.poi.hssf.record.CFRuleRecord;
 import org.apache.poi.hssf.record.CFRuleRecord.ComparisonOperator;
 import org.apache.poi.hssf.record.cf.BorderFormatting;
@@ -205,6 +205,6 @@ public final class HSSFConditionalFormattingRule
                if(parsedExpression ==null) {
                        return null;
                }
-               return FormulaParser.toFormulaString(workbook, parsedExpression);
+               return HSSFFormulaParser.toFormulaString(workbook, parsedExpression);
        }
 }
index f9399ee8c54eeecc9656cb5bbe4a8a4e45d3ceb9..16ef3b2a9f898c575bc24e2def684a1ca0c3ca73 100644 (file)
@@ -1,8 +1,14 @@
 package org.apache.poi.hssf.usermodel;\r
 \r
+import org.apache.poi.hssf.model.HSSFFormulaParser;\r
 import org.apache.poi.hssf.model.Workbook;\r
+import org.apache.poi.hssf.record.NameRecord;\r
 import org.apache.poi.hssf.record.formula.NamePtg;\r
 import org.apache.poi.hssf.record.formula.NameXPtg;\r
+import org.apache.poi.hssf.record.formula.Ptg;\r
+import org.apache.poi.ss.formula.EvaluationName;\r
+import org.apache.poi.ss.formula.EvaluationWorkbook;\r
+import org.apache.poi.ss.formula.FormulaParsingWorkbook;\r
 import org.apache.poi.ss.formula.FormulaRenderingWorkbook;\r
 \r
 /**\r
@@ -10,8 +16,9 @@ import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
  * \r
  * @author Josh Micich\r
  */\r
-public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook {\r
+public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, EvaluationWorkbook, FormulaParsingWorkbook {\r
 \r
+       private final HSSFWorkbook _uBook;\r
        private final Workbook _iBook;\r
        \r
        public static HSSFEvaluationWorkbook create(HSSFWorkbook book) {\r
@@ -22,9 +29,58 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook {
        }\r
 \r
        private HSSFEvaluationWorkbook(HSSFWorkbook book) {\r
+               _uBook = book;\r
                _iBook = book.getWorkbook();\r
        }\r
 \r
+       public int getExternalSheetIndex(String sheetName) {\r
+               int sheetIndex = _uBook.getSheetIndex(sheetName);\r
+               return _iBook.checkExternSheet(sheetIndex);\r
+       }\r
+\r
+       public EvaluationName getName(int index) {\r
+               return new Name(_iBook.getNameRecord(index), index);\r
+       }\r
+\r
+       public EvaluationName getName(String name) {\r
+               for(int i=0; i < _iBook.getNumNames(); i++) {\r
+                       NameRecord nr = _iBook.getNameRecord(i);\r
+                       if (name.equalsIgnoreCase(nr.getNameText())) {\r
+                               return new Name(nr, i);\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+\r
+       public int getSheetIndex(HSSFSheet sheet) {\r
+               return _uBook.getSheetIndex(sheet);\r
+       }\r
+\r
+       public String getSheetName(int sheetIndex) {\r
+               return _uBook.getSheetName(sheetIndex);\r
+       }\r
+\r
+       public int getNameIndex(String name) {\r
+               return _uBook.getNameIndex(name);\r
+       }\r
+\r
+       public NameXPtg getNameXPtg(String name) {\r
+               return _iBook.getNameXPtg(name);\r
+       }\r
+\r
+       public HSSFSheet getSheet(int sheetIndex) {\r
+               return _uBook.getSheetAt(sheetIndex);\r
+       }\r
+\r
+       public HSSFSheet getSheetByExternSheetIndex(int externSheetIndex) {\r
+               int sheetIndex = _iBook.getSheetIndexFromExternSheetIndex(externSheetIndex);\r
+               return _uBook.getSheetAt(sheetIndex);\r
+       }\r
+\r
+       public HSSFWorkbook getWorkbook() {\r
+               return _uBook;\r
+       }\r
+\r
        public String resolveNameXText(NameXPtg n) {\r
                return _iBook.resolveNameXText(n.getSheetRefIndex(), n.getNameIndex());\r
        }\r
@@ -35,4 +91,45 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook {
        public String getNameText(NamePtg namePtg) {\r
                return _iBook.getNameRecord(namePtg.getIndex()).getNameText();\r
        }\r
+       public EvaluationName getName(NamePtg namePtg) {\r
+               int ix = namePtg.getIndex();\r
+               return new Name(_iBook.getNameRecord(ix), ix);\r
+       }\r
+       public Ptg[] getFormulaTokens(HSSFCell cell) {\r
+               return HSSFFormulaParser.parse(cell.getCellFormula(), _uBook);\r
+       }\r
+\r
+       private static final class Name implements EvaluationName {\r
+\r
+               private final NameRecord _nameRecord;\r
+               private final int _index;\r
+\r
+               public Name(NameRecord nameRecord, int index) {\r
+                       _nameRecord = nameRecord;\r
+                       _index = index;\r
+               }\r
+\r
+               public Ptg[] getNameDefinition() {\r
+                       return _nameRecord.getNameDefinition();\r
+               }\r
+\r
+               public String getNameText() {\r
+                       return _nameRecord.getNameText();\r
+               }\r
+\r
+               public boolean hasFormula() {\r
+                       return _nameRecord.hasFormula();\r
+               }\r
+\r
+               public boolean isFunctionName() {\r
+                       return _nameRecord.isFunctionName();\r
+               }\r
+\r
+               public boolean isRange() {\r
+                       return _nameRecord.hasFormula(); // TODO - is this right?\r
+               }\r
+               public NamePtg createPtg() {\r
+                       return new NamePtg(_index);\r
+               }\r
+       }\r
 }\r
index 0e6e4933821d78701f3b42e12be98b177ec3f18a..1e6aa163c065854b8a6c0c9de4460870ed65e263 100644 (file)
-/* ====================================================================
-   Licensed to the Apache Software Foundation (ASF) under one or more
-   contributor license agreements.  See the NOTICE file distributed with
-   this work for additional information regarding copyright ownership.
-   The ASF licenses this file to You under the Apache License, Version 2.0
-   (the "License"); you may not use this file except in compliance with
-   the License.  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-==================================================================== */
-
-package org.apache.poi.hssf.usermodel;
-
-import java.util.Iterator;
-import java.util.Stack;
-
-import org.apache.poi.hssf.model.FormulaParser;
-import org.apache.poi.hssf.model.Workbook;
-import org.apache.poi.hssf.record.NameRecord;
-import org.apache.poi.hssf.record.formula.Area3DPtg;
-import org.apache.poi.hssf.record.formula.AreaPtg;
-import org.apache.poi.hssf.record.formula.BoolPtg;
-import org.apache.poi.hssf.record.formula.ControlPtg;
-import org.apache.poi.hssf.record.formula.ErrPtg;
-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.Ptg;
-import org.apache.poi.hssf.record.formula.Ref3DPtg;
-import org.apache.poi.hssf.record.formula.RefPtg;
-import org.apache.poi.hssf.record.formula.StringPtg;
-import org.apache.poi.hssf.record.formula.UnionPtg;
-import org.apache.poi.hssf.record.formula.UnknownPtg;
-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.NameXEval;
-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.RefEval;
-import org.apache.poi.hssf.record.formula.eval.StringEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-import org.apache.poi.hssf.util.CellReference;
-
-/**
- * Evaluates formula cells.<p/>
- * 
- * For performance reasons, this class keeps a cache of all previously calculated intermediate
- * cell values.  Be sure to call {@link #clearCache()} if any workbook cells are changed between
- * calls to evaluate~ methods on this class.
- * 
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- * @author Josh Micich
- */
-public class HSSFFormulaEvaluator {
-
-    /**
-     * used to track the number of evaluations
-     */
-    private static final class Counter {
-        public int value;
-        public int depth;
-        public Counter() {
-            value = 0;
-        }
-    }
-
-    private final HSSFWorkbook _workbook;
-    private final EvaluationCache _cache;
-
-    private Counter _evaluationCounter;
-
-    /**
-     * @deprecated (Sep 2008) HSSFSheet parameter is ignored
-     */
-    public HSSFFormulaEvaluator(HSSFSheet sheet, HSSFWorkbook workbook) {
-        this(workbook);
-        if (false) {
-            sheet.toString(); // suppress unused parameter compiler warning
-        }
-    }
-    public HSSFFormulaEvaluator(HSSFWorkbook workbook) {
-        this(workbook, new EvaluationCache(), new Counter());
-    }
-
-    private HSSFFormulaEvaluator(HSSFWorkbook workbook, EvaluationCache cache, Counter evaluationCounter) {
-        _workbook = workbook;
-        _cache = cache;
-        _evaluationCounter = evaluationCounter;
-    }
-
-    /**
-     * for debug use. Used in toString methods
-     */
-    /* package */ String getSheetName(HSSFSheet sheet) {
-        return _workbook.getSheetName(_workbook.getSheetIndex(sheet));
-    }
-    /**
-     * for debug/test use
-     */
-    /* package */ int getEvaluationCount() {
-        return _evaluationCounter.value;
-    }
-
-    private static boolean isDebugLogEnabled() {
-        return false;
-    }
-    private static void logDebug(String s) {
-        if (isDebugLogEnabled()) {
-            System.out.println(s);
-        }
-    }
-
-    /**
-     * Does nothing
-     * @deprecated (Aug 2008) - not needed, since the current row can be derived from the cell
-     */
-    public void setCurrentRow(HSSFRow row) {
-        // do nothing
-        if (false) {
-            row.getClass(); // suppress unused parameter compiler warning
-        }
-    }
-
-    /**
-     * Should be called whenever there are changes to input cells in the evaluated workbook.
-     * Failure to call this method after changing cell values will cause incorrect behaviour
-     * of the evaluate~ methods of this class
-     */
-    public void clearCache() {
-        _cache.clear();
-    }
-
-    /**
-     * Returns an underlying FormulaParser, for the specified
-     *  Formula String and HSSFWorkbook.
-     * This will allow you to generate the Ptgs yourself, if
-     *  your needs are more complex than just having the
-     *  formula evaluated.
-     */
-    public static FormulaParser getUnderlyingParser(HSSFWorkbook workbook, String formula) {
-        return new FormulaParser(formula, workbook);
-    }
-
-    /**
-     * 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) {
-        if (cell == null) {
-            return null;
-        }
-        
-        switch (cell.getCellType()) {
-            case HSSFCell.CELL_TYPE_BOOLEAN:
-                return CellValue.valueOf(cell.getBooleanCellValue());
-            case HSSFCell.CELL_TYPE_ERROR:
-                return CellValue.getError(cell.getErrorCellValue());
-            case HSSFCell.CELL_TYPE_FORMULA:
-                return evaluateFormulaCellValue(cell);
-            case HSSFCell.CELL_TYPE_NUMERIC:
-                return new CellValue(cell.getNumericCellValue());
-            case HSSFCell.CELL_TYPE_STRING:
-                return new CellValue(cell.getRichStringCellValue().getString());
-        }
-        throw new IllegalStateException("Bad cell type (" + cell.getCellType() + ")");
-    }
-
-
-    /**
-     * 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 || cell.getCellType() != HSSFCell.CELL_TYPE_FORMULA) {
-            return -1;
-        }
-        CellValue cv = evaluateFormulaCellValue(cell);
-        // cell remains a formula cell, but the cached value is changed
-        setCellValue(cell, cv);
-        return cv.getCellType();
-    }
-
-    /**
-     * 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) {
-            return null;
-        }
-        if (cell.getCellType() == HSSFCell.CELL_TYPE_FORMULA) {
-            CellValue cv = evaluateFormulaCellValue(cell);
-            setCellType(cell, cv); // cell will no longer be a formula cell
-            setCellValue(cell, cv);
-        }
-        return cell;
-    }
-    private static void setCellType(HSSFCell cell, CellValue cv) {
-        int cellType = cv.getCellType();
-        switch (cellType) {
-            case HSSFCell.CELL_TYPE_BOOLEAN:
-            case HSSFCell.CELL_TYPE_ERROR:
-            case HSSFCell.CELL_TYPE_NUMERIC:
-            case HSSFCell.CELL_TYPE_STRING:
-                cell.setCellType(cellType);
-                return;
-            case HSSFCell.CELL_TYPE_BLANK:
-                // never happens - blanks eventually get translated to zero
-            case HSSFCell.CELL_TYPE_FORMULA:
-                // this will never happen, we have already evaluated the formula
-        }
-        throw new IllegalStateException("Unexpected cell value type (" + cellType + ")");
-    }
-
-    private static void setCellValue(HSSFCell cell, CellValue cv) {
-        int cellType = cv.getCellType();
-        switch (cellType) {
-            case HSSFCell.CELL_TYPE_BOOLEAN:
-                cell.setCellValue(cv.getBooleanValue());
-                break;
-            case HSSFCell.CELL_TYPE_ERROR:
-                cell.setCellErrorValue(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:
-                // never happens - blanks eventually get translated to zero
-            case HSSFCell.CELL_TYPE_FORMULA:
-                // this will never happen, we have already evaluated the formula
-            default:
-                throw new IllegalStateException("Unexpected cell value type (" + cellType + ")");
-        }
-    }
-
-    /**
-     * 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) {
-        HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(wb);
-        for(int i=0; i<wb.getNumberOfSheets(); i++) {
-            HSSFSheet sheet = wb.getSheetAt(i);
-
-            for (Iterator rit = sheet.rowIterator(); rit.hasNext();) {
-                HSSFRow r = (HSSFRow)rit.next();
-
-                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
-     */
-    private CellValue evaluateFormulaCellValue(HSSFCell cell) {
-        ValueEval eval = internalEvaluate(cell);
-        if (eval instanceof NumberEval) {
-            NumberEval ne = (NumberEval) eval;
-            return new CellValue(ne.getNumberValue());
-        }
-        if (eval instanceof BoolEval) {
-            BoolEval be = (BoolEval) eval;
-            return CellValue.valueOf(be.getBooleanValue());
-        }
-        if (eval instanceof StringEval) {
-            StringEval ne = (StringEval) eval;
-            return new CellValue(ne.getStringValue());
-        }
-        if (eval instanceof ErrorEval) {
-            return CellValue.getError(((ErrorEval)eval).getErrorCode());
-        }
-        throw new RuntimeException("Unexpected eval class (" + eval.getClass().getName() + ")");
-    }
-
-    /**
-     * 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.)
-     * @return never <code>null</code>, never {@link BlankEval}
-     */
-    private ValueEval internalEvaluate(HSSFCell srcCell) {
-        int srcRowNum = srcCell.getRowIndex();
-        int srcColNum = srcCell.getCellNum();
-
-        ValueEval result;
-
-        int sheetIndex = _workbook.getSheetIndex(srcCell.getSheet());
-        result = _cache.getValue(sheetIndex, srcRowNum, srcColNum);
-        if (result != null) {
-            return result;
-        }
-        _evaluationCounter.value++;
-        _evaluationCounter.depth++;
-
-        EvaluationCycleDetector tracker = EvaluationCycleDetectorManager.getTracker();
-
-        if(!tracker.startEvaluate(_workbook, sheetIndex, srcRowNum, srcColNum)) {
-            return ErrorEval.CIRCULAR_REF_ERROR;
-        }
-        try {
-            result = evaluateCell(sheetIndex, srcRowNum, (short)srcColNum, srcCell.getCellFormula());
-        } finally {
-            tracker.endEvaluate(_workbook, sheetIndex, srcRowNum, srcColNum);
-            _cache.setValue(sheetIndex, srcRowNum, srcColNum, result);
-            _evaluationCounter.depth--;
-        }
-        if (isDebugLogEnabled()) {
-            String sheetName = _workbook.getSheetName(sheetIndex);
-            CellReference cr = new CellReference(srcRowNum, srcColNum);
-            logDebug("Evaluated " + sheetName + "!" + cr.formatAsString() + " to " + result.toString());
-        }
-        return result;
-    }
-    private ValueEval evaluateCell(int sheetIndex, int srcRowNum, short srcColNum, String cellFormulaText) {
-
-        Ptg[] ptgs = FormulaParser.parse(cellFormulaText, _workbook);
-
-        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) {
-                // skip Parentheses, Attr, etc
-                continue;
-            }
-            if (ptg instanceof MemErrPtg) { continue; }
-            if (ptg instanceof MissingArgPtg) {
-                // TODO - might need to push BlankEval or MissingArgEval
-                continue;
-            }
-            Eval opResult;
-            if (ptg instanceof OperationPtg) {
-                OperationPtg optg = (OperationPtg) ptg;
-
-                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;
-                }
-//                logDebug("invoke " + operation + " (nAgs=" + numops + ")");
-                opResult = invokeOperation(operation, ops, _workbook, sheetIndex, srcRowNum, srcColNum);
-            } else {
-                opResult = getEvalForPtg(ptg, sheetIndex);
-            }
-            if (opResult == null) {
-                throw new RuntimeException("Evaluation result must not be null");
-            }
-//            logDebug("push " + opResult);
-            stack.push(opResult);
-        }
-
-        ValueEval value = ((ValueEval) stack.pop());
-        if (!stack.isEmpty()) {
-            throw new IllegalStateException("evaluation stack not empty");
-        }
-        value = dereferenceValue(value, srcRowNum, srcColNum);
-        if (value == BlankEval.INSTANCE) {
-            // 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.getRelativeValue(0, 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, 
-            HSSFWorkbook workbook, int sheetIndex, int srcRowNum, int srcColNum) {
-
-        if(operation instanceof FunctionEval) {
-            FunctionEval fe = (FunctionEval) operation;
-            if(fe.isFreeRefFunction()) {
-                return fe.getFreeRefFunction().evaluate(ops, workbook, sheetIndex, srcRowNum, srcColNum);
-            }
-        }
-        return operation.evaluate(ops, srcRowNum, (short)srcColNum);
-    }
-
-    private HSSFSheet getOtherSheet(int externSheetIndex) {
-        Workbook wb = _workbook.getWorkbook();
-        return _workbook.getSheetAt(wb.getSheetIndexFromExternSheetIndex(externSheetIndex));
-    }
-
-    /**
-     * returns an appropriate Eval impl instance for the Ptg. The Ptg must be
-     * one of: Area3DPtg, AreaPtg, ReferencePtg, Ref3DPtg, IntPtg, NumberPtg,
-     * StringPtg, BoolPtg <br/>special Note: OperationPtg subtypes cannot be
-     * passed here!
-     */
-    private Eval getEvalForPtg(Ptg ptg, int sheetIndex) {
-        if (ptg instanceof NamePtg) {
-            // named ranges, macro functions
-            NamePtg namePtg = (NamePtg) ptg;
-            int numberOfNames = _workbook.getNumberOfNames();
-            int nameIndex = namePtg.getIndex();
-            if(nameIndex < 0 || nameIndex >= numberOfNames) {
-                throw new RuntimeException("Bad name index (" + nameIndex
-                        + "). Allowed range is (0.." + (numberOfNames-1) + ")");
-            }
-            NameRecord nameRecord = _workbook.getWorkbook().getNameRecord(nameIndex);
-            if (nameRecord.isFunctionName()) {
-                return new NameEval(nameRecord.getNameText());
-            }
-            if (nameRecord.hasFormula()) {
-                return evaluateNameFormula(nameRecord.getNameDefinition(), sheetIndex);
-            }
-
-            throw new RuntimeException("Don't now how to evalate name '" + nameRecord.getNameText() + "'");
-        }
-        if (ptg instanceof NameXPtg) {
-            NameXPtg nameXPtg = (NameXPtg) ptg;
-            return new NameXEval(nameXPtg.getSheetRefIndex(), nameXPtg.getNameIndex());
-        }
-
-        if (ptg instanceof IntPtg) {
-            return new NumberEval(((IntPtg)ptg).getValue());
-        }
-        if (ptg instanceof NumberPtg) {
-            return new NumberEval(((NumberPtg)ptg).getValue());
-        }
-        if (ptg instanceof StringPtg) {
-            return new StringEval(((StringPtg) ptg).getValue());
-        }
-        if (ptg instanceof BoolPtg) {
-            return BoolEval.valueOf(((BoolPtg) ptg).getValue());
-        }
-        if (ptg instanceof ErrPtg) {
-            return ErrorEval.valueOf(((ErrPtg) ptg).getErrorCode());
-        }
-        HSSFSheet sheet = _workbook.getSheetAt(sheetIndex);
-        if (ptg instanceof RefPtg) {
-            return new LazyRefEval(((RefPtg) ptg), sheet, this);
-        }
-        if (ptg instanceof AreaPtg) {
-            return new LazyAreaEval(((AreaPtg) ptg), sheet, this);
-        }
-        if (ptg instanceof Ref3DPtg) {
-            Ref3DPtg refPtg = (Ref3DPtg) ptg;
-            HSSFSheet xsheet = getOtherSheet(refPtg.getExternSheetIndex());
-            return new LazyRefEval(refPtg, xsheet, this);
-        }
-        if (ptg instanceof Area3DPtg) {
-            Area3DPtg a3dp = (Area3DPtg) ptg;
-            HSSFSheet xsheet = getOtherSheet(a3dp.getExternSheetIndex());
-            return new LazyAreaEval(a3dp, xsheet, this);
-        }
-
-        if (ptg instanceof UnknownPtg) {
-            // POI uses UnknownPtg when the encoded Ptg array seems to be corrupted.
-            // This seems to occur in very rare cases (e.g. unused name formulas in bug 44774, attachment 21790)
-            // In any case, formulas are re-parsed before execution, so UnknownPtg should not get here 
-            throw new RuntimeException("UnknownPtg not allowed");
-        }
-        
-        throw new RuntimeException("Unexpected ptg class (" + ptg.getClass().getName() + ")");
-    }
-    private Eval evaluateNameFormula(Ptg[] ptgs, int sheetIndex) {
-        if (ptgs.length > 1) {
-            throw new RuntimeException("Complex name formulas not supported yet");
-        }
-        return getEvalForPtg(ptgs[0], sheetIndex);
-    }
-
-    /**
-     * 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.
-     */
-    /* package */ ValueEval getEvalForCell(HSSFCell cell) {
-
-        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);
-            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() + ")");
-    }
-
-    /**
-     * 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 {
-        public static final CellValue TRUE = new CellValue(HSSFCell.CELL_TYPE_BOOLEAN, 0.0, true,  null, 0);
-        public static final CellValue FALSE= new CellValue(HSSFCell.CELL_TYPE_BOOLEAN, 0.0, false, null, 0);
-        
-        private final int _cellType;
-        private final double _numberValue;
-        private final boolean _booleanValue;
-        private final String _textValue;
-        private final int _errorCode;
-
-        private CellValue(int cellType, double numberValue, boolean booleanValue, 
-                String textValue, int errorCode) {
-            _cellType = cellType;
-            _numberValue = numberValue;
-            _booleanValue = booleanValue;
-            _textValue = textValue;
-            _errorCode = errorCode;
-        }
-        
-        
-        /* package*/ CellValue(double numberValue) {
-            this(HSSFCell.CELL_TYPE_NUMERIC, numberValue, false, null, 0);
-        }
-        /* package*/ static CellValue valueOf(boolean booleanValue) {
-            return booleanValue ? TRUE : FALSE;
-        }
-        /* package*/ CellValue(String stringValue) {
-            this(HSSFCell.CELL_TYPE_STRING, 0.0, false, stringValue, 0);
-        }
-        /* package*/ static CellValue getError(int errorCode) {
-            return new CellValue(HSSFCell.CELL_TYPE_ERROR, 0.0, false, null, errorCode);
-        }
-        
-        
-        /**
-         * @return Returns the booleanValue.
-         */
-        public boolean getBooleanValue() {
-            return _booleanValue;
-        }
-        /**
-         * @return Returns the numberValue.
-         */
-        public double getNumberValue() {
-            return _numberValue;
-        }
-        /**
-         * @return Returns the stringValue.
-         */
-        public String getStringValue() {
-            return _textValue;
-        }
-        /**
-         * @return Returns the cellType.
-         */
-        public int getCellType() {
-            return _cellType;
-        }
-        /**
-         * @return Returns the errorValue.
-         */
-        public byte getErrorValue() {
-            return (byte) _errorCode;
-        }
-        /**
-         * @return Returns the richTextStringValue.
-         * @deprecated (Sep 2008) Text formatting is lost during formula evaluation.  Use {@link #getStringValue()}  
-         */
-        public HSSFRichTextString getRichTextStringValue() {
-            return new HSSFRichTextString(_textValue);
-        }
-        public String toString() {
-            StringBuffer sb = new StringBuffer(64);
-            sb.append(getClass().getName()).append(" [");
-            sb.append(formatAsString());
-            sb.append("]");
-            return sb.toString();
-        }
-
-        public String formatAsString() {
-            switch (_cellType) {
-                case HSSFCell.CELL_TYPE_NUMERIC:
-                    return String.valueOf(_numberValue);
-                case HSSFCell.CELL_TYPE_STRING:
-                    return '"' + _textValue + '"';
-                case HSSFCell.CELL_TYPE_BOOLEAN:
-                    return _booleanValue ? "TRUE" : "FALSE";
-                case HSSFCell.CELL_TYPE_ERROR:
-                    return ErrorEval.getText(_errorCode);
-            }
-            return "<error unexpected cell type " + _cellType + ">";
-        }
-    }
-
-    /**
-     * debug method
-     */
-    void inspectPtgs(String formula) {
-        Ptg[] ptgs = FormulaParser.parse(formula, _workbook);
-        System.out.println("<ptg-group>");
-        for (int i = 0, iSize = ptgs.length; i < iSize; i++) {
-            System.out.println("<ptg>");
-            System.out.println(ptgs[i]);
-            if (ptgs[i] instanceof OperationPtg) {
-                System.out.println("numoperands: " + ((OperationPtg) ptgs[i]).getNumberOfOperands());
-            }
-            System.out.println("</ptg>");
-        }
-        System.out.println("</ptg-group>");
-    }
-}
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.hssf.usermodel;\r
+\r
+import java.util.Iterator;\r
+\r
+import org.apache.poi.hssf.record.formula.eval.BoolEval;\r
+import org.apache.poi.hssf.record.formula.eval.ErrorEval;\r
+import org.apache.poi.hssf.record.formula.eval.NumberEval;\r
+import org.apache.poi.hssf.record.formula.eval.StringEval;\r
+import org.apache.poi.hssf.record.formula.eval.ValueEval;\r
+import org.apache.poi.ss.formula.WorkbookEvaluator;\r
+\r
+/**\r
+ * Evaluates formula cells.<p/>\r
+ *\r
+ * For performance reasons, this class keeps a cache of all previously calculated intermediate\r
+ * cell values.  Be sure to call {@link #clearCache()} if any workbook cells are changed between\r
+ * calls to evaluate~ methods on this class.\r
+ *\r
+ * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;\r
+ * @author Josh Micich\r
+ */\r
+public class HSSFFormulaEvaluator {\r
+\r
+       private WorkbookEvaluator _bookEvaluator;\r
+\r
+       /**\r
+        * @deprecated (Sep 2008) HSSFSheet parameter is ignored\r
+        */\r
+       public HSSFFormulaEvaluator(HSSFSheet sheet, HSSFWorkbook workbook) {\r
+               this(workbook);\r
+               if (false) {\r
+                       sheet.toString(); // suppress unused parameter compiler warning\r
+               }\r
+       }\r
+       public HSSFFormulaEvaluator(HSSFWorkbook workbook) {\r
+               _bookEvaluator = new WorkbookEvaluator(HSSFEvaluationWorkbook.create(workbook));\r
+       }\r
+\r
+       /**\r
+        * TODO for debug/test use\r
+        */\r
+       /* package */ int getEvaluationCount() {\r
+               return _bookEvaluator.getEvaluationCount();\r
+       }\r
+\r
+       /**\r
+        * Does nothing\r
+        * @deprecated (Aug 2008) - not needed, since the current row can be derived from the cell\r
+        */\r
+       public void setCurrentRow(HSSFRow row) {\r
+               // do nothing\r
+               if (false) {\r
+                       row.getClass(); // suppress unused parameter compiler warning\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Should be called whenever there are major changes (e.g. moving sheets) to input cells\r
+        * in the evaluated workbook.\r
+        * Failure to call this method after changing cell values will cause incorrect behaviour\r
+        * of the evaluate~ methods of this class\r
+        */\r
+       public void clearAllCachedResultValues() {\r
+               _bookEvaluator.clearAllCachedResultValues();\r
+       }\r
+       /**\r
+        * Should be called whenever there are changes to individual input cells in the evaluated workbook.\r
+        * Failure to call this method after changing cell values will cause incorrect behaviour\r
+        * of the evaluate~ methods of this class\r
+        */\r
+       public void clearCachedResultValue(HSSFSheet sheet, int rowIndex, int columnIndex) {\r
+               _bookEvaluator.clearCachedResultValue(sheet, rowIndex, columnIndex);\r
+       }\r
+\r
+       /**\r
+        * If cell contains a formula, the formula is evaluated and returned,\r
+        * else the CellValue simply copies the appropriate cell value from\r
+        * the cell and also its cell type. This method should be preferred over\r
+        * evaluateInCell() when the call should not modify the contents of the\r
+        * original cell.\r
+        * @param cell\r
+        */\r
+       public CellValue evaluate(HSSFCell cell) {\r
+               if (cell == null) {\r
+                       return null;\r
+               }\r
+\r
+               switch (cell.getCellType()) {\r
+                       case HSSFCell.CELL_TYPE_BOOLEAN:\r
+                               return CellValue.valueOf(cell.getBooleanCellValue());\r
+                       case HSSFCell.CELL_TYPE_ERROR:\r
+                               return CellValue.getError(cell.getErrorCellValue());\r
+                       case HSSFCell.CELL_TYPE_FORMULA:\r
+                               return evaluateFormulaCellValue(cell);\r
+                       case HSSFCell.CELL_TYPE_NUMERIC:\r
+                               return new CellValue(cell.getNumericCellValue());\r
+                       case HSSFCell.CELL_TYPE_STRING:\r
+                               return new CellValue(cell.getRichStringCellValue().getString());\r
+               }\r
+               throw new IllegalStateException("Bad cell type (" + cell.getCellType() + ")");\r
+       }\r
+\r
+\r
+       /**\r
+        * If cell contains formula, it evaluates the formula,\r
+        *  and saves the result of the formula. The cell\r
+        *  remains as a formula cell.\r
+        * Else if cell does not contain formula, this method leaves\r
+        *  the cell unchanged.\r
+        * Note that the type of the formula result is returned,\r
+        *  so you know what kind of value is also stored with\r
+        *  the formula.\r
+        * <pre>\r
+        * int evaluatedCellType = evaluator.evaluateFormulaCell(cell);\r
+        * </pre>\r
+        * Be aware that your cell will hold both the formula,\r
+        *  and the result. If you want the cell replaced with\r
+        *  the result of the formula, use {@link #evaluateInCell(HSSFCell)}\r
+        * @param cell The cell to evaluate\r
+        * @return The type of the formula result (the cell's type remains as HSSFCell.CELL_TYPE_FORMULA however)\r
+        */\r
+       public int evaluateFormulaCell(HSSFCell cell) {\r
+               if (cell == null || cell.getCellType() != HSSFCell.CELL_TYPE_FORMULA) {\r
+                       return -1;\r
+               }\r
+               CellValue cv = evaluateFormulaCellValue(cell);\r
+               // cell remains a formula cell, but the cached value is changed\r
+               setCellValue(cell, cv);\r
+               return cv.getCellType();\r
+       }\r
+\r
+       /**\r
+        * If cell contains formula, it evaluates the formula, and\r
+        *  puts the formula result back into the cell, in place\r
+        *  of the old formula.\r
+        * Else if cell does not contain formula, this method leaves\r
+        *  the cell unchanged.\r
+        * Note that the same instance of HSSFCell is returned to\r
+        * allow chained calls like:\r
+        * <pre>\r
+        * int evaluatedCellType = evaluator.evaluateInCell(cell).getCellType();\r
+        * </pre>\r
+        * Be aware that your cell value will be changed to hold the\r
+        *  result of the formula. If you simply want the formula\r
+        *  value computed for you, use {@link #evaluateFormulaCell(HSSFCell)}\r
+        * @param cell\r
+        */\r
+       public HSSFCell evaluateInCell(HSSFCell cell) {\r
+               if (cell == null) {\r
+                       return null;\r
+               }\r
+               if (cell.getCellType() == HSSFCell.CELL_TYPE_FORMULA) {\r
+                       CellValue cv = evaluateFormulaCellValue(cell);\r
+                       setCellType(cell, cv); // cell will no longer be a formula cell\r
+                       setCellValue(cell, cv);\r
+               }\r
+               return cell;\r
+       }\r
+       private static void setCellType(HSSFCell cell, CellValue cv) {\r
+               int cellType = cv.getCellType();\r
+               switch (cellType) {\r
+                       case HSSFCell.CELL_TYPE_BOOLEAN:\r
+                       case HSSFCell.CELL_TYPE_ERROR:\r
+                       case HSSFCell.CELL_TYPE_NUMERIC:\r
+                       case HSSFCell.CELL_TYPE_STRING:\r
+                               cell.setCellType(cellType);\r
+                               return;\r
+                       case HSSFCell.CELL_TYPE_BLANK:\r
+                               // never happens - blanks eventually get translated to zero\r
+                       case HSSFCell.CELL_TYPE_FORMULA:\r
+                               // this will never happen, we have already evaluated the formula\r
+               }\r
+               throw new IllegalStateException("Unexpected cell value type (" + cellType + ")");\r
+       }\r
+\r
+       private static void setCellValue(HSSFCell cell, CellValue cv) {\r
+               int cellType = cv.getCellType();\r
+               switch (cellType) {\r
+                       case HSSFCell.CELL_TYPE_BOOLEAN:\r
+                               cell.setCellValue(cv.getBooleanValue());\r
+                               break;\r
+                       case HSSFCell.CELL_TYPE_ERROR:\r
+                               cell.setCellErrorValue(cv.getErrorValue());\r
+                               break;\r
+                       case HSSFCell.CELL_TYPE_NUMERIC:\r
+                               cell.setCellValue(cv.getNumberValue());\r
+                               break;\r
+                       case HSSFCell.CELL_TYPE_STRING:\r
+                               cell.setCellValue(cv.getRichTextStringValue());\r
+                               break;\r
+                       case HSSFCell.CELL_TYPE_BLANK:\r
+                               // never happens - blanks eventually get translated to zero\r
+                       case HSSFCell.CELL_TYPE_FORMULA:\r
+                               // this will never happen, we have already evaluated the formula\r
+                       default:\r
+                               throw new IllegalStateException("Unexpected cell value type (" + cellType + ")");\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Loops over all cells in all sheets of the supplied\r
+        *  workbook.\r
+        * For cells that contain formulas, their formulas are\r
+        *  evaluated, and the results are saved. These cells\r
+        *  remain as formula cells.\r
+        * For cells that do not contain formulas, no changes\r
+        *  are made.\r
+        * This is a helpful wrapper around looping over all\r
+        *  cells, and calling evaluateFormulaCell on each one.\r
+        */\r
+       public static void evaluateAllFormulaCells(HSSFWorkbook wb) {\r
+               HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(wb);\r
+               for(int i=0; i<wb.getNumberOfSheets(); i++) {\r
+                       HSSFSheet sheet = wb.getSheetAt(i);\r
+\r
+                       for (Iterator rit = sheet.rowIterator(); rit.hasNext();) {\r
+                               HSSFRow r = (HSSFRow)rit.next();\r
+\r
+                               for (Iterator cit = r.cellIterator(); cit.hasNext();) {\r
+                                       HSSFCell c = (HSSFCell)cit.next();\r
+                                       if (c.getCellType() == HSSFCell.CELL_TYPE_FORMULA)\r
+                                               evaluator.evaluateFormulaCell(c);\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Returns a CellValue wrapper around the supplied ValueEval instance.\r
+        * @param eval\r
+        */\r
+       private CellValue evaluateFormulaCellValue(HSSFCell cell) {\r
+               ValueEval eval = _bookEvaluator.evaluate(cell);\r
+               if (eval instanceof NumberEval) {\r
+                       NumberEval ne = (NumberEval) eval;\r
+                       return new CellValue(ne.getNumberValue());\r
+               }\r
+               if (eval instanceof BoolEval) {\r
+                       BoolEval be = (BoolEval) eval;\r
+                       return CellValue.valueOf(be.getBooleanValue());\r
+               }\r
+               if (eval instanceof StringEval) {\r
+                       StringEval ne = (StringEval) eval;\r
+                       return new CellValue(ne.getStringValue());\r
+               }\r
+               if (eval instanceof ErrorEval) {\r
+                       return CellValue.getError(((ErrorEval)eval).getErrorCode());\r
+               }\r
+               throw new RuntimeException("Unexpected eval class (" + eval.getClass().getName() + ")");\r
+       }\r
+\r
+       /**\r
+        * Mimics the 'data view' of a cell. This allows formula evaluator\r
+        * to return a CellValue instead of precasting the value to String\r
+        * or Number or boolean type.\r
+        * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;\r
+        */\r
+       public static final class CellValue {\r
+               public static final CellValue TRUE = new CellValue(HSSFCell.CELL_TYPE_BOOLEAN, 0.0, true,  null, 0);\r
+               public static final CellValue FALSE= new CellValue(HSSFCell.CELL_TYPE_BOOLEAN, 0.0, false, null, 0);\r
+\r
+               private final int _cellType;\r
+               private final double _numberValue;\r
+               private final boolean _booleanValue;\r
+               private final String _textValue;\r
+               private final int _errorCode;\r
+\r
+               private CellValue(int cellType, double numberValue, boolean booleanValue,\r
+                               String textValue, int errorCode) {\r
+                       _cellType = cellType;\r
+                       _numberValue = numberValue;\r
+                       _booleanValue = booleanValue;\r
+                       _textValue = textValue;\r
+                       _errorCode = errorCode;\r
+               }\r
+\r
+\r
+               /* package*/ CellValue(double numberValue) {\r
+                       this(HSSFCell.CELL_TYPE_NUMERIC, numberValue, false, null, 0);\r
+               }\r
+               /* package*/ static CellValue valueOf(boolean booleanValue) {\r
+                       return booleanValue ? TRUE : FALSE;\r
+               }\r
+               /* package*/ CellValue(String stringValue) {\r
+                       this(HSSFCell.CELL_TYPE_STRING, 0.0, false, stringValue, 0);\r
+               }\r
+               /* package*/ static CellValue getError(int errorCode) {\r
+                       return new CellValue(HSSFCell.CELL_TYPE_ERROR, 0.0, false, null, errorCode);\r
+               }\r
+\r
+\r
+               /**\r
+                * @return Returns the booleanValue.\r
+                */\r
+               public boolean getBooleanValue() {\r
+                       return _booleanValue;\r
+               }\r
+               /**\r
+                * @return Returns the numberValue.\r
+                */\r
+               public double getNumberValue() {\r
+                       return _numberValue;\r
+               }\r
+               /**\r
+                * @return Returns the stringValue.\r
+                */\r
+               public String getStringValue() {\r
+                       return _textValue;\r
+               }\r
+               /**\r
+                * @return Returns the cellType.\r
+                */\r
+               public int getCellType() {\r
+                       return _cellType;\r
+               }\r
+               /**\r
+                * @return Returns the errorValue.\r
+                */\r
+               public byte getErrorValue() {\r
+                       return (byte) _errorCode;\r
+               }\r
+               /**\r
+                * @return Returns the richTextStringValue.\r
+                * @deprecated (Sep 2008) Text formatting is lost during formula evaluation.  Use {@link #getStringValue()}\r
+                */\r
+               public HSSFRichTextString getRichTextStringValue() {\r
+                       return new HSSFRichTextString(_textValue);\r
+               }\r
+               public String toString() {\r
+                       StringBuffer sb = new StringBuffer(64);\r
+                       sb.append(getClass().getName()).append(" [");\r
+                       sb.append(formatAsString());\r
+                       sb.append("]");\r
+                       return sb.toString();\r
+               }\r
+\r
+               public String formatAsString() {\r
+                       switch (_cellType) {\r
+                               case HSSFCell.CELL_TYPE_NUMERIC:\r
+                                       return String.valueOf(_numberValue);\r
+                               case HSSFCell.CELL_TYPE_STRING:\r
+                                       return '"' + _textValue + '"';\r
+                               case HSSFCell.CELL_TYPE_BOOLEAN:\r
+                                       return _booleanValue ? "TRUE" : "FALSE";\r
+                               case HSSFCell.CELL_TYPE_ERROR:\r
+                                       return ErrorEval.getText(_errorCode);\r
+                       }\r
+                       return "<error unexpected cell type " + _cellType + ">";\r
+               }\r
+       }\r
+}\r
index 8c264e4a597f468b7a849c343062cdcce3292f38..c24fa0508454f75af3d109a01a8dfc670ecf1898 100644 (file)
@@ -95,7 +95,7 @@ public final class HSSFName {
      */
     private void setSheetName(String sheetName){
         int sheetNumber = _book.getSheetIndex(sheetName);
-        short externSheetNumber = _book.getExternalSheetIndex(sheetNumber);
+        short externSheetNumber = _book.getWorkbook().checkExternSheet(sheetNumber);
         _definedNameRec.setExternSheetNumber(externSheetNumber);
     }
 
index a12aff5e127bd45c230c69ecf11709d454a78338..25e24426a88bea98311e7d716358d4a5fe842b4c 100644 (file)
@@ -661,52 +661,6 @@ public class HSSFWorkbook extends POIDocument
         return -1;
     }
 
-    /* package */ int findSheetIndex(Sheet sheet) {
-        for(int i=0; i<_sheets.size(); i++) {
-            HSSFSheet hSheet = (HSSFSheet) _sheets.get(i);
-            if(hSheet.getSheet() == sheet) {
-                return i;
-            }
-        }
-        throw new IllegalArgumentException("Specified sheet not found in this workbook");
-    }
-
-    /**
-     * Returns the external sheet index of the sheet
-     *  with the given internal index, creating one
-     *  if needed.
-     * Used by some of the more obscure formula and
-     *  named range things.
-     * @deprecated for POI internal use only (formula parsing).  This method is likely to
-     * be removed in future versions of POI.
-     */
-    public short getExternalSheetIndex(int internalSheetIndex) {
-        return workbook.checkExternSheet(internalSheetIndex);
-    }
-    /**
-     * @deprecated for POI internal use only (formula rendering).  This method is likely to
-     * be removed in future versions of POI.
-     */
-    public String findSheetNameFromExternSheet(int externSheetIndex){
-        // TODO - don't expose internal ugliness like externSheet indexes to the user model API
-        return workbook.findSheetNameFromExternSheet(externSheetIndex);
-    }
-    /**
-     * @deprecated for POI internal use only (formula rendering).  This method is likely to
-     * be removed in future versions of POI.
-     * 
-     * @param refIndex Index to REF entry in EXTERNSHEET record in the Link Table
-     * @param definedNameIndex zero-based to DEFINEDNAME or EXTERNALNAME record
-     * @return the string representation of the defined or external name
-     */
-    public String resolveNameXText(int refIndex, int definedNameIndex) {
-        // TODO - make this less cryptic / move elsewhere
-        return workbook.resolveNameXText(refIndex, definedNameIndex);
-    }
-
-
-
-
     /**
      * create an HSSFSheet for this HSSFWorkbook, adds it to the sheets and returns
      * the high level representation.  Use this to create new sheets.
@@ -750,7 +704,7 @@ public class HSSFWorkbook extends POIDocument
         if (filterDbNameIndex >=0) {
             NameRecord origNameRecord = workbook.getNameRecord(filterDbNameIndex);
             // copy original formula but adjust 3D refs to the new external sheet index
-            int newExtSheetIx = getExternalSheetIndex(newSheetIndex);
+            int newExtSheetIx = workbook.checkExternSheet(newSheetIndex);
             Ptg[] ptgs = origNameRecord.getNameDefinition();
             for (int i=0; i< ptgs.length; i++) {
                 Ptg ptg = ptgs[i];
diff --git a/src/java/org/apache/poi/hssf/usermodel/LazyAreaEval.java b/src/java/org/apache/poi/hssf/usermodel/LazyAreaEval.java
deleted file mode 100644 (file)
index 0e1d1fd..0000000
+++ /dev/null
@@ -1,79 +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 org.apache.poi.hssf.record.formula.AreaI;
-import org.apache.poi.hssf.record.formula.AreaI.OffsetArea;
-import org.apache.poi.hssf.record.formula.eval.AreaEval;
-import org.apache.poi.hssf.record.formula.eval.AreaEvalBase;
-import org.apache.poi.hssf.record.formula.eval.BlankEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-import org.apache.poi.hssf.util.CellReference;
-
-/**
- *
- * @author Josh Micich 
- */
-final class LazyAreaEval extends AreaEvalBase {
-
-       private final HSSFSheet _sheet;
-       private HSSFFormulaEvaluator _evaluator;
-
-       public LazyAreaEval(AreaI ptg, HSSFSheet sheet, HSSFFormulaEvaluator evaluator) {
-               super(ptg);
-               _sheet = sheet;
-               _evaluator = evaluator;
-       }
-
-       public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) { 
-               
-               int rowIx = (relativeRowIndex + getFirstRow() ) & 0xFFFF;
-               int colIx = (relativeColumnIndex + getFirstColumn() ) & 0x00FF;
-               
-               HSSFRow row = _sheet.getRow(rowIx);
-               if (row == null) {
-                       return BlankEval.INSTANCE;
-               }
-               HSSFCell cell = row.getCell(colIx);
-               if (cell == null) {
-                       return BlankEval.INSTANCE;
-               }
-               return _evaluator.getEvalForCell(cell);
-       }
-
-       public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) {
-               AreaI area = new OffsetArea(getFirstRow(), getFirstColumn(),
-                               relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);
-
-               return new LazyAreaEval(area, _sheet, _evaluator);
-       }
-       public String toString() {
-               CellReference crA = new CellReference(getFirstRow(), getFirstColumn());
-               CellReference crB = new CellReference(getLastRow(), getLastColumn());
-               StringBuffer sb = new StringBuffer();
-               sb.append(getClass().getName()).append("[");
-               String sheetName = _evaluator.getSheetName(_sheet);
-               sb.append(sheetName);
-               sb.append('!');
-               sb.append(crA.formatAsString());
-               sb.append(':');
-               sb.append(crB.formatAsString());
-               sb.append("]");
-               return sb.toString();
-       }
-}
diff --git a/src/java/org/apache/poi/hssf/usermodel/LazyRefEval.java b/src/java/org/apache/poi/hssf/usermodel/LazyRefEval.java
deleted file mode 100644 (file)
index d26ac59..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/* ====================================================================\r
-   Licensed to the Apache Software Foundation (ASF) under one or more\r
-   contributor license agreements.  See the NOTICE file distributed with\r
-   this work for additional information regarding copyright ownership.\r
-   The ASF licenses this file to You under the Apache License, Version 2.0\r
-   (the "License"); you may not use this file except in compliance with\r
-   the License.  You may obtain a copy of the License at\r
-\r
-       http://www.apache.org/licenses/LICENSE-2.0\r
-\r
-   Unless required by applicable law or agreed to in writing, software\r
-   distributed under the License is distributed on an "AS IS" BASIS,\r
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
-   See the License for the specific language governing permissions and\r
-   limitations under the License.\r
-==================================================================== */\r
-\r
-package org.apache.poi.hssf.usermodel;\r
-\r
-import org.apache.poi.hssf.record.formula.AreaI;\r
-import org.apache.poi.hssf.record.formula.Ref3DPtg;\r
-import org.apache.poi.hssf.record.formula.RefPtg;\r
-import org.apache.poi.hssf.record.formula.AreaI.OffsetArea;\r
-import org.apache.poi.hssf.record.formula.eval.AreaEval;\r
-import org.apache.poi.hssf.record.formula.eval.BlankEval;\r
-import org.apache.poi.hssf.record.formula.eval.RefEvalBase;\r
-import org.apache.poi.hssf.record.formula.eval.ValueEval;\r
-import org.apache.poi.hssf.util.CellReference;\r
-\r
-/**\r
-*\r
-* @author Josh Micich \r
-*/\r
-final class LazyRefEval extends RefEvalBase {\r
-\r
-       private final HSSFSheet _sheet;\r
-       private final HSSFFormulaEvaluator _evaluator;\r
-\r
-\r
-       public LazyRefEval(RefPtg ptg, HSSFSheet sheet, HSSFFormulaEvaluator evaluator) {\r
-               super(ptg.getRow(), ptg.getColumn());\r
-               _sheet = sheet;\r
-               _evaluator = evaluator;\r
-       }\r
-       public LazyRefEval(Ref3DPtg ptg, HSSFSheet sheet, HSSFFormulaEvaluator evaluator) {\r
-               super(ptg.getRow(), ptg.getColumn());\r
-               _sheet = sheet;\r
-               _evaluator = evaluator;\r
-       }\r
-\r
-       public ValueEval getInnerValueEval() {\r
-               int rowIx = getRow();\r
-               int colIx = getColumn();\r
-               \r
-               HSSFRow row = _sheet.getRow(rowIx);\r
-               if (row == null) {\r
-                       return BlankEval.INSTANCE;\r
-               }\r
-               HSSFCell cell = row.getCell(colIx);\r
-               if (cell == null) {\r
-                       return BlankEval.INSTANCE;\r
-               }\r
-               return _evaluator.getEvalForCell(cell);\r
-       }\r
-       \r
-       public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) {\r
-               \r
-               AreaI area = new OffsetArea(getRow(), getColumn(),\r
-                               relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);\r
-\r
-               return new LazyAreaEval(area, _sheet, _evaluator);\r
-       }\r
-       \r
-       public String toString() {\r
-               CellReference cr = new CellReference(getRow(), getColumn());\r
-               StringBuffer sb = new StringBuffer();\r
-               sb.append(getClass().getName()).append("[");\r
-               String sheetName = _evaluator.getSheetName(_sheet);\r
-               sb.append(sheetName);\r
-               sb.append('!');\r
-               sb.append(cr.formatAsString());\r
-               sb.append("]");\r
-               return sb.toString();\r
-       }\r
-}\r
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 21dc067..0000000
+++ /dev/null
@@ -1,184 +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 };
-       // TODO - use singleton instances directly instead of reflection
-       private static final Map _constructorsByPtgClass = initialiseConstructorsMap();
-       private static final Map _instancesByPtgClass = initialiseInstancesMap();
-       
-       private OperationEvaluatorFactory() {
-               // no instances of this class
-       }
-       
-       private static Map initialiseConstructorsMap() {
-               Map m = new HashMap(32);
-               add(m, ConcatPtg.class, ConcatEval.class);
-               add(m, FuncPtg.class, FuncVarEval.class);
-               add(m, FuncVarPtg.class, FuncVarEval.class);
-               return m;
-       }
-       private static Map initialiseInstancesMap() {
-               Map m = new HashMap(32);
-               add(m, EqualPtg.class, EqualEval.instance);
-               add(m, GreaterEqualPtg.class, GreaterEqualEval.instance);
-               add(m, GreaterThanPtg.class, GreaterThanEval.instance);
-               add(m, LessEqualPtg.class, LessEqualEval.instance);
-               add(m, LessThanPtg.class, LessThanEval.instance);
-               add(m, NotEqualPtg.class, NotEqualEval.instance);
-
-               add(m, AddPtg.class, AddEval.instance);
-               add(m, DividePtg.class, DivideEval.instance);
-               add(m, MultiplyPtg.class, MultiplyEval.instance);
-               add(m, PercentPtg.class, PercentEval.instance);
-               add(m, PowerPtg.class, PowerEval.instance);
-               add(m, SubtractPtg.class, SubtractEval.instance);
-               add(m, UnaryMinusPtg.class, UnaryMinusEval.instance);
-               add(m, UnaryPlusPtg.class, UnaryPlusEval.instance);
-               return m;
-       }
-
-       private static void add(Map m, Class ptgClass, OperationEval evalInstance) {
-               if(!Ptg.class.isAssignableFrom(ptgClass)) {
-                       throw new IllegalArgumentException("Expected Ptg subclass");
-               }
-               m.put(ptgClass, evalInstance);
-       }
-
-       private static void add(Map m, Class ptgClass, Class evalClass) {
-               // perform some validation now, to keep later exception handlers simple
-               if(!Ptg.class.isAssignableFrom(ptgClass)) {
-                       throw new IllegalArgumentException("Expected Ptg subclass");
-               }
-               
-               if(!OperationEval.class.isAssignableFrom(evalClass)) {
-                       throw new IllegalArgumentException("Expected OperationEval subclass");
-               }
-               if (!Modifier.isPublic(evalClass.getModifiers())) {
-                       throw new RuntimeException("Eval class must be public");
-               }
-               if (Modifier.isAbstract(evalClass.getModifiers())) {
-                       throw new RuntimeException("Eval class must not be abstract");
-               }
-               
-               Constructor constructor;
-               try {
-                       constructor = evalClass.getDeclaredConstructor(OPERATION_CONSTRUCTOR_CLASS_ARRAY);
-               } catch (NoSuchMethodException e) {
-                       throw new RuntimeException("Missing constructor");
-               }
-               if (!Modifier.isPublic(constructor.getModifiers())) {
-                       throw new RuntimeException("Eval constructor must be public");
-               }
-               m.put(ptgClass, constructor);
-       }
-
-       /**
-        * returns the OperationEval concrete impl instance corresponding
-        * to the supplied operationPtg
-        */
-       public static OperationEval create(OperationPtg ptg) {
-               if(ptg == null) {
-                       throw new IllegalArgumentException("ptg must not be null");
-               }
-               Object result;
-               
-               Class ptgClass = ptg.getClass();
-               
-               result = _instancesByPtgClass.get(ptgClass);
-               if (result != null) {
-                       return (OperationEval) result;
-               }
-               
-               
-               Constructor constructor = (Constructor) _constructorsByPtgClass.get(ptgClass);
-               if(constructor == null) {
-                       if(ptgClass == ExpPtg.class) {
-                               // ExpPtg is used for array formulas and shared formulas.
-                               // it is currently unsupported, and may not even get implemented here
-                               throw new RuntimeException("ExpPtg currently not supported");
-                       }
-                       throw new RuntimeException("Unexpected operation ptg class (" + ptgClass.getName() + ")");
-               }
-               
-               Object[] initargs = { ptg };
-               try {
-                       result = constructor.newInstance(initargs);
-               } catch (IllegalArgumentException e) {
-                       throw new RuntimeException(e);
-               } catch (InstantiationException e) {
-                       throw new RuntimeException(e);
-               } catch (IllegalAccessException e) {
-                       throw new RuntimeException(e);
-               } catch (InvocationTargetException e) {
-                       throw new RuntimeException(e);
-               }
-               return (OperationEval) result;
-       }
-}
diff --git a/src/java/org/apache/poi/ss/formula/CellEvaluationFrame.java b/src/java/org/apache/poi/ss/formula/CellEvaluationFrame.java
new file mode 100644 (file)
index 0000000..b6f542a
--- /dev/null
@@ -0,0 +1,71 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.ss.formula;\r
+\r
+/**\r
+ * Stores the parameters that identify the evaluation of one cell.<br/>\r
+ */\r
+final class CellEvaluationFrame {\r
+\r
+       private final int _sheetIndex;\r
+       private final int _srcRowNum;\r
+       private final int _srcColNum;\r
+       private final int _hashCode;\r
+\r
+       public CellEvaluationFrame(int sheetIndex, int srcRowNum, int srcColNum) {\r
+               if (sheetIndex < 0) {\r
+                       throw new IllegalArgumentException("sheetIndex must not be negative");\r
+               }\r
+               _sheetIndex = sheetIndex;\r
+               _srcRowNum = srcRowNum;\r
+               _srcColNum = srcColNum;\r
+               _hashCode = sheetIndex + 17 * (srcRowNum + 17 * srcColNum);\r
+       }\r
+\r
+       public boolean equals(Object obj) {\r
+               CellEvaluationFrame other = (CellEvaluationFrame) obj;\r
+               if (_sheetIndex != other._sheetIndex) {\r
+                       return false;\r
+               }\r
+               if (_srcRowNum != other._srcRowNum) {\r
+                       return false;\r
+               }\r
+               if (_srcColNum != other._srcColNum) {\r
+                       return false;\r
+               }\r
+               return true;\r
+       }\r
+       public int hashCode() {\r
+               return _hashCode;\r
+       }\r
+\r
+       /**\r
+        * @return human readable string for debug purposes\r
+        */\r
+       public String formatAsString() {\r
+               return "R=" + _srcRowNum + " C=" + _srcColNum + " ShIx=" + _sheetIndex;\r
+       }\r
+\r
+       public String toString() {\r
+               StringBuffer sb = new StringBuffer(64);\r
+               sb.append(getClass().getName()).append(" [");\r
+               sb.append(formatAsString());\r
+               sb.append("]");\r
+               return sb.toString();\r
+       }\r
+}
\ No newline at end of file
diff --git a/src/java/org/apache/poi/ss/formula/CellEvaluator.java b/src/java/org/apache/poi/ss/formula/CellEvaluator.java
new file mode 100644 (file)
index 0000000..22846c2
--- /dev/null
@@ -0,0 +1,54 @@
+package org.apache.poi.ss.formula;\r
+\r
+import org.apache.poi.hssf.record.formula.eval.BlankEval;\r
+import org.apache.poi.hssf.record.formula.eval.BoolEval;\r
+import org.apache.poi.hssf.record.formula.eval.ErrorEval;\r
+import org.apache.poi.hssf.record.formula.eval.NumberEval;\r
+import org.apache.poi.hssf.record.formula.eval.StringEval;\r
+import org.apache.poi.hssf.record.formula.eval.ValueEval;\r
+import org.apache.poi.hssf.usermodel.HSSFCell;\r
+import org.apache.poi.hssf.usermodel.HSSFSheet;\r
+\r
+final class CellEvaluator {\r
+\r
+       private final WorkbookEvaluator _bookEvaluator;\r
+       private final EvaluationTracker _tracker;\r
+\r
+       public CellEvaluator(WorkbookEvaluator bookEvaluator, EvaluationTracker tracker) {\r
+               _bookEvaluator = bookEvaluator;\r
+               _tracker = tracker;\r
+       }\r
+\r
+          /**\r
+     * Given a cell, find its type and from that create an appropriate ValueEval\r
+     * impl instance and return that. Since the cell could be an external\r
+     * reference, we need the sheet that this belongs to.\r
+     * Non existent cells are treated as empty.\r
+     */\r
+       public ValueEval getEvalForCell(HSSFCell cell) {\r
+\r
+        if (cell == null) {\r
+            return BlankEval.INSTANCE;\r
+        }\r
+        switch (cell.getCellType()) {\r
+            case HSSFCell.CELL_TYPE_NUMERIC:\r
+                return new NumberEval(cell.getNumericCellValue());\r
+            case HSSFCell.CELL_TYPE_STRING:\r
+                return new StringEval(cell.getRichStringCellValue().getString());\r
+            case HSSFCell.CELL_TYPE_FORMULA:\r
+                return _bookEvaluator.internalEvaluate(cell, _tracker);\r
+            case HSSFCell.CELL_TYPE_BOOLEAN:\r
+                return BoolEval.valueOf(cell.getBooleanCellValue());\r
+            case HSSFCell.CELL_TYPE_BLANK:\r
+                return BlankEval.INSTANCE;\r
+            case HSSFCell.CELL_TYPE_ERROR:\r
+                return ErrorEval.valueOf(cell.getErrorCellValue());\r
+        }\r
+        throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");\r
+    }\r
+\r
+       public String getSheetName(HSSFSheet sheet) {\r
+        return _bookEvaluator.getSheetName(sheet);\r
+       }\r
+\r
+}\r
diff --git a/src/java/org/apache/poi/ss/formula/EvaluationCache.java b/src/java/org/apache/poi/ss/formula/EvaluationCache.java
new file mode 100644 (file)
index 0000000..d78f4a7
--- /dev/null
@@ -0,0 +1,99 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.ss.formula;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.poi.hssf.record.formula.eval.ValueEval;
+import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
+
+/**
+ * Performance optimisation for {@link HSSFFormulaEvaluator}. This class stores previously
+ * calculated values of already visited cells, to avoid unnecessary re-calculation when the 
+ * same cells are referenced multiple times
+ * 
+ * 
+ * @author Josh Micich
+ */
+final class EvaluationCache {
+
+       private static final CellEvaluationFrame[] EMPTY_CEF_ARRAY = { };
+       private final Map _valuesByKey;
+       private final Map _consumingCellsByDest;
+
+       /* package */EvaluationCache() {
+               _valuesByKey = new HashMap();
+               _consumingCellsByDest = new HashMap();
+       }
+
+       public ValueEval getValue(int sheetIndex, int srcRowNum, int srcColNum) {
+               return getValue(new CellEvaluationFrame(sheetIndex, srcRowNum, srcColNum));
+       }
+
+       /* package */ ValueEval getValue(CellEvaluationFrame key) {
+               return (ValueEval) _valuesByKey.get(key);
+       }
+
+       public void setValue(int sheetIndex, int srcRowNum, int srcColNum, ValueEval value) {
+               setValue(new CellEvaluationFrame(sheetIndex, srcRowNum, srcColNum), value);
+       }
+
+       /* package */ void setValue(CellEvaluationFrame key, ValueEval value) {
+               if (_valuesByKey.containsKey(key)) {
+                       throw new RuntimeException("Already have cached value for this cell");
+               }
+               _valuesByKey.put(key, value);
+       }
+
+       /**
+        * Should be called whenever there are changes to input cells in the evaluated workbook.
+        */
+       public void clear() {
+               _valuesByKey.clear();
+       }
+
+       public void clearValue(int sheetIndex, int rowIndex, int columnIndex) {
+               clearValuesRecursive(new CellEvaluationFrame(sheetIndex, rowIndex, columnIndex));
+               
+       }
+
+       private void clearValuesRecursive(CellEvaluationFrame cef) {
+               CellEvaluationFrame[] consumingCells = getConsumingCells(cef);
+               for (int i = 0; i < consumingCells.length; i++) {
+                       clearValuesRecursive(consumingCells[i]);
+               }
+               _valuesByKey.remove(cef);
+               _consumingCellsByDest.remove(cef);
+       }
+
+       private CellEvaluationFrame[] getConsumingCells(CellEvaluationFrame cef) {
+               List temp = (List) _consumingCellsByDest.get(cef);
+               if (temp == null) {
+                       return EMPTY_CEF_ARRAY;
+               }
+               int nItems = temp.size();
+               if (temp.size() < 1) {
+                       return EMPTY_CEF_ARRAY;
+               }
+               CellEvaluationFrame[] result = new CellEvaluationFrame[nItems];
+               temp.toArray(result);
+               return result;
+       }
+}
diff --git a/src/java/org/apache/poi/ss/formula/EvaluationName.java b/src/java/org/apache/poi/ss/formula/EvaluationName.java
new file mode 100644 (file)
index 0000000..ae8624c
--- /dev/null
@@ -0,0 +1,41 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.ss.formula;\r
+\r
+import org.apache.poi.hssf.record.formula.NamePtg;\r
+import org.apache.poi.hssf.record.formula.Ptg;\r
+/**\r
+ * Abstracts a name record for formula evaluation.<br/>\r
+ * \r
+ * For POI internal use only\r
+ * \r
+ * @author Josh Micich\r
+ */\r
+public interface EvaluationName {\r
+\r
+       String getNameText();\r
+\r
+       boolean isFunctionName();\r
+\r
+       boolean hasFormula();\r
+\r
+       Ptg[] getNameDefinition();\r
+\r
+       boolean isRange();\r
+       NamePtg createPtg();\r
+}\r
diff --git a/src/java/org/apache/poi/ss/formula/EvaluationTracker.java b/src/java/org/apache/poi/ss/formula/EvaluationTracker.java
new file mode 100755 (executable)
index 0000000..20ab329
--- /dev/null
@@ -0,0 +1,106 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.ss.formula;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.poi.hssf.record.formula.eval.ErrorEval;
+import org.apache.poi.hssf.record.formula.eval.ValueEval;
+import org.apache.poi.hssf.usermodel.HSSFCell;
+
+/**
+ * Instances of this class keep track of multiple dependent cell evaluations due
+ * to recursive calls to {@link WorkbookEvaluator#evaluate(HSSFCell)}
+ * 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 EvaluationTracker {
+
+       private final List _evaluationFrames;
+       private final EvaluationCache _cache;
+
+       public EvaluationTracker(EvaluationCache cache) {
+               _cache = cache;
+               _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 ValueEval startEvaluate(int sheetIndex, int srcRowNum, int srcColNum) {
+               CellEvaluationFrame cef = new CellEvaluationFrame(sheetIndex, srcRowNum, srcColNum);
+               if (_evaluationFrames.contains(cef)) {
+                       return ErrorEval.CIRCULAR_REF_ERROR;
+               }
+               ValueEval result = _cache.getValue(cef);
+               if (result == null) {
+                       _evaluationFrames.add(cef);
+               }
+               return result;
+       }
+
+       /**
+        * 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.
+        * @param result 
+        */
+       public void endEvaluate(int sheetIndex, int srcRowNum, int srcColNum, ValueEval result) {
+               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(sheetIndex, 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);
+               
+               _cache.setValue(cefActual, result);
+       }
+}
diff --git a/src/java/org/apache/poi/ss/formula/EvaluationWorkbook.java b/src/java/org/apache/poi/ss/formula/EvaluationWorkbook.java
new file mode 100644 (file)
index 0000000..29083f7
--- /dev/null
@@ -0,0 +1,42 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.ss.formula;\r
+\r
+import org.apache.poi.hssf.record.formula.NamePtg;\r
+import org.apache.poi.hssf.record.formula.NameXPtg;\r
+import org.apache.poi.hssf.record.formula.Ptg;\r
+import org.apache.poi.hssf.usermodel.HSSFCell;\r
+import org.apache.poi.hssf.usermodel.HSSFSheet;\r
+/**\r
+ * Abstracts a workbook for the purpose of formula evaluation.<br/>\r
+ * \r
+ * For POI internal use only\r
+ * \r
+ * @author Josh Micich\r
+ */\r
+public interface EvaluationWorkbook {\r
+       String getSheetName(int sheetIndex);\r
+       int getSheetIndex(HSSFSheet sheet);\r
+\r
+       HSSFSheet getSheet(int sheetIndex);\r
+\r
+       HSSFSheet getSheetByExternSheetIndex(int externSheetIndex);\r
+       EvaluationName getName(NamePtg namePtg);\r
+       String resolveNameXText(NameXPtg ptg);\r
+       Ptg[] getFormulaTokens(HSSFCell cell);\r
+}\r
diff --git a/src/java/org/apache/poi/ss/formula/FormulaParser.java b/src/java/org/apache/poi/ss/formula/FormulaParser.java
new file mode 100644 (file)
index 0000000..df2f8c2
--- /dev/null
@@ -0,0 +1,976 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.ss.formula;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.poi.hssf.record.UnicodeString;
+import org.apache.poi.hssf.record.constant.ErrorConstant;
+import org.apache.poi.hssf.record.formula.AbstractFunctionPtg;
+import org.apache.poi.hssf.record.formula.AddPtg;
+import org.apache.poi.hssf.record.formula.Area3DPtg;
+import org.apache.poi.hssf.record.formula.AreaPtg;
+import org.apache.poi.hssf.record.formula.ArrayPtg;
+import org.apache.poi.hssf.record.formula.BoolPtg;
+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.ErrPtg;
+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.IntPtg;
+import org.apache.poi.hssf.record.formula.LessEqualPtg;
+import org.apache.poi.hssf.record.formula.LessThanPtg;
+import org.apache.poi.hssf.record.formula.MissingArgPtg;
+import org.apache.poi.hssf.record.formula.MultiplyPtg;
+import org.apache.poi.hssf.record.formula.NamePtg;
+import org.apache.poi.hssf.record.formula.NameXPtg;
+import org.apache.poi.hssf.record.formula.NotEqualPtg;
+import org.apache.poi.hssf.record.formula.NumberPtg;
+import org.apache.poi.hssf.record.formula.ParenthesisPtg;
+import org.apache.poi.hssf.record.formula.PercentPtg;
+import org.apache.poi.hssf.record.formula.PowerPtg;
+import org.apache.poi.hssf.record.formula.Ptg;
+import org.apache.poi.hssf.record.formula.Ref3DPtg;
+import org.apache.poi.hssf.record.formula.RefPtg;
+import org.apache.poi.hssf.record.formula.StringPtg;
+import org.apache.poi.hssf.record.formula.SubtractPtg;
+import org.apache.poi.hssf.record.formula.UnaryMinusPtg;
+import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
+import org.apache.poi.hssf.record.formula.function.FunctionMetadata;
+import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
+import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
+import org.apache.poi.hssf.util.AreaReference;
+import org.apache.poi.hssf.util.CellReference;
+import org.apache.poi.hssf.util.CellReference.NameType;
+
+/**
+ * This class parses a formula string into a List of tokens in RPN order.
+ * Inspired by
+ *           Lets Build a Compiler, by Jack Crenshaw
+ * BNF for the formula expression is :
+ * <expression> ::= <term> [<addop> <term>]*
+ * <term> ::= <factor>  [ <mulop> <factor> ]*
+ * <factor> ::= <number> | (<expression>) | <cellRef> | <function>
+ * <function> ::= <functionName> ([expression [, expression]*])
+ *
+ *  @author Avik Sengupta <avik at apache dot org>
+ *  @author Andrew C. oliver (acoliver at apache dot org)
+ *  @author Eric Ladner (eladner at goldinc dot com)
+ *  @author Cameron Riley (criley at ekmail.com)
+ *  @author Peter M. Murray (pete at quantrix dot com)
+ *  @author Pavel Krupets (pkrupets at palmtreebusiness dot com)
+ *  @author Josh Micich
+ */
+public final class FormulaParser {
+
+    /**
+     * Specific exception thrown when a supplied formula does not parse properly.<br/>
+     * Primarily used by test cases when testing for specific parsing exceptions.</p>
+     *
+     */
+    static final class FormulaParseException extends RuntimeException {
+        // This class was given package scope until it would become clear that it is useful to
+        // general client code.
+        public FormulaParseException(String msg) {
+            super(msg);
+        }
+    }
+
+
+    private final String formulaString;
+    private final int formulaLength;
+    private int pointer;
+
+    private ParseNode _rootNode;
+
+    private static char TAB = '\t';
+
+    /**
+     * Lookahead Character.
+     * gets value '\0' when the input string is exhausted
+     */
+    private char look;
+
+    private FormulaParsingWorkbook book;
+
+
+
+    /**
+     * Create the formula parser, with the string that is to be
+     *  parsed against the supplied workbook.
+     * A later call the parse() method to return ptg list in
+     *  rpn order, then call the getRPNPtg() to retrive the
+     *  parse results.
+     * This class is recommended only for single threaded use.
+     *
+     * If you only have a usermodel.HSSFWorkbook, and not a
+     *  model.Workbook, then use the convenience method on
+     *  usermodel.HSSFFormulaEvaluator
+     */
+    private FormulaParser(String formula, FormulaParsingWorkbook book){
+        formulaString = formula;
+        pointer=0;
+        this.book = book;
+        formulaLength = formulaString.length();
+    }
+
+    public static Ptg[] parse(String formula, FormulaParsingWorkbook book) {
+        return parse(formula, book, FormulaType.CELL);
+    }
+
+    public static Ptg[] parse(String formula, FormulaParsingWorkbook workbook, int formulaType) {
+        FormulaParser fp = new FormulaParser(formula, workbook);
+        fp.parse();
+        return fp.getRPNPtg(formulaType);
+    }
+
+    /** Read New Character From Input Stream */
+    private void GetChar() {
+        // Check to see if we've walked off the end of the string.
+        if (pointer > formulaLength) {
+            throw new RuntimeException("too far");
+        }
+        if (pointer < formulaLength) {
+            look=formulaString.charAt(pointer);
+        } else {
+            // Just return if so and reset 'look' to something to keep
+            // SkipWhitespace from spinning
+            look = (char)0;
+        }
+        pointer++;
+        //System.out.println("Got char: "+ look);
+    }
+
+    /** Report What Was Expected */
+    private RuntimeException expected(String s) {
+        String msg;
+
+        if (look == '=' && formulaString.substring(0, pointer-1).trim().length() < 1) {
+            msg = "The specified formula '" + formulaString
+                + "' starts with an equals sign which is not allowed.";
+        } else {
+            msg = "Parse error near char " + (pointer-1) + " '" + look + "'"
+                + " in specified formula '" + formulaString + "'. Expected "
+                + s;
+        }
+        return new FormulaParseException(msg);
+    }
+
+    /** Recognize an Alpha Character */
+    private boolean IsAlpha(char c) {
+        return Character.isLetter(c) || c == '$' || c=='_';
+    }
+
+    /** Recognize a Decimal Digit */
+    private boolean IsDigit(char c) {
+        return Character.isDigit(c);
+    }
+
+    /** Recognize an Alphanumeric */
+    private boolean  IsAlNum(char c) {
+        return  (IsAlpha(c) || IsDigit(c));
+    }
+
+    /** Recognize White Space */
+    private boolean IsWhite( char c) {
+        return  (c ==' ' || c== TAB);
+    }
+
+    /** Skip Over Leading White Space */
+    private void SkipWhite() {
+        while (IsWhite(look)) {
+            GetChar();
+        }
+    }
+
+    /**
+     *  Consumes the next input character if it is equal to the one specified otherwise throws an
+     *  unchecked exception. This method does <b>not</b> consume whitespace (before or after the
+     *  matched character).
+     */
+    private void Match(char x) {
+        if (look != x) {
+            throw expected("'" + x + "'");
+        }
+        GetChar();
+    }
+
+    /**
+     * Parses a sheet name, named range name, or simple cell reference.<br/>
+     * Note - identifiers in Excel can contain dots, so this method may return a String
+     * which may need to be converted to an area reference.  For example, this method
+     * may return a value like "A1..B2", in which case the caller must convert it to
+     * an area reference like "A1:B2"
+     */
+    private String parseIdentifier() {
+        StringBuffer Token = new StringBuffer();
+        if (!IsAlpha(look) && look != '\'') {
+            throw expected("Name");
+        }
+        if(look == '\'')
+        {
+            Match('\'');
+            boolean done = look == '\'';
+            while(!done)
+            {
+                Token.append(look);
+                GetChar();
+                if(look == '\'')
+                {
+                    Match('\'');
+                    done = look != '\'';
+                }
+            }
+        }
+        else
+        {
+            // allow for any sequence of dots and identifier chars
+            // special case of two consecutive dots is best treated in the calling code
+            while (IsAlNum(look) || look == '.') {
+                Token.append(look);
+                GetChar();
+            }
+        }
+        return Token.toString();
+    }
+
+    /** Get a Number */
+    private String GetNum() {
+        StringBuffer value = new StringBuffer();
+
+        while (IsDigit(this.look)){
+            value.append(this.look);
+            GetChar();
+        }
+        return value.length() == 0 ? null : value.toString();
+    }
+
+    private ParseNode parseFunctionReferenceOrName() {
+        String name = parseIdentifier();
+        if (look == '('){
+            //This is a function
+            return function(name);
+        }
+        return new ParseNode(parseNameOrReference(name));
+    }
+
+    private Ptg parseNameOrReference(String name) {
+
+        AreaReference areaRef = parseArea(name);
+        if (areaRef != null) {
+            // will happen if dots are used instead of colon
+            return new AreaPtg(areaRef.formatAsString());
+        }
+
+        if (look == ':' || look == '.') { // this is a AreaReference
+            GetChar();
+
+            while (look == '.') { // formulas can have . or .. or ... instead of :
+                GetChar();
+            }
+
+            String first = name;
+            String second = parseIdentifier();
+            return new AreaPtg(first+":"+second);
+        }
+
+        if (look == '!') {
+            Match('!');
+            String sheetName = name;
+            String first = parseIdentifier();
+            int externIdx = book.getExternalSheetIndex(sheetName);
+            areaRef = parseArea(name);
+            if (areaRef != null) {
+                // will happen if dots are used instead of colon
+                return new Area3DPtg(areaRef.formatAsString(), externIdx);
+            }
+            if (look == ':') {
+                Match(':');
+                String second=parseIdentifier();
+                if (look == '!') {
+                    //The sheet name was included in both of the areas. Only really
+                    //need it once
+                    Match('!');
+                    String third=parseIdentifier();
+
+                    if (!sheetName.equals(second))
+                        throw new RuntimeException("Unhandled double sheet reference.");
+
+                    return new Area3DPtg(first+":"+third,externIdx);
+                }
+                return new Area3DPtg(first+":"+second,externIdx);
+            }
+            return new Ref3DPtg(first, externIdx);
+        }
+        if (name.equalsIgnoreCase("TRUE") || name.equalsIgnoreCase("FALSE")) {
+            return new BoolPtg(name.toUpperCase());
+        }
+
+        // This can be either a cell ref or a named range
+        // Try to spot which it is
+        int nameType = CellReference.classifyCellReference(name);
+        if (nameType == NameType.CELL) {
+            return new RefPtg(name);
+        }
+        if (nameType != NameType.NAMED_RANGE) {
+            new FormulaParseException("Name '" + name
+                + "' does not look like a cell reference or named range");
+        }
+        EvaluationName evalName = book.getName(name);
+        if (evalName == null) {
+            throw new FormulaParseException("Specified named range '"
+                    + name + "' does not exist in the current workbook.");
+        }
+        if (evalName.isRange()) {
+            return evalName.createPtg();
+        }
+        throw new FormulaParseException("Specified name '"
+                    + name + "' is not a range as expected");
+    }
+
+    /**
+     * @param name an 'identifier' like string (i.e. contains alphanums, and dots)
+     * @return <code>null</code> if name cannot be split at a dot
+     */
+    private AreaReference parseArea(String name) {
+        int dotPos = name.indexOf('.');
+        if (dotPos < 0) {
+            return null;
+        }
+        int dotCount = 1;
+        while (dotCount<name.length() && name.charAt(dotPos+dotCount) == '.') {
+            dotCount++;
+            if (dotCount>3) {
+                // four or more consecutive dots does not convert to ':'
+                return null;
+            }
+        }
+        // This expression is only valid as an area ref, if the LHS and RHS of the dot(s) are both
+        // cell refs.  Otherwise, this expression must be a named range name
+        String partA = name.substring(0, dotPos);
+        if (!isValidCellReference(partA)) {
+            return null;
+        }
+        String partB = name.substring(dotPos+dotCount);
+        if (!isValidCellReference(partB)) {
+            return null;
+        }
+        CellReference topLeft = new CellReference(partA);
+        CellReference bottomRight = new CellReference(partB);
+        return new AreaReference(topLeft, bottomRight);
+    }
+
+    /**
+     * @return <code>true</code> if the specified name is a valid cell reference
+     */
+    private static boolean isValidCellReference(String str) {
+        return CellReference.classifyCellReference(str) == NameType.CELL;
+    }
+
+
+    /**
+     * Note - Excel function names are 'case aware but not case sensitive'.  This method may end
+     * up creating a defined name record in the workbook if the specified name is not an internal
+     * Excel function, and has not been encountered before.
+     *
+     * @param name case preserved function name (as it was entered/appeared in the formula).
+     */
+    private ParseNode function(String name) {
+        Ptg nameToken = null;
+        if(!AbstractFunctionPtg.isBuiltInFunctionName(name)) {
+            // user defined function
+            // in the token tree, the name is more or less the first argument
+
+            EvaluationName hName = book.getName(name);
+            if (hName == null) {
+
+                nameToken = book.getNameXPtg(name);
+                if (nameToken == null) {
+                    throw new FormulaParseException("Name '" + name
+                            + "' is completely unknown in the current workbook");
+                }
+            } else {
+                if (!hName.isFunctionName()) {
+                    throw new FormulaParseException("Attempt to use name '" + name
+                            + "' as a function, but defined name in workbook does not refer to a function");
+                }
+
+                // calls to user-defined functions within the workbook
+                // get a Name token which points to a defined name record
+                nameToken = hName.createPtg();
+            }
+        }
+
+        Match('(');
+        ParseNode[] args = Arguments();
+        Match(')');
+
+        return getFunction(name, nameToken, args);
+    }
+
+    /**
+     * Generates the variable function ptg for the formula.
+     * <p>
+     * For IF Formulas, additional PTGs are added to the tokens
+     * @param name a {@link NamePtg} or {@link NameXPtg} or <code>null</code>
+     * @param numArgs
+     * @return Ptg a null is returned if we're in an IF formula, it needs extreme manipulation and is handled in this function
+     */
+    private ParseNode getFunction(String name, Ptg namePtg, ParseNode[] args) {
+
+        FunctionMetadata fm = FunctionMetadataRegistry.getFunctionByName(name.toUpperCase());
+        int numArgs = args.length;
+        if(fm == null) {
+            if (namePtg == null) {
+                throw new IllegalStateException("NamePtg must be supplied for external functions");
+            }
+            // must be external function
+            ParseNode[] allArgs = new ParseNode[numArgs+1];
+            allArgs[0] = new ParseNode(namePtg);
+            System.arraycopy(args, 0, allArgs, 1, numArgs);
+            return new ParseNode(new FuncVarPtg(name, (byte)(numArgs+1)), allArgs);
+        }
+
+        if (namePtg != null) {
+            throw new IllegalStateException("NamePtg no applicable to internal functions");
+        }
+        boolean isVarArgs = !fm.hasFixedArgsLength();
+        int funcIx = fm.getIndex();
+        validateNumArgs(args.length, fm);
+
+        AbstractFunctionPtg retval;
+        if(isVarArgs) {
+            retval = new FuncVarPtg(name, (byte)numArgs);
+        } else {
+            retval = new FuncPtg(funcIx);
+        }
+        return new ParseNode(retval, args);
+    }
+
+    private void validateNumArgs(int numArgs, FunctionMetadata fm) {
+        if(numArgs < fm.getMinParams()) {
+            String msg = "Too few arguments to function '" + fm.getName() + "'. ";
+            if(fm.hasFixedArgsLength()) {
+                msg += "Expected " + fm.getMinParams();
+            } else {
+                msg += "At least " + fm.getMinParams() + " were expected";
+            }
+            msg += " but got " + numArgs + ".";
+            throw new FormulaParseException(msg);
+         }
+        if(numArgs > fm.getMaxParams()) {
+            String msg = "Too many arguments to function '" + fm.getName() + "'. ";
+            if(fm.hasFixedArgsLength()) {
+                msg += "Expected " + fm.getMaxParams();
+            } else {
+                msg += "At most " + fm.getMaxParams() + " were expected";
+            }
+            msg += " but got " + numArgs + ".";
+            throw new FormulaParseException(msg);
+       }
+    }
+
+    private static boolean isArgumentDelimiter(char ch) {
+        return ch ==  ',' || ch == ')';
+    }
+
+    /** get arguments to a function */
+    private ParseNode[] Arguments() {
+        //average 2 args per function
+        List temp = new ArrayList(2);
+        SkipWhite();
+        if(look == ')') {
+            return ParseNode.EMPTY_ARRAY;
+        }
+
+        boolean missedPrevArg = true;
+        int numArgs = 0;
+        while (true) {
+            SkipWhite();
+            if (isArgumentDelimiter(look)) {
+                if (missedPrevArg) {
+                    temp.add(new ParseNode(MissingArgPtg.instance));
+                    numArgs++;
+                }
+                if (look == ')') {
+                    break;
+                }
+                Match(',');
+                missedPrevArg = true;
+                continue;
+            }
+            temp.add(comparisonExpression());
+            numArgs++;
+            missedPrevArg = false;
+            SkipWhite();
+            if (!isArgumentDelimiter(look)) {
+                throw expected("',' or ')'");
+            }
+        }
+        ParseNode[] result = new ParseNode[temp.size()];
+        temp.toArray(result);
+        return result;
+    }
+
+   /** Parse and Translate a Math Factor  */
+    private ParseNode powerFactor() {
+        ParseNode result = percentFactor();
+        while(true) {
+            SkipWhite();
+            if(look != '^') {
+                return result;
+            }
+            Match('^');
+            ParseNode other = percentFactor();
+            result = new ParseNode(PowerPtg.instance, result, other);
+        }
+    }
+
+    private ParseNode percentFactor() {
+        ParseNode result = parseSimpleFactor();
+        while(true) {
+            SkipWhite();
+            if(look != '%') {
+                return result;
+            }
+            Match('%');
+            result = new ParseNode(PercentPtg.instance, result);
+        }
+    }
+
+
+    /**
+     * factors (without ^ or % )
+     */
+    private ParseNode parseSimpleFactor() {
+        SkipWhite();
+        switch(look) {
+            case '#':
+                return new ParseNode(ErrPtg.valueOf(parseErrorLiteral()));
+            case '-':
+                Match('-');
+                return new ParseNode(UnaryMinusPtg.instance, powerFactor());
+            case '+':
+                Match('+');
+                return new ParseNode(UnaryPlusPtg.instance, powerFactor());
+            case '(':
+                Match('(');
+                ParseNode inside = comparisonExpression();
+                Match(')');
+                return new ParseNode(ParenthesisPtg.instance, inside);
+            case '"':
+                return new ParseNode(new StringPtg(parseStringLiteral()));
+            case '{':
+                Match('{');
+                ParseNode arrayNode = parseArray();
+                Match('}');
+                return arrayNode;
+        }
+        if (IsAlpha(look) || look == '\''){
+            return parseFunctionReferenceOrName();
+        }
+        // else - assume number
+        return new ParseNode(parseNumber());
+    }
+
+
+    private ParseNode parseArray() {
+        List rowsData = new ArrayList();
+        while(true) {
+            Object[] singleRowData = parseArrayRow();
+            rowsData.add(singleRowData);
+            if (look == '}') {
+                break;
+            }
+            if (look != ';') {
+                throw expected("'}' or ';'");
+            }
+            Match(';');
+        }
+        int nRows = rowsData.size();
+        Object[][] values2d = new Object[nRows][];
+        rowsData.toArray(values2d);
+        int nColumns = values2d[0].length;
+        checkRowLengths(values2d, nColumns);
+
+        return new ParseNode(new ArrayPtg(values2d));
+    }
+    private void checkRowLengths(Object[][] values2d, int nColumns) {
+        for (int i = 0; i < values2d.length; i++) {
+            int rowLen = values2d[i].length;
+            if (rowLen != nColumns) {
+                throw new FormulaParseException("Array row " + i + " has length " + rowLen
+                        + " but row 0 has length " + nColumns);
+            }
+        }
+    }
+
+    private Object[] parseArrayRow() {
+        List temp = new ArrayList();
+        while (true) {
+            temp.add(parseArrayItem());
+            SkipWhite();
+            switch(look) {
+                case '}':
+                case ';':
+                    break;
+                case ',':
+                    Match(',');
+                    continue;
+                default:
+                    throw expected("'}' or ','");
+
+            }
+            break;
+        }
+
+        Object[] result = new Object[temp.size()];
+        temp.toArray(result);
+        return result;
+    }
+
+    private Object parseArrayItem() {
+        SkipWhite();
+        switch(look) {
+            case '"': return new UnicodeString(parseStringLiteral());
+            case '#': return ErrorConstant.valueOf(parseErrorLiteral());
+            case 'F': case 'f':
+            case 'T': case 't':
+                return parseBooleanLiteral();
+        }
+        // else assume number
+        return convertArrayNumber(parseNumber());
+    }
+
+    private Boolean parseBooleanLiteral() {
+        String iden = parseIdentifier();
+        if ("TRUE".equalsIgnoreCase(iden)) {
+            return Boolean.TRUE;
+        }
+        if ("FALSE".equalsIgnoreCase(iden)) {
+            return Boolean.FALSE;
+        }
+        throw expected("'TRUE' or 'FALSE'");
+    }
+
+    private static Double convertArrayNumber(Ptg ptg) {
+        if (ptg instanceof IntPtg) {
+            return new Double(((IntPtg)ptg).getValue());
+        }
+        if (ptg instanceof NumberPtg) {
+            return new Double(((NumberPtg)ptg).getValue());
+        }
+        throw new RuntimeException("Unexpected ptg (" + ptg.getClass().getName() + ")");
+    }
+
+    private Ptg parseNumber() {
+        String number2 = null;
+        String exponent = null;
+        String number1 = GetNum();
+
+        if (look == '.') {
+            GetChar();
+            number2 = GetNum();
+        }
+
+        if (look == 'E') {
+            GetChar();
+
+            String sign = "";
+            if (look == '+') {
+                GetChar();
+            } else if (look == '-') {
+                GetChar();
+                sign = "-";
+            }
+
+            String number = GetNum();
+            if (number == null) {
+                throw expected("Integer");
+            }
+            exponent = sign + number;
+        }
+
+        if (number1 == null && number2 == null) {
+            throw expected("Integer");
+        }
+
+        return getNumberPtgFromString(number1, number2, exponent);
+    }
+
+
+    private int parseErrorLiteral() {
+        Match('#');
+        String part1 = parseIdentifier().toUpperCase();
+
+        switch(part1.charAt(0)) {
+            case 'V':
+                if(part1.equals("VALUE")) {
+                    Match('!');
+                    return HSSFErrorConstants.ERROR_VALUE;
+                }
+                throw expected("#VALUE!");
+            case 'R':
+                if(part1.equals("REF")) {
+                    Match('!');
+                    return HSSFErrorConstants.ERROR_REF;
+                }
+                throw expected("#REF!");
+            case 'D':
+                if(part1.equals("DIV")) {
+                    Match('/');
+                    Match('0');
+                    Match('!');
+                    return HSSFErrorConstants.ERROR_DIV_0;
+                }
+                throw expected("#DIV/0!");
+            case 'N':
+                if(part1.equals("NAME")) {
+                    Match('?');  // only one that ends in '?'
+                    return HSSFErrorConstants.ERROR_NAME;
+                }
+                if(part1.equals("NUM")) {
+                    Match('!');
+                    return HSSFErrorConstants.ERROR_NUM;
+                }
+                if(part1.equals("NULL")) {
+                    Match('!');
+                    return HSSFErrorConstants.ERROR_NULL;
+                }
+                if(part1.equals("N")) {
+                    Match('/');
+                    if(look != 'A' && look != 'a') {
+                        throw expected("#N/A");
+                    }
+                    Match(look);
+                    // Note - no '!' or '?' suffix
+                    return HSSFErrorConstants.ERROR_NA;
+                }
+                throw expected("#NAME?, #NUM!, #NULL! or #N/A");
+
+        }
+        throw expected("#VALUE!, #REF!, #DIV/0!, #NAME?, #NUM!, #NULL! or #N/A");
+    }
+
+
+    /**
+     * Get a PTG for an integer from its string representation.
+     * return Int or Number Ptg based on size of input
+     */
+    private static Ptg getNumberPtgFromString(String number1, String number2, String exponent) {
+        StringBuffer number = new StringBuffer();
+
+        if (number2 == null) {
+            number.append(number1);
+
+            if (exponent != null) {
+                number.append('E');
+                number.append(exponent);
+            }
+
+            String numberStr = number.toString();
+            int intVal;
+            try {
+                intVal = Integer.parseInt(numberStr);
+            } catch (NumberFormatException e) {
+                return new NumberPtg(numberStr);
+            }
+            if (IntPtg.isInRange(intVal)) {
+                return new IntPtg(intVal);
+            }
+            return new NumberPtg(numberStr);
+        }
+
+        if (number1 != null) {
+            number.append(number1);
+        }
+
+        number.append('.');
+        number.append(number2);
+
+        if (exponent != null) {
+            number.append('E');
+            number.append(exponent);
+        }
+
+        return new NumberPtg(number.toString());
+    }
+
+
+    private String parseStringLiteral() {
+        Match('"');
+
+        StringBuffer token = new StringBuffer();
+        while (true) {
+            if (look == '"') {
+                GetChar();
+                if (look != '"') {
+                    break;
+                }
+             }
+            token.append(look);
+            GetChar();
+        }
+        return token.toString();
+    }
+
+    /** Parse and Translate a Math Term */
+    private ParseNode  Term() {
+        ParseNode result = powerFactor();
+        while(true) {
+            SkipWhite();
+            Ptg operator;
+            switch(look) {
+                case '*':
+                    Match('*');
+                    operator = MultiplyPtg.instance;
+                    break;
+                case '/':
+                    Match('/');
+                    operator = DividePtg.instance;
+                    break;
+                default:
+                    return result; // finished with Term
+            }
+            ParseNode other = powerFactor();
+            result = new ParseNode(operator, result, other);
+        }
+    }
+
+    private ParseNode comparisonExpression() {
+        ParseNode result = concatExpression();
+        while (true) {
+            SkipWhite();
+            switch(look) {
+                case '=':
+                case '>':
+                case '<':
+                    Ptg comparisonToken = getComparisonToken();
+                    ParseNode other = concatExpression();
+                    result = new ParseNode(comparisonToken, result, other);
+                    continue;
+            }
+            return result; // finished with predicate expression
+        }
+    }
+
+    private Ptg getComparisonToken() {
+        if(look == '=') {
+            Match(look);
+            return EqualPtg.instance;
+        }
+        boolean isGreater = look == '>';
+        Match(look);
+        if(isGreater) {
+            if(look == '=') {
+                Match('=');
+                return GreaterEqualPtg.instance;
+            }
+            return GreaterThanPtg.instance;
+        }
+        switch(look) {
+            case '=':
+                Match('=');
+                return LessEqualPtg.instance;
+            case '>':
+                Match('>');
+                return NotEqualPtg.instance;
+        }
+        return LessThanPtg.instance;
+    }
+
+
+    private ParseNode concatExpression() {
+        ParseNode result = additiveExpression();
+        while (true) {
+            SkipWhite();
+            if(look != '&') {
+                break; // finished with concat expression
+            }
+            Match('&');
+            ParseNode other = additiveExpression();
+            result = new ParseNode(ConcatPtg.instance, result, other);
+        }
+        return result;
+    }
+
+
+    /** Parse and Translate an Expression */
+    private ParseNode additiveExpression() {
+        ParseNode result = Term();
+        while (true) {
+            SkipWhite();
+            Ptg operator;
+            switch(look) {
+                case '+':
+                    Match('+');
+                    operator = AddPtg.instance;
+                    break;
+                case '-':
+                    Match('-');
+                    operator = SubtractPtg.instance;
+                    break;
+                default:
+                    return result; // finished with additive expression
+            }
+            ParseNode other = Term();
+            result = new ParseNode(operator, result, other);
+        }
+    }
+
+    //{--------------------------------------------------------------}
+    //{ Parse and Translate an Assignment Statement }
+    /**
+procedure Assignment;
+var Name: string[8];
+begin
+   Name := GetName;
+   Match('=');
+   Expression;
+
+end;
+     **/
+
+
+    /**
+     *  API call to execute the parsing of the formula
+     * 
+     */
+    private void parse() {
+        pointer=0;
+        GetChar();
+        _rootNode = comparisonExpression();
+
+        if(pointer <= formulaLength) {
+            String msg = "Unused input [" + formulaString.substring(pointer-1)
+                + "] after attempting to parse the formula [" + formulaString + "]";
+            throw new FormulaParseException(msg);
+        }
+    }
+
+    private Ptg[] getRPNPtg(int formulaType) {
+        OperandClassTransformer oct = new OperandClassTransformer(formulaType);
+        // RVA is for 'operand class': 'reference', 'value', 'array'
+        oct.transformFormula(_rootNode);
+        return ParseNode.toTokenArray(_rootNode);
+    }
+}
diff --git a/src/java/org/apache/poi/ss/formula/FormulaParsingWorkbook.java b/src/java/org/apache/poi/ss/formula/FormulaParsingWorkbook.java
new file mode 100644 (file)
index 0000000..69431c2
--- /dev/null
@@ -0,0 +1,37 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.ss.formula;\r
+\r
+import org.apache.poi.hssf.record.formula.NameXPtg;\r
+\r
+/**\r
+ * Abstracts a workbook for the purpose of formula parsing.<br/>\r
+ * \r
+ * For POI internal use only\r
+ * \r
+ * @author Josh Micich\r
+ */\r
+public interface FormulaParsingWorkbook {\r
+       /**\r
+        *  named range name matching is case insensitive\r
+        */\r
+       EvaluationName getName(String name);\r
+\r
+       int getExternalSheetIndex(String sheetName);\r
+       NameXPtg getNameXPtg(String name);\r
+}\r
index 21386cd0dd46d1cf2233347960dabf5550299e76..980c345d396b62e31635e4bfb36ff4c1e440caa2 100644 (file)
@@ -1,3 +1,20 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
 package org.apache.poi.ss.formula;\r
 \r
 import java.util.List;\r
@@ -11,6 +28,13 @@ import org.apache.poi.hssf.record.formula.OperationPtg;
 import org.apache.poi.hssf.record.formula.ParenthesisPtg;\r
 import org.apache.poi.hssf.record.formula.Ptg;\r
 \r
+/**\r
+ * Common logic for rendering formulas.<br/>\r
+ * \r
+ * For POI internal use only\r
+ * \r
+ * @author Josh Micich\r
+ */\r
 public class FormulaRenderer {\r
     /**\r
      * Convenience method which takes in a list then passes it to the\r
index 3a92aa8cbc364b9058579469fb64991c5411122c..ac95f4da0f04e3e3e165401eb0920187de2af881 100644 (file)
@@ -1,8 +1,32 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
 package org.apache.poi.ss.formula;\r
 \r
 import org.apache.poi.hssf.record.formula.NamePtg;\r
 import org.apache.poi.hssf.record.formula.NameXPtg;\r
 \r
+/**\r
+ * Abstracts a workbook for the purpose of converting formula to text.<br/>\r
+ * \r
+ * For POI internal use only\r
+ * \r
+ * @author Josh Micich\r
+ */\r
 public interface FormulaRenderingWorkbook {\r
 \r
        String getSheetNameByExternSheet(int externSheetIndex);\r
diff --git a/src/java/org/apache/poi/ss/formula/FormulaType.java b/src/java/org/apache/poi/ss/formula/FormulaType.java
new file mode 100644 (file)
index 0000000..3b47030
--- /dev/null
@@ -0,0 +1,40 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.ss.formula;\r
+\r
+/**\r
+ * Enumeration of various formula types.<br/>\r
+ * \r
+ * For POI internal use only\r
+ * \r
+ * @author Josh Micich\r
+ */\r
+public final class FormulaType {\r
+       private FormulaType() {\r
+               // no instances of this class\r
+       }\r
+    public static final int CELL = 0;\r
+    public static final int SHARED = 1;\r
+    public static final int ARRAY =2;\r
+    public static final int CONDFORMAT = 3;\r
+    public static final int NAMEDRANGE = 4;\r
+    // this constant is currently very specific.  The exact differences from general data\r
+    // validation formulas or conditional format formulas is not known yet\r
+    public static final int DATAVALIDATION_LIST = 5;\r
+\r
+}\r
diff --git a/src/java/org/apache/poi/ss/formula/LazyAreaEval.java b/src/java/org/apache/poi/ss/formula/LazyAreaEval.java
new file mode 100644 (file)
index 0000000..07c368e
--- /dev/null
@@ -0,0 +1,82 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.ss.formula;
+
+import org.apache.poi.hssf.record.formula.AreaI;
+import org.apache.poi.hssf.record.formula.AreaI.OffsetArea;
+import org.apache.poi.hssf.record.formula.eval.AreaEval;
+import org.apache.poi.hssf.record.formula.eval.AreaEvalBase;
+import org.apache.poi.hssf.record.formula.eval.BlankEval;
+import org.apache.poi.hssf.record.formula.eval.ValueEval;
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.util.CellReference;
+
+/**
+ *
+ * @author Josh Micich 
+ */
+final class LazyAreaEval extends AreaEvalBase {
+
+       private final HSSFSheet _sheet;
+       private final CellEvaluator _evaluator;
+
+       public LazyAreaEval(AreaI ptg, HSSFSheet sheet, CellEvaluator evaluator) {
+               super(ptg);
+               _sheet = sheet;
+               _evaluator = evaluator;
+       }
+
+       public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) { 
+               
+               int rowIx = (relativeRowIndex + getFirstRow() ) & 0xFFFF;
+               int colIx = (relativeColumnIndex + getFirstColumn() ) & 0x00FF;
+               
+               HSSFRow row = _sheet.getRow(rowIx);
+               if (row == null) {
+                       return BlankEval.INSTANCE;
+               }
+               HSSFCell cell = row.getCell(colIx);
+               if (cell == null) {
+                       return BlankEval.INSTANCE;
+               }
+               return _evaluator.getEvalForCell(cell);
+       }
+
+       public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) {
+               AreaI area = new OffsetArea(getFirstRow(), getFirstColumn(),
+                               relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);
+
+               return new LazyAreaEval(area, _sheet, _evaluator);
+       }
+       public String toString() {
+               CellReference crA = new CellReference(getFirstRow(), getFirstColumn());
+               CellReference crB = new CellReference(getLastRow(), getLastColumn());
+               StringBuffer sb = new StringBuffer();
+               sb.append(getClass().getName()).append("[");
+               String sheetName = _evaluator.getSheetName(_sheet);
+               sb.append(sheetName);
+               sb.append('!');
+               sb.append(crA.formatAsString());
+               sb.append(':');
+               sb.append(crB.formatAsString());
+               sb.append("]");
+               return sb.toString();
+       }
+}
diff --git a/src/java/org/apache/poi/ss/formula/LazyRefEval.java b/src/java/org/apache/poi/ss/formula/LazyRefEval.java
new file mode 100644 (file)
index 0000000..fa1f3a3
--- /dev/null
@@ -0,0 +1,88 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.ss.formula;\r
+\r
+import org.apache.poi.hssf.record.formula.AreaI;\r
+import org.apache.poi.hssf.record.formula.Ref3DPtg;\r
+import org.apache.poi.hssf.record.formula.RefPtg;\r
+import org.apache.poi.hssf.record.formula.AreaI.OffsetArea;\r
+import org.apache.poi.hssf.record.formula.eval.AreaEval;\r
+import org.apache.poi.hssf.record.formula.eval.BlankEval;\r
+import org.apache.poi.hssf.record.formula.eval.RefEvalBase;\r
+import org.apache.poi.hssf.record.formula.eval.ValueEval;\r
+import org.apache.poi.hssf.usermodel.HSSFCell;\r
+import org.apache.poi.hssf.usermodel.HSSFRow;\r
+import org.apache.poi.hssf.usermodel.HSSFSheet;\r
+import org.apache.poi.hssf.util.CellReference;\r
+\r
+/**\r
+*\r
+* @author Josh Micich \r
+*/\r
+final class LazyRefEval extends RefEvalBase {\r
+\r
+       private final HSSFSheet _sheet;\r
+       private final CellEvaluator _evaluator;\r
+\r
+\r
+       public LazyRefEval(RefPtg ptg, HSSFSheet sheet, CellEvaluator evaluator) {\r
+               super(ptg.getRow(), ptg.getColumn());\r
+               _sheet = sheet;\r
+               _evaluator = evaluator;\r
+       }\r
+       public LazyRefEval(Ref3DPtg ptg, HSSFSheet sheet, CellEvaluator evaluator) {\r
+               super(ptg.getRow(), ptg.getColumn());\r
+               _sheet = sheet;\r
+               _evaluator = evaluator;\r
+       }\r
+\r
+       public ValueEval getInnerValueEval() {\r
+               int rowIx = getRow();\r
+               int colIx = getColumn();\r
+               \r
+               HSSFRow row = _sheet.getRow(rowIx);\r
+               if (row == null) {\r
+                       return BlankEval.INSTANCE;\r
+               }\r
+               HSSFCell cell = row.getCell(colIx);\r
+               if (cell == null) {\r
+                       return BlankEval.INSTANCE;\r
+               }\r
+               return _evaluator.getEvalForCell(cell);\r
+       }\r
+       \r
+       public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) {\r
+               \r
+               AreaI area = new OffsetArea(getRow(), getColumn(),\r
+                               relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);\r
+\r
+               return new LazyAreaEval(area, _sheet, _evaluator);\r
+       }\r
+       \r
+       public String toString() {\r
+               CellReference cr = new CellReference(getRow(), getColumn());\r
+               StringBuffer sb = new StringBuffer();\r
+               sb.append(getClass().getName()).append("[");\r
+               String sheetName = _evaluator.getSheetName(_sheet);\r
+               sb.append(sheetName);\r
+               sb.append('!');\r
+               sb.append(cr.formatAsString());\r
+               sb.append("]");\r
+               return sb.toString();\r
+       }\r
+}\r
diff --git a/src/java/org/apache/poi/ss/formula/OperandClassTransformer.java b/src/java/org/apache/poi/ss/formula/OperandClassTransformer.java
new file mode 100644 (file)
index 0000000..79087e4
--- /dev/null
@@ -0,0 +1,259 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.ss.formula;
+
+import org.apache.poi.hssf.record.formula.AbstractFunctionPtg;
+import org.apache.poi.hssf.record.formula.ControlPtg;
+import org.apache.poi.hssf.record.formula.ValueOperatorPtg;
+import org.apache.poi.hssf.record.formula.Ptg;
+
+/**
+ * This class performs 'operand class' transformation. Non-base tokens are classified into three 
+ * operand classes:
+ * <ul>
+ * <li>reference</li> 
+ * <li>value</li> 
+ * <li>array</li> 
+ * </ul>
+ * <p/>
+ * 
+ * The final operand class chosen for each token depends on the formula type and the token's place
+ * in the formula. If POI gets the operand class wrong, Excel <em>may</em> interpret the formula
+ * incorrectly.  This condition is typically manifested as a formula cell that displays as '#VALUE!',
+ * but resolves correctly when the user presses F2, enter.<p/>
+ * 
+ * The logic implemented here was partially inspired by the description in
+ * "OpenOffice.org's Documentation of the Microsoft Excel File Format".  The model presented there
+ * seems to be inconsistent with observed Excel behaviour (These differences have not been fully
+ * investigated). The implementation in this class has been heavily modified in order to satisfy
+ * concrete examples of how Excel performs the same logic (see TestRVA).<p/>
+ * 
+ * Hopefully, as additional important test cases are identified and added to the test suite, 
+ * patterns might become more obvious in this code and allow for simplification.
+ * 
+ * @author Josh Micich
+ */
+final class OperandClassTransformer {
+
+       private final int _formulaType;
+
+       public OperandClassTransformer(int formulaType) {
+               _formulaType = formulaType;
+       }
+
+       /**
+        * Traverses the supplied formula parse tree, calling <tt>Ptg.setClass()</tt> for each non-base
+        * token to set its operand class.
+        */
+       public void transformFormula(ParseNode rootNode) {
+               byte rootNodeOperandClass;
+               switch (_formulaType) {
+                       case FormulaType.CELL:
+                               rootNodeOperandClass = Ptg.CLASS_VALUE;
+                               break;
+                       case FormulaType.DATAVALIDATION_LIST:
+                               rootNodeOperandClass = Ptg.CLASS_REF;
+                               break;
+                       default:
+                               throw new RuntimeException("Incomplete code - formula type (" 
+                                               + _formulaType + ") not supported yet");
+               
+               }
+               transformNode(rootNode, rootNodeOperandClass, false);
+       }
+
+       /**
+        * @param callerForceArrayFlag <code>true</code> if one of the current node's parents is a 
+        * function Ptg which has been changed from default 'V' to 'A' type (due to requirements on
+        * the function return value).
+        */
+       private void transformNode(ParseNode node, byte desiredOperandClass,
+                       boolean callerForceArrayFlag) {
+               Ptg token = node.getToken();
+               ParseNode[] children = node.getChildren();
+               boolean isSimpleValueFunc = isSimpleValueFunction(token);
+               
+               if (isSimpleValueFunc) {
+                       boolean localForceArray = desiredOperandClass == Ptg.CLASS_ARRAY;
+                       for (int i = 0; i < children.length; i++) {
+                               transformNode(children[i], desiredOperandClass, localForceArray);
+                       }
+                       setSimpleValueFuncClass((AbstractFunctionPtg) token, desiredOperandClass, callerForceArrayFlag);
+                       return;
+               }
+               
+               if (token instanceof ValueOperatorPtg || token instanceof ControlPtg) {
+                       // Value Operator Ptgs and Control are base tokens, so token will be unchanged
+                       // but any child nodes are processed according to desiredOperandClass and callerForceArrayFlag
+                       
+                       // As per OOO documentation Sec 3.2.4 "Token Class Transformation", "Step 1"
+                       // All direct operands of value operators that are initially 'R' type will 
+                       // be converted to 'V' type.
+                       byte localDesiredOperandClass = desiredOperandClass == Ptg.CLASS_REF ? Ptg.CLASS_VALUE : desiredOperandClass;
+                       for (int i = 0; i < children.length; i++) {
+                               transformNode(children[i], localDesiredOperandClass, callerForceArrayFlag);
+                       }
+                       return;
+               }
+               if (token instanceof AbstractFunctionPtg) {
+                       transformFunctionNode((AbstractFunctionPtg) token, children, desiredOperandClass, callerForceArrayFlag);
+                       return;
+               }
+               if (children.length > 0) {
+                       throw new IllegalStateException("Node should not have any children");
+               }
+
+               if (token.isBaseToken()) {
+                       // nothing to do
+                       return;
+               }
+               token.setClass(transformClass(token.getPtgClass(), desiredOperandClass, callerForceArrayFlag));
+       }
+
+       private static boolean isSimpleValueFunction(Ptg token) {
+               if (token instanceof AbstractFunctionPtg) {
+                       AbstractFunctionPtg aptg = (AbstractFunctionPtg) token;
+                       if (aptg.getDefaultOperandClass() != Ptg.CLASS_VALUE) {
+                               return false;
+                       }
+                       int numberOfOperands = aptg.getNumberOfOperands();
+                       for (int i=numberOfOperands-1; i>=0; i--) {
+                               if (aptg.getParameterClass(i) != Ptg.CLASS_VALUE) {
+                                       return false;
+                               }
+                       }
+                       return true;
+               }
+               return false;
+       }
+
+       private byte transformClass(byte currentOperandClass, byte desiredOperandClass,
+                       boolean callerForceArrayFlag) {
+               switch (desiredOperandClass) {
+                       case Ptg.CLASS_VALUE:
+                               if (!callerForceArrayFlag) {
+                                       return Ptg.CLASS_VALUE;
+                               }
+                               // else fall through
+                       case Ptg.CLASS_ARRAY:
+                               return Ptg.CLASS_ARRAY; 
+                       case Ptg.CLASS_REF:
+                               if (!callerForceArrayFlag) {
+                                       return currentOperandClass;
+                               }
+                               return Ptg.CLASS_REF; 
+               }
+               throw new IllegalStateException("Unexpected operand class (" + desiredOperandClass + ")");
+       }
+
+       private void transformFunctionNode(AbstractFunctionPtg afp, ParseNode[] children,
+                       byte desiredOperandClass, boolean callerForceArrayFlag) {
+
+               boolean localForceArrayFlag;
+               byte defaultReturnOperandClass = afp.getDefaultOperandClass();
+
+               if (callerForceArrayFlag) {
+                       switch (defaultReturnOperandClass) {
+                               case Ptg.CLASS_REF:
+                                       if (desiredOperandClass == Ptg.CLASS_REF) {
+                                               afp.setClass(Ptg.CLASS_REF);
+                                       } else {
+                                               afp.setClass(Ptg.CLASS_ARRAY);
+                                       }
+                                       localForceArrayFlag = false;
+                                       break;
+                               case Ptg.CLASS_ARRAY:
+                                       afp.setClass(Ptg.CLASS_ARRAY);
+                                       localForceArrayFlag = false;
+                                       break;
+                               case Ptg.CLASS_VALUE:
+                                       afp.setClass(Ptg.CLASS_ARRAY);
+                                       localForceArrayFlag = true;
+                                       break;
+                               default:
+                                       throw new IllegalStateException("Unexpected operand class ("
+                                                       + defaultReturnOperandClass + ")");
+                       }
+               } else {
+                       if (defaultReturnOperandClass == desiredOperandClass) {
+                               localForceArrayFlag = false;
+                               // an alternative would have been to for non-base Ptgs to set their operand class 
+                               // from their default, but this would require the call in many subclasses because
+                               // the default OC is not known until the end of the constructor
+                               afp.setClass(defaultReturnOperandClass); 
+                       } else {
+                               switch (desiredOperandClass) {
+                                       case Ptg.CLASS_VALUE:
+                                               // always OK to set functions to return 'value'
+                                               afp.setClass(Ptg.CLASS_VALUE); 
+                                               localForceArrayFlag = false;
+                                               break;
+                                       case Ptg.CLASS_ARRAY:
+                                               switch (defaultReturnOperandClass) {
+                                                       case Ptg.CLASS_REF:
+                                                               afp.setClass(Ptg.CLASS_REF);
+//                                                             afp.setClass(Ptg.CLASS_ARRAY);
+                                                               break;
+                                                       case Ptg.CLASS_VALUE:
+                                                               afp.setClass(Ptg.CLASS_ARRAY);
+                                                               break;
+                                                       default:
+                                                               throw new IllegalStateException("Unexpected operand class ("
+                                                                               + defaultReturnOperandClass + ")");
+                                               }
+                                               localForceArrayFlag = (defaultReturnOperandClass == Ptg.CLASS_VALUE);
+                                               break;
+                                       case Ptg.CLASS_REF:
+                                               switch (defaultReturnOperandClass) {
+                                                       case Ptg.CLASS_ARRAY:
+                                                               afp.setClass(Ptg.CLASS_ARRAY);
+                                                               break;
+                                                       case Ptg.CLASS_VALUE:
+                                                               afp.setClass(Ptg.CLASS_VALUE);
+                                                               break;
+                                                       default:
+                                                               throw new IllegalStateException("Unexpected operand class ("
+                                                                               + defaultReturnOperandClass + ")");
+                                               }
+                                               localForceArrayFlag = false;
+                                               break;
+                                       default:
+                                               throw new IllegalStateException("Unexpected operand class ("
+                                                               + desiredOperandClass + ")");
+                               }
+
+                       }
+               }
+
+               for (int i = 0; i < children.length; i++) {
+                       ParseNode child = children[i];
+                       byte paramOperandClass = afp.getParameterClass(i);
+                       transformNode(child, paramOperandClass, localForceArrayFlag);
+               }
+       }
+
+       private void setSimpleValueFuncClass(AbstractFunctionPtg afp,
+                       byte desiredOperandClass, boolean callerForceArrayFlag) {
+
+               if (callerForceArrayFlag  || desiredOperandClass == Ptg.CLASS_ARRAY) {
+                       afp.setClass(Ptg.CLASS_ARRAY);
+               } else {
+                       afp.setClass(Ptg.CLASS_VALUE); 
+               }
+       }
+}
diff --git a/src/java/org/apache/poi/ss/formula/OperationEvaluatorFactory.java b/src/java/org/apache/poi/ss/formula/OperationEvaluatorFactory.java
new file mode 100755 (executable)
index 0000000..eaa57c1
--- /dev/null
@@ -0,0 +1,184 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.ss.formula;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.poi.hssf.record.formula.AddPtg;
+import org.apache.poi.hssf.record.formula.ConcatPtg;
+import org.apache.poi.hssf.record.formula.DividePtg;
+import org.apache.poi.hssf.record.formula.EqualPtg;
+import org.apache.poi.hssf.record.formula.ExpPtg;
+import org.apache.poi.hssf.record.formula.FuncPtg;
+import org.apache.poi.hssf.record.formula.FuncVarPtg;
+import org.apache.poi.hssf.record.formula.GreaterEqualPtg;
+import org.apache.poi.hssf.record.formula.GreaterThanPtg;
+import org.apache.poi.hssf.record.formula.LessEqualPtg;
+import org.apache.poi.hssf.record.formula.LessThanPtg;
+import org.apache.poi.hssf.record.formula.MultiplyPtg;
+import org.apache.poi.hssf.record.formula.NotEqualPtg;
+import org.apache.poi.hssf.record.formula.OperationPtg;
+import org.apache.poi.hssf.record.formula.PercentPtg;
+import org.apache.poi.hssf.record.formula.PowerPtg;
+import org.apache.poi.hssf.record.formula.Ptg;
+import org.apache.poi.hssf.record.formula.SubtractPtg;
+import org.apache.poi.hssf.record.formula.UnaryMinusPtg;
+import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
+import org.apache.poi.hssf.record.formula.eval.AddEval;
+import org.apache.poi.hssf.record.formula.eval.ConcatEval;
+import org.apache.poi.hssf.record.formula.eval.DivideEval;
+import org.apache.poi.hssf.record.formula.eval.EqualEval;
+import org.apache.poi.hssf.record.formula.eval.FuncVarEval;
+import org.apache.poi.hssf.record.formula.eval.GreaterEqualEval;
+import org.apache.poi.hssf.record.formula.eval.GreaterThanEval;
+import org.apache.poi.hssf.record.formula.eval.LessEqualEval;
+import org.apache.poi.hssf.record.formula.eval.LessThanEval;
+import org.apache.poi.hssf.record.formula.eval.MultiplyEval;
+import org.apache.poi.hssf.record.formula.eval.NotEqualEval;
+import org.apache.poi.hssf.record.formula.eval.OperationEval;
+import org.apache.poi.hssf.record.formula.eval.PercentEval;
+import org.apache.poi.hssf.record.formula.eval.PowerEval;
+import org.apache.poi.hssf.record.formula.eval.SubtractEval;
+import org.apache.poi.hssf.record.formula.eval.UnaryMinusEval;
+import org.apache.poi.hssf.record.formula.eval.UnaryPlusEval;
+
+/**
+ * This class creates <tt>OperationEval</tt> instances to help evaluate <tt>OperationPtg</tt>
+ * formula tokens.
+ * 
+ * @author Josh Micich
+ */
+final class OperationEvaluatorFactory {
+       private static final Class[] OPERATION_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class };
+       // TODO - use singleton instances directly instead of reflection
+       private static final Map _constructorsByPtgClass = initialiseConstructorsMap();
+       private static final Map _instancesByPtgClass = initialiseInstancesMap();
+       
+       private OperationEvaluatorFactory() {
+               // no instances of this class
+       }
+       
+       private static Map initialiseConstructorsMap() {
+               Map m = new HashMap(32);
+               add(m, ConcatPtg.class, ConcatEval.class);
+               add(m, FuncPtg.class, FuncVarEval.class);
+               add(m, FuncVarPtg.class, FuncVarEval.class);
+               return m;
+       }
+       private static Map initialiseInstancesMap() {
+               Map m = new HashMap(32);
+               add(m, EqualPtg.class, EqualEval.instance);
+               add(m, GreaterEqualPtg.class, GreaterEqualEval.instance);
+               add(m, GreaterThanPtg.class, GreaterThanEval.instance);
+               add(m, LessEqualPtg.class, LessEqualEval.instance);
+               add(m, LessThanPtg.class, LessThanEval.instance);
+               add(m, NotEqualPtg.class, NotEqualEval.instance);
+
+               add(m, AddPtg.class, AddEval.instance);
+               add(m, DividePtg.class, DivideEval.instance);
+               add(m, MultiplyPtg.class, MultiplyEval.instance);
+               add(m, PercentPtg.class, PercentEval.instance);
+               add(m, PowerPtg.class, PowerEval.instance);
+               add(m, SubtractPtg.class, SubtractEval.instance);
+               add(m, UnaryMinusPtg.class, UnaryMinusEval.instance);
+               add(m, UnaryPlusPtg.class, UnaryPlusEval.instance);
+               return m;
+       }
+
+       private static void add(Map m, Class ptgClass, OperationEval evalInstance) {
+               if(!Ptg.class.isAssignableFrom(ptgClass)) {
+                       throw new IllegalArgumentException("Expected Ptg subclass");
+               }
+               m.put(ptgClass, evalInstance);
+       }
+
+       private static void add(Map m, Class ptgClass, Class evalClass) {
+               // perform some validation now, to keep later exception handlers simple
+               if(!Ptg.class.isAssignableFrom(ptgClass)) {
+                       throw new IllegalArgumentException("Expected Ptg subclass");
+               }
+               
+               if(!OperationEval.class.isAssignableFrom(evalClass)) {
+                       throw new IllegalArgumentException("Expected OperationEval subclass");
+               }
+               if (!Modifier.isPublic(evalClass.getModifiers())) {
+                       throw new RuntimeException("Eval class must be public");
+               }
+               if (Modifier.isAbstract(evalClass.getModifiers())) {
+                       throw new RuntimeException("Eval class must not be abstract");
+               }
+               
+               Constructor constructor;
+               try {
+                       constructor = evalClass.getDeclaredConstructor(OPERATION_CONSTRUCTOR_CLASS_ARRAY);
+               } catch (NoSuchMethodException e) {
+                       throw new RuntimeException("Missing constructor");
+               }
+               if (!Modifier.isPublic(constructor.getModifiers())) {
+                       throw new RuntimeException("Eval constructor must be public");
+               }
+               m.put(ptgClass, constructor);
+       }
+
+       /**
+        * returns the OperationEval concrete impl instance corresponding
+        * to the supplied operationPtg
+        */
+       public static OperationEval create(OperationPtg ptg) {
+               if(ptg == null) {
+                       throw new IllegalArgumentException("ptg must not be null");
+               }
+               Object result;
+               
+               Class ptgClass = ptg.getClass();
+               
+               result = _instancesByPtgClass.get(ptgClass);
+               if (result != null) {
+                       return (OperationEval) result;
+               }
+               
+               
+               Constructor constructor = (Constructor) _constructorsByPtgClass.get(ptgClass);
+               if(constructor == null) {
+                       if(ptgClass == ExpPtg.class) {
+                               // ExpPtg is used for array formulas and shared formulas.
+                               // it is currently unsupported, and may not even get implemented here
+                               throw new RuntimeException("ExpPtg currently not supported");
+                       }
+                       throw new RuntimeException("Unexpected operation ptg class (" + ptgClass.getName() + ")");
+               }
+               
+               Object[] initargs = { ptg };
+               try {
+                       result = constructor.newInstance(initargs);
+               } catch (IllegalArgumentException e) {
+                       throw new RuntimeException(e);
+               } catch (InstantiationException e) {
+                       throw new RuntimeException(e);
+               } catch (IllegalAccessException e) {
+                       throw new RuntimeException(e);
+               } catch (InvocationTargetException e) {
+                       throw new RuntimeException(e);
+               }
+               return (OperationEval) result;
+       }
+}
diff --git a/src/java/org/apache/poi/ss/formula/ParseNode.java b/src/java/org/apache/poi/ss/formula/ParseNode.java
new file mode 100644 (file)
index 0000000..75685de
--- /dev/null
@@ -0,0 +1,201 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.ss.formula;
+
+import org.apache.poi.hssf.record.formula.AttrPtg;
+import org.apache.poi.hssf.record.formula.FuncVarPtg;
+import org.apache.poi.hssf.record.formula.Ptg;
+import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
+/**
+ * Represents a syntactic element from a formula by encapsulating the corresponding <tt>Ptg</tt>
+ * token.  Each <tt>ParseNode</tt> may have child <tt>ParseNode</tt>s in the case when the wrapped
+ * <tt>Ptg</tt> is non-atomic.
+ * 
+ * @author Josh Micich
+ */
+final class ParseNode {
+
+       public static final ParseNode[] EMPTY_ARRAY = { };
+       private final Ptg _token;
+       private final ParseNode[] _children;
+       private boolean _isIf;
+       private final int _tokenCount;
+
+       public ParseNode(Ptg token, ParseNode[] children) {
+               _token = token;
+               _children = children;
+               _isIf = isIf(token);
+               int tokenCount = 1;
+               for (int i = 0; i < children.length; i++) {
+                       tokenCount += children[i].getTokenCount();
+               }
+               if (_isIf) {
+                       // there will be 2 or 3 extra tAttr tokens according to whether the false param is present
+                       tokenCount += children.length;
+               }
+               _tokenCount = tokenCount;
+       }
+       public ParseNode(Ptg token) {
+               this(token, EMPTY_ARRAY);
+       }
+       public ParseNode(Ptg token, ParseNode child0) {
+               this(token, new ParseNode[] { child0, });
+       }
+       public ParseNode(Ptg token, ParseNode child0, ParseNode child1) {
+               this(token, new ParseNode[] { child0, child1, });
+       }
+       private int getTokenCount() {
+               return _tokenCount;
+       }
+
+       /**
+        * Collects the array of <tt>Ptg</tt> tokens for the specified tree.
+        */
+       public static Ptg[] toTokenArray(ParseNode rootNode) {
+               TokenCollector temp = new TokenCollector(rootNode.getTokenCount());
+               rootNode.collectPtgs(temp);
+               return temp.getResult();
+       }
+       private void collectPtgs(TokenCollector temp) {
+               if (isIf(getToken())) {
+                       collectIfPtgs(temp);
+                       return;
+               }
+               for (int i=0; i< getChildren().length; i++) {
+                       getChildren()[i].collectPtgs(temp);
+               }
+               temp.add(getToken());
+       }
+       /**
+        * The IF() function gets marked up with two or three tAttr tokens.
+        * Similar logic will be required for CHOOSE() when it is supported
+        * 
+        * See excelfileformat.pdf sec 3.10.5 "tAttr (19H)
+        */
+       private void collectIfPtgs(TokenCollector temp) {
+
+               // condition goes first
+               getChildren()[0].collectPtgs(temp);
+               
+               // placeholder for tAttrIf
+               int ifAttrIndex = temp.createPlaceholder();
+               
+               // true parameter
+               getChildren()[1].collectPtgs(temp);
+               
+               // placeholder for first skip attr
+               int skipAfterTrueParamIndex = temp.createPlaceholder();
+               int trueParamSize = temp.sumTokenSizes(ifAttrIndex+1, skipAfterTrueParamIndex);
+
+               AttrPtg attrIf = new AttrPtg();
+               attrIf.setOptimizedIf(true);
+               AttrPtg attrSkipAfterTrue = new AttrPtg();
+               attrSkipAfterTrue.setGoto(true);
+               
+               if (getChildren().length > 2) {
+                       // false param present
+                       
+                       // false parameter
+                       getChildren()[2].collectPtgs(temp);
+                       
+                       int skipAfterFalseParamIndex = temp.createPlaceholder();
+
+                       AttrPtg attrSkipAfterFalse = new AttrPtg();
+                       attrSkipAfterFalse.setGoto(true);
+
+                       int falseParamSize =  temp.sumTokenSizes(skipAfterTrueParamIndex+1, skipAfterFalseParamIndex);
+                       
+                       attrIf.setData((short)(trueParamSize + 4)); // distance to start of false parameter. +4 for skip after true
+                       attrSkipAfterTrue.setData((short)(falseParamSize + 4 + 4 - 1)); // 1 less than distance to end of if FuncVar(size=4). +4 for attr skip before 
+                       attrSkipAfterFalse.setData((short)(4 - 1)); // 1 less than distance to end of if FuncVar(size=4). 
+
+                       temp.setPlaceholder(ifAttrIndex, attrIf);
+                       temp.setPlaceholder(skipAfterTrueParamIndex, attrSkipAfterTrue);
+                       temp.setPlaceholder(skipAfterFalseParamIndex, attrSkipAfterFalse);
+               } else {
+                       // false parameter not present
+                       attrIf.setData((short)(trueParamSize + 4)); // distance to start of FuncVar. +4 for skip after true
+                       attrSkipAfterTrue.setData((short)(4 - 1)); // 1 less than distance to end of if FuncVar(size=4). 
+                       
+                       temp.setPlaceholder(ifAttrIndex, attrIf);
+                       temp.setPlaceholder(skipAfterTrueParamIndex, attrSkipAfterTrue);
+               }
+               
+               temp.add(getToken());
+       }
+
+       private static boolean isIf(Ptg token) {
+               if (token instanceof FuncVarPtg) {
+                       FuncVarPtg func = (FuncVarPtg) token;
+                       if (FunctionMetadataRegistry.FUNCTION_NAME_IF.equals(func.getName())) {
+                               return true;
+                       }
+               }
+               return false;
+       }
+
+       public Ptg getToken() {
+               return _token;
+       }
+
+       public ParseNode[] getChildren() {
+               return _children;
+       }
+
+       private static final class TokenCollector {
+
+               private final Ptg[] _ptgs;
+               private int _offset;
+
+               public TokenCollector(int tokenCount) {
+                       _ptgs = new Ptg[tokenCount];
+                       _offset = 0;
+               }
+
+               public int sumTokenSizes(int fromIx, int toIx) {
+                       int result = 0;
+                       for (int i=fromIx; i<toIx; i++) {
+                               result += _ptgs[i].getSize();
+                       }
+                       return result;
+               }
+
+               public int createPlaceholder() {
+                       return _offset++;
+               }
+
+               public void add(Ptg token) {
+                       if (token == null) {
+                               throw new IllegalArgumentException("token must not be null");
+                       }
+                       _ptgs[_offset] = token;
+                       _offset++;
+               }
+
+               public void setPlaceholder(int index, Ptg token) {
+                       if (_ptgs[index] != null) {
+                               throw new IllegalStateException("Invalid placeholder index (" + index + ")");
+                       }
+                       _ptgs[index] = token;
+               }
+
+               public Ptg[] getResult() {
+                       return _ptgs;
+               }
+       }
+}
index 420527dd819c2eac555fab7df8879868b6c10ab7..b7ccfaf0eb970bcd40d0089fae52b26c8172a04e 100644 (file)
@@ -1,5 +1,30 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
 package org.apache.poi.ss.formula;\r
 \r
+/**\r
+ * Should be implemented by any {@link Ptg} subclass that needs a workbook to render its formula.\r
+ * <br/>\r
+ * \r
+ * For POI internal use only\r
+ * \r
+ * @author Josh Micich\r
+ */\r
 public interface WorkbookDependentFormula {\r
        String toFormulaString(FormulaRenderingWorkbook book);\r
 }\r
diff --git a/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java b/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java
new file mode 100644 (file)
index 0000000..199d35b
--- /dev/null
@@ -0,0 +1,348 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.ss.formula;
+
+import java.util.Stack;
+
+import org.apache.poi.hssf.record.formula.Area3DPtg;
+import org.apache.poi.hssf.record.formula.AreaPtg;
+import org.apache.poi.hssf.record.formula.BoolPtg;
+import org.apache.poi.hssf.record.formula.ControlPtg;
+import org.apache.poi.hssf.record.formula.ErrPtg;
+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.Ptg;
+import org.apache.poi.hssf.record.formula.Ref3DPtg;
+import org.apache.poi.hssf.record.formula.RefPtg;
+import org.apache.poi.hssf.record.formula.StringPtg;
+import org.apache.poi.hssf.record.formula.UnionPtg;
+import org.apache.poi.hssf.record.formula.UnknownPtg;
+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.NameXEval;
+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.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.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.util.CellReference;
+
+/**
+ * Evaluates formula cells.<p/>
+ *
+ * For performance reasons, this class keeps a cache of all previously calculated intermediate
+ * cell values.  Be sure to call {@link #clearCache()} if any workbook cells are changed between
+ * calls to evaluate~ methods on this class.<br/>
+ *
+ * For POI internal use only
+ *
+ * @author Josh Micich
+ */
+public class WorkbookEvaluator {
+
+       /**
+        * used to track the number of evaluations
+        */
+       private static final class Counter {
+               public int value;
+               public int depth;
+               public Counter() {
+                       value = 0;
+               }
+       }
+
+       private final EvaluationWorkbook _workbook;
+       private final EvaluationCache _cache;
+
+       private Counter _evaluationCounter;
+
+       public WorkbookEvaluator(EvaluationWorkbook workbook) {
+               _workbook = workbook;
+               _cache = new EvaluationCache();
+               _evaluationCounter = new Counter();
+       }
+
+       /**
+        * for debug use. Used in toString methods
+        */
+       /* package */ String getSheetName(HSSFSheet sheet) {
+               return getSheetName(getSheetIndex(sheet));
+       }
+       private String getSheetName(int sheetIndex) {
+               return _workbook.getSheetName(sheetIndex);
+       }
+       /**
+        * for debug/test use
+        */
+       public int getEvaluationCount() {
+               return _evaluationCounter.value;
+       }
+
+       private static boolean isDebugLogEnabled() {
+               return false;
+       }
+       private static void logDebug(String s) {
+               if (isDebugLogEnabled()) {
+                       System.out.println(s);
+               }
+       }
+
+       /**
+        * Should be called whenever there are changes to input cells in the evaluated workbook.
+        * Failure to call this method after changing cell values will cause incorrect behaviour
+        * of the evaluate~ methods of this class
+        */
+       public void clearAllCachedResultValues() {
+               _cache.clear();
+       }
+
+       public void clearCachedResultValue(HSSFSheet sheet, int rowIndex, int columnIndex) {
+               int sheetIndex = getSheetIndex(sheet);
+               _cache.clearValue(sheetIndex, rowIndex, columnIndex);
+
+       }
+       private int getSheetIndex(HSSFSheet sheet) {
+               // TODO cache sheet indexes too
+               return _workbook.getSheetIndex(sheet);
+       }
+
+       public ValueEval evaluate(HSSFCell srcCell) {
+               return internalEvaluate(srcCell, new EvaluationTracker(_cache));
+       }
+
+       /**
+        * 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.)
+        * @return never <code>null</code>, never {@link BlankEval}
+        */
+       /* package */ ValueEval internalEvaluate(HSSFCell srcCell, EvaluationTracker tracker) {
+               int srcRowNum = srcCell.getRowIndex();
+               int srcColNum = srcCell.getCellNum();
+
+               ValueEval result;
+
+               int sheetIndex = getSheetIndex(srcCell.getSheet());
+               result = tracker.startEvaluate(sheetIndex, srcRowNum, srcColNum);
+               if (result != null) {
+                       return result;
+               }
+               _evaluationCounter.value++;
+               _evaluationCounter.depth++;
+
+               try {
+                       Ptg[] ptgs = _workbook.getFormulaTokens(srcCell);
+                       result = evaluateCell(sheetIndex, srcRowNum, (short)srcColNum, ptgs, tracker);
+               } finally {
+                       tracker.endEvaluate(sheetIndex, srcRowNum, srcColNum, result);
+                       _evaluationCounter.depth--;
+               }
+               if (isDebugLogEnabled()) {
+                       String sheetName = getSheetName(sheetIndex);
+                       CellReference cr = new CellReference(srcRowNum, srcColNum);
+                       logDebug("Evaluated " + sheetName + "!" + cr.formatAsString() + " to " + result.toString());
+               }
+               return result;
+       }
+       private ValueEval evaluateCell(int sheetIndex, int srcRowNum, short srcColNum, Ptg[] ptgs, EvaluationTracker tracker) {
+
+               Stack stack = new Stack();
+               for (int i = 0, iSize = ptgs.length; i < iSize; i++) {
+
+                       // since we don't know how to handle these yet :(
+                       Ptg ptg = ptgs[i];
+                       if (ptg instanceof ControlPtg) {
+                               // skip Parentheses, Attr, etc
+                               continue;
+                       }
+                       if (ptg instanceof MemErrPtg) { continue; }
+                       if (ptg instanceof MissingArgPtg) {
+                               // TODO - might need to push BlankEval or MissingArgEval
+                               continue;
+                       }
+                       Eval opResult;
+                       if (ptg instanceof OperationPtg) {
+                               OperationPtg optg = (OperationPtg) ptg;
+
+                               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;
+                               }
+//                             logDebug("invoke " + operation + " (nAgs=" + numops + ")");
+                               opResult = invokeOperation(operation, ops, _workbook, sheetIndex, srcRowNum, srcColNum);
+                       } else {
+                               opResult = getEvalForPtg(ptg, sheetIndex, tracker);
+                       }
+                       if (opResult == null) {
+                               throw new RuntimeException("Evaluation result must not be null");
+                       }
+//                     logDebug("push " + opResult);
+                       stack.push(opResult);
+               }
+
+               ValueEval value = ((ValueEval) stack.pop());
+               if (!stack.isEmpty()) {
+                       throw new IllegalStateException("evaluation stack not empty");
+               }
+               value = dereferenceValue(value, srcRowNum, srcColNum);
+               if (value == BlankEval.INSTANCE) {
+                       // 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.getRelativeValue(0, 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,
+                       EvaluationWorkbook workbook, int sheetIndex, int srcRowNum, int srcColNum) {
+
+               if(operation instanceof FunctionEval) {
+                       FunctionEval fe = (FunctionEval) operation;
+                       if(fe.isFreeRefFunction()) {
+                               return fe.getFreeRefFunction().evaluate(ops, workbook, sheetIndex, srcRowNum, srcColNum);
+                       }
+               }
+               return operation.evaluate(ops, srcRowNum, (short)srcColNum);
+       }
+
+       private HSSFSheet getOtherSheet(int externSheetIndex) {
+               return _workbook.getSheetByExternSheetIndex(externSheetIndex);
+       }
+
+       /**
+        * returns an appropriate Eval impl instance for the Ptg. The Ptg must be
+        * one of: Area3DPtg, AreaPtg, ReferencePtg, Ref3DPtg, IntPtg, NumberPtg,
+        * StringPtg, BoolPtg <br/>special Note: OperationPtg subtypes cannot be
+        * passed here!
+        */
+       private Eval getEvalForPtg(Ptg ptg, int sheetIndex, EvaluationTracker tracker) {
+               if (ptg instanceof NamePtg) {
+                       // named ranges, macro functions
+                       NamePtg namePtg = (NamePtg) ptg;
+                       EvaluationName nameRecord = _workbook.getName(namePtg);
+                       if (nameRecord.isFunctionName()) {
+                               return new NameEval(nameRecord.getNameText());
+                       }
+                       if (nameRecord.hasFormula()) {
+                               return evaluateNameFormula(nameRecord.getNameDefinition(), sheetIndex, tracker);
+                       }
+
+                       throw new RuntimeException("Don't now how to evalate name '" + nameRecord.getNameText() + "'");
+               }
+               if (ptg instanceof NameXPtg) {
+                       return new NameXEval(((NameXPtg) ptg));
+               }
+
+               if (ptg instanceof IntPtg) {
+                       return new NumberEval(((IntPtg)ptg).getValue());
+               }
+               if (ptg instanceof NumberPtg) {
+                       return new NumberEval(((NumberPtg)ptg).getValue());
+               }
+               if (ptg instanceof StringPtg) {
+                       return new StringEval(((StringPtg) ptg).getValue());
+               }
+               if (ptg instanceof BoolPtg) {
+                       return BoolEval.valueOf(((BoolPtg) ptg).getValue());
+               }
+               if (ptg instanceof ErrPtg) {
+                       return ErrorEval.valueOf(((ErrPtg) ptg).getErrorCode());
+               }
+               CellEvaluator ce = new CellEvaluator(this, tracker);
+               HSSFSheet sheet = _workbook.getSheet(sheetIndex);
+               if (ptg instanceof RefPtg) {
+                       return new LazyRefEval(((RefPtg) ptg), sheet, ce);
+               }
+               if (ptg instanceof AreaPtg) {
+                       return new LazyAreaEval(((AreaPtg) ptg), sheet, ce);
+               }
+               if (ptg instanceof Ref3DPtg) {
+                       Ref3DPtg refPtg = (Ref3DPtg) ptg;
+                       HSSFSheet xsheet = getOtherSheet(refPtg.getExternSheetIndex());
+                       return new LazyRefEval(refPtg, xsheet, ce);
+               }
+               if (ptg instanceof Area3DPtg) {
+                       Area3DPtg a3dp = (Area3DPtg) ptg;
+                       HSSFSheet xsheet = getOtherSheet(a3dp.getExternSheetIndex());
+                       return new LazyAreaEval(a3dp, xsheet, ce);
+               }
+
+               if (ptg instanceof UnknownPtg) {
+                       // POI uses UnknownPtg when the encoded Ptg array seems to be corrupted.
+                       // This seems to occur in very rare cases (e.g. unused name formulas in bug 44774, attachment 21790)
+                       // In any case, formulas are re-parsed before execution, so UnknownPtg should not get here
+                       throw new RuntimeException("UnknownPtg not allowed");
+               }
+
+               throw new RuntimeException("Unexpected ptg class (" + ptg.getClass().getName() + ")");
+       }
+       private Eval evaluateNameFormula(Ptg[] ptgs, int sheetIndex, EvaluationTracker tracker) {
+               if (ptgs.length > 1) {
+                       throw new RuntimeException("Complex name formulas not supported yet");
+               }
+               return getEvalForPtg(ptgs[0], sheetIndex, tracker);
+       }
+}
index a17414af749a76eaa617db7288a0309c1be6c56e..2c56ea57a17283c0c388ccbeab47743e0a011b41 100644 (file)
@@ -26,7 +26,7 @@ import junit.framework.TestCase;
 
 import org.apache.poi.hssf.HSSFTestDataSamples;
 import org.apache.poi.hssf.eventusermodel.EventWorkbookBuilder.SheetRecordCollectingListener;
-import org.apache.poi.hssf.model.FormulaParser;
+import org.apache.poi.hssf.model.HSSFFormulaParser;
 import org.apache.poi.hssf.model.Workbook;
 import org.apache.poi.hssf.record.FormulaRecord;
 import org.apache.poi.hssf.record.Record;
@@ -101,7 +101,7 @@ public final class TestEventWorkbookBuilder extends TestCase {
                
                // Check we can get the formula without breaking
                for(int i=0; i<fRecs.length; i++) {
-                       FormulaParser.toFormulaString(stubHSSF, fRecs[i].getParsedExpression());
+                       HSSFFormulaParser.toFormulaString(stubHSSF, fRecs[i].getParsedExpression());
                }
                
                // Peer into just one formula, and check that
@@ -123,19 +123,19 @@ public final class TestEventWorkbookBuilder extends TestCase {
                fr = fRecs[0];
                assertEquals(1, fr.getRow());
                assertEquals(0, fr.getColumn());
-               assertEquals("Sheet1!A1", FormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression()));
+               assertEquals("Sheet1!A1", HSSFFormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression()));
                
                // Sheet 1 A5 is to another sheet
                fr = fRecs[3];
                assertEquals(4, fr.getRow());
                assertEquals(0, fr.getColumn());
-               assertEquals("'S2'!A1", FormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression()));
+               assertEquals("'S2'!A1", HSSFFormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression()));
                
                // Sheet 1 A7 is to another sheet, range
                fr = fRecs[5];
                assertEquals(6, fr.getRow());
                assertEquals(0, fr.getColumn());
-               assertEquals("SUM(Sh3!A1:A4)", FormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression()));
+               assertEquals("SUM(Sh3!A1:A4)", HSSFFormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression()));
                
                
                // Now, load via Usermodel and re-check
index bb9f3de605343caf41b29e51a7b46aa590df2ce6..016fa8398c3a582315c829316f7bb1dfbe3fda77 100644 (file)
@@ -21,7 +21,6 @@ import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;
 
 import org.apache.poi.hssf.HSSFTestDataSamples;
-import org.apache.poi.hssf.model.FormulaParser.FormulaParseException;
 import org.apache.poi.hssf.record.constant.ErrorConstant;
 import org.apache.poi.hssf.record.formula.AbstractFunctionPtg;
 import org.apache.poi.hssf.record.formula.AddPtg;
@@ -56,6 +55,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.ss.formula.FormulaParserTestHelper;
 
 /**
  * Test the low level formula parser functionality. High level tests are to
@@ -67,10 +67,13 @@ public final class TestFormulaParser extends TestCase {
         * @return parsed token array already confirmed not <code>null</code>
         */
        /* package */ static Ptg[] parseFormula(String formula) {
-               Ptg[] result = FormulaParser.parse(formula, null);
+               Ptg[] result = HSSFFormulaParser.parse(formula, (HSSFWorkbook)null);
                assertNotNull("Ptg array should not be null", result);
                return result;
        }
+       private static String toFormulaString(Ptg[] ptgs) {
+               return HSSFFormulaParser.toFormulaString((HSSFWorkbook)null, ptgs);
+       }
 
        public void testSimpleFormula() {
                Ptg[] ptgs = parseFormula("2+2");
@@ -133,7 +136,7 @@ public final class TestFormulaParser extends TestCase {
                HSSFWorkbook w = HSSFTestDataSamples.openSampleWorkbook("testNames.xls");
                HSSFEvaluationWorkbook book = HSSFEvaluationWorkbook.create(w);
 
-               Ptg[] ptg = FormulaParser.parse("myFunc()", w);
+               Ptg[] ptg = HSSFFormulaParser.parse("myFunc()", w);
                // myFunc() actually takes 1 parameter.  Don't know if POI will ever be able to detect this problem
 
                // the name gets encoded as the first arg
@@ -405,7 +408,7 @@ public final class TestFormulaParser extends TestCase {
                Ptg[] ptgs = {
                                new FuncPtg(10),
                };
-               assertEquals("NA()", FormulaParser.toFormulaString(book, ptgs));
+               assertEquals("NA()", HSSFFormulaParser.toFormulaString(book, ptgs));
        }
 
        public void testPercent() {
@@ -639,11 +642,11 @@ public final class TestFormulaParser extends TestCase {
                String formulaString;
                Ptg[] ptgs;
                ptgs = parseFormula("sum(5, 2, if(3>2, sum(A1:A2), 6))");
-               formulaString = FormulaParser.toFormulaString(null, ptgs);
+               formulaString = toFormulaString(ptgs);
                assertEquals("SUM(5,2,IF(3>2,SUM(A1:A2),6))", formulaString);
 
                ptgs = parseFormula("if(1<2,sum(5, 2, if(3>2, sum(A1:A2), 6)),4)");
-               formulaString = FormulaParser.toFormulaString(null, ptgs);
+               formulaString = toFormulaString(ptgs);
                assertEquals("IF(1<2,SUM(5,2,IF(3>2,SUM(A1:A2),6)),4)", formulaString);
        }
        public void testParserErrors() {
@@ -665,12 +668,9 @@ public final class TestFormulaParser extends TestCase {
                try {
                        parseFormula(formula);
                        throw new AssertionFailedError("expected parse exception");
-               } catch (FormulaParseException e) {
-                       // expected during successful test
-                       assertNotNull(e.getMessage());
                } catch (RuntimeException e) {
-                       e.printStackTrace();
-                       fail("Wrong exception:" + e.getMessage());
+                       // expected during successful test
+                       FormulaParserTestHelper.confirmParseException(e);
                }
        }
 
@@ -697,7 +697,7 @@ public final class TestFormulaParser extends TestCase {
                Ptg[] ptgs = { spacePtg, new IntPtg(4), };
                String formulaString;
                try {
-                       formulaString = FormulaParser.toFormulaString(null, ptgs);
+                       formulaString = toFormulaString(ptgs);
                } catch (IllegalStateException e) {
                        if(e.getMessage().equalsIgnoreCase("too much stuff left on the stack")) {
                                throw new AssertionFailedError("Identified bug 44609");
@@ -709,7 +709,7 @@ public final class TestFormulaParser extends TestCase {
                assertEquals("4", formulaString);
 
                ptgs = new Ptg[] { new IntPtg(3), spacePtg, new IntPtg(4), spacePtg, AddPtg.instance, };
-               formulaString = FormulaParser.toFormulaString(null, ptgs);
+               formulaString = toFormulaString(ptgs);
                assertEquals("3+4", formulaString);
        }
 
@@ -725,7 +725,7 @@ public final class TestFormulaParser extends TestCase {
                                DividePtg.instance,
                };
                try {
-                       FormulaParser.toFormulaString(null, ptgs);
+                       toFormulaString(ptgs);
                        fail("Expected exception was not thrown");
                } catch (IllegalStateException e) {
                        // expected during successful test
@@ -764,10 +764,10 @@ public final class TestFormulaParser extends TestCase {
        private static void confirmArgCountMsg(String formula, String expectedMessage) {
                HSSFWorkbook book = new HSSFWorkbook();
                try {
-                       FormulaParser.parse(formula, book);
+                       HSSFFormulaParser.parse(formula, book);
                        throw new AssertionFailedError("Didn't get parse exception as expected");
-               } catch (FormulaParseException e) {
-                       assertEquals(expectedMessage, e.getMessage());
+               } catch (RuntimeException e) {
+                       FormulaParserTestHelper.confirmParseException(e, expectedMessage);
                }
        }
 
@@ -776,15 +776,17 @@ public final class TestFormulaParser extends TestCase {
                try {
                        parseFormula("round(3.14;2)");
                        throw new AssertionFailedError("Didn't get parse exception as expected");
-               } catch (FormulaParseException e) {
-                       assertEquals("Parse error near char 10 ';' in specified formula 'round(3.14;2)'. Expected ',' or ')'", e.getMessage());
+               } catch (RuntimeException e) {
+                       FormulaParserTestHelper.confirmParseException(e, 
+                                       "Parse error near char 10 ';' in specified formula 'round(3.14;2)'. Expected ',' or ')'");
                }
 
                try {
                        parseFormula(" =2+2");
                        throw new AssertionFailedError("Didn't get parse exception as expected");
-               } catch (FormulaParseException e) {
-                       assertEquals("The specified formula ' =2+2' starts with an equals sign which is not allowed.", e.getMessage());
+               } catch (RuntimeException e) {
+                       FormulaParserTestHelper.confirmParseException(e, 
+                                       "The specified formula ' =2+2' starts with an equals sign which is not allowed.");
                }
        }
        
@@ -819,7 +821,7 @@ public final class TestFormulaParser extends TestCase {
 
                Ptg[] ptgs;
                try {
-                       ptgs = FormulaParser.parse("count(pfy1)", wb);
+                       ptgs = HSSFFormulaParser.parse("count(pfy1)", wb);
                } catch (IllegalArgumentException e) {
                        if (e.getMessage().equals("Specified colIx (1012) is out of range")) {
                                throw new AssertionFailedError("Identified bug 45354");
@@ -835,10 +837,9 @@ public final class TestFormulaParser extends TestCase {
                try {
                        cell.setCellFormula("count(pf1)");
                        throw new AssertionFailedError("Expected formula parse execption");
-               } catch (FormulaParseException e) {
-                       if (!e.getMessage().equals("Specified named range 'pf1' does not exist in the current workbook.")) {
-                               throw e;
-                       }
+               } catch (RuntimeException e) {
+                       FormulaParserTestHelper.confirmParseException(e, 
+                                       "Specified named range 'pf1' does not exist in the current workbook.");
                }
                cell.setCellFormula("count(fp1)"); // plain cell ref, col is in range
        }
@@ -850,14 +851,14 @@ public final class TestFormulaParser extends TestCase {
                HSSFWorkbook book = new HSSFWorkbook();
                book.createSheet("Sheet1");
                
-               ptgs = FormulaParser.parse("Sheet1!A10:A40000", book);
+               ptgs = HSSFFormulaParser.parse("Sheet1!A10:A40000", book);
                aptg = (AreaI) ptgs[0];
                if (aptg.getLastRow() == -25537) {
                        throw new AssertionFailedError("Identified bug 45358");
                }
                assertEquals(39999, aptg.getLastRow());
                
-               ptgs = FormulaParser.parse("Sheet1!A10:A65536", book);
+               ptgs = HSSFFormulaParser.parse("Sheet1!A10:A65536", book);
                aptg = (AreaI) ptgs[0];
                assertEquals(65535, aptg.getLastRow());
                
index 2bede8e5f563b2b5216502e3428f2548ee7ab052..fbbdbd54dc70cd071b56f90cc3867debfeff8249 100644 (file)
@@ -17,9 +17,9 @@
 
 package org.apache.poi.hssf.model;
 
+import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;
 
-import org.apache.poi.hssf.model.FormulaParser.FormulaParseException;
 import org.apache.poi.hssf.record.formula.FuncVarPtg;
 import org.apache.poi.hssf.record.formula.NamePtg;
 import org.apache.poi.hssf.record.formula.Ptg;
@@ -30,6 +30,7 @@ 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.formula.FormulaParserTestHelper;
 
 /**
  * Test the low level formula parser functionality,
@@ -51,21 +52,21 @@ public final class TestFormulaParserEval extends TestCase {
                name.setNameName("testName");
                name.setReference("A1:A2");
 
-               ptgs = FormulaParser.parse("SUM(testName)", workbook);
+               ptgs = HSSFFormulaParser.parse("SUM(testName)", workbook);
                assertTrue("two tokens expected, got "+ptgs.length,ptgs.length == 2);
                assertEquals(NamePtg.class, ptgs[0].getClass());
                assertEquals(FuncVarPtg.class, ptgs[1].getClass());
 
                // Now make it a single cell
                name.setReference("C3");
-               ptgs = FormulaParser.parse("SUM(testName)", workbook);
+               ptgs = HSSFFormulaParser.parse("SUM(testName)", workbook);
                assertTrue("two tokens expected, got "+ptgs.length,ptgs.length == 2);
                assertEquals(NamePtg.class, ptgs[0].getClass());
                assertEquals(FuncVarPtg.class, ptgs[1].getClass());
                
                // And make it non-contiguous
                name.setReference("A1:A2,C3");
-               ptgs = FormulaParser.parse("SUM(testName)", workbook);
+               ptgs = HSSFFormulaParser.parse("SUM(testName)", workbook);
                assertTrue("two tokens expected, got "+ptgs.length,ptgs.length == 2);
                assertEquals(NamePtg.class, ptgs[0].getClass());
                assertEquals(FuncVarPtg.class, ptgs[1].getClass());
@@ -89,11 +90,12 @@ public final class TestFormulaParserEval extends TestCase {
                CellValue result;
                try {
                        result = fe.evaluate(cell);
-               } catch (FormulaParseException e) {
-                       if(e.getMessage().equals("Found reference to named range \"A\", but that named range wasn't defined!")) {
-                               fail("Identifed bug 44539");
+               } catch (RuntimeException e) {
+                       FormulaParserTestHelper.confirmParseException(e);
+                       if (!e.getMessage().equals("Found reference to named range \"A\", but that named range wasn't defined!")) {
+                               throw new AssertionFailedError("Identifed bug 44539");
                        }
-                       throw new RuntimeException(e);
+                       throw e;
                }
                assertEquals(HSSFCell.CELL_TYPE_NUMERIC, result.getCellType());
                assertEquals(42.0, result.getNumberValue(), 0.0);
index 47cfb574c1dd29ee093f89db321696420d081cc6..530c2b09c2d45e17297259dd9a6c6f2d09d96afa 100644 (file)
@@ -23,6 +23,7 @@ import junit.framework.TestCase;
 import org.apache.poi.hssf.record.formula.AbstractFunctionPtg;
 import org.apache.poi.hssf.record.formula.FuncVarPtg;
 import org.apache.poi.hssf.record.formula.Ptg;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 
 /**
  * Tests specific formula examples in <tt>OperandClassTransformer</tt>.
@@ -31,9 +32,15 @@ import org.apache.poi.hssf.record.formula.Ptg;
  */
 public final class TestOperandClassTransformer extends TestCase {
 
+       private static Ptg[] parseFormula(String formula) {
+               Ptg[] result = HSSFFormulaParser.parse(formula, (HSSFWorkbook)null);
+               assertNotNull("Ptg array should not be null", result);
+               return result;
+       }
+       
        public void testMdeterm() {
                String formula = "MDETERM(ABS(A1))";
-               Ptg[] ptgs = FormulaParser.parse(formula, null);
+               Ptg[] ptgs = parseFormula(formula);
 
                confirmTokenClass(ptgs, 0, Ptg.CLASS_ARRAY);
                confirmFuncClass(ptgs, 1, "ABS", Ptg.CLASS_ARRAY);
@@ -51,7 +58,7 @@ public final class TestOperandClassTransformer extends TestCase {
         */
        public void DISABLED_testIndexPi1() {
                String formula = "INDEX(PI(),1)";
-               Ptg[] ptgs = FormulaParser.parse(formula, null);
+               Ptg[] ptgs = parseFormula(formula);
 
                confirmFuncClass(ptgs, 1, "PI", Ptg.CLASS_ARRAY); // fails as of POI 3.1
                confirmFuncClass(ptgs, 2, "INDEX", Ptg.CLASS_VALUE);
@@ -63,7 +70,7 @@ public final class TestOperandClassTransformer extends TestCase {
         */
        public void testDirectOperandOfValueOperator() {
                String formula = "COUNT(A1*1)";
-               Ptg[] ptgs = FormulaParser.parse(formula, null);
+               Ptg[] ptgs = parseFormula(formula);
                if (ptgs[0].getPtgClass() == Ptg.CLASS_REF) {
                        throw new AssertionFailedError("Identified bug 45348");
                }
@@ -78,13 +85,13 @@ public final class TestOperandClassTransformer extends TestCase {
        public void testRtoV() {
 
                String formula = "lookup(A1, A3:A52, B3:B52)";
-               Ptg[] ptgs = FormulaParser.parse(formula, null);
+               Ptg[] ptgs = parseFormula(formula);
                confirmTokenClass(ptgs, 0, Ptg.CLASS_VALUE);
        }
        
        public void testComplexIRR_bug45041() {
                String formula = "(1+IRR(SUMIF(A:A,ROW(INDIRECT(MIN(A:A)&\":\"&MAX(A:A))),B:B),0))^365-1";
-               Ptg[] ptgs = FormulaParser.parse(formula, null);
+               Ptg[] ptgs = parseFormula(formula);
 
                FuncVarPtg rowFunc = (FuncVarPtg) ptgs[10];
                FuncVarPtg sumifFunc = (FuncVarPtg) ptgs[12];
index 1ec7c92ae9c608111d054d0bc9bda06f2fa0a7e7..eb69a5cd0b3c853535d4c0a12c57d03f749a451d 100644 (file)
@@ -82,7 +82,7 @@ public final class TestRVA extends TestCase {
 
        private void confirmCell(HSSFCell formulaCell, String formula, HSSFWorkbook wb) {
                Ptg[] excelPtgs = FormulaExtractor.getPtgs(formulaCell);
-               Ptg[] poiPtgs = FormulaParser.parse(formula, wb);
+               Ptg[] poiPtgs = HSSFFormulaParser.parse(formula, wb);
                int nExcelTokens = excelPtgs.length;
                int nPoiTokens = poiPtgs.length;
                if (nExcelTokens != nPoiTokens) {
index f5b80f6dac38fc7694a87f9ccdd0edada5c2dd65..84f68977480509b1ea61c1563e1336b2c0a63c91 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.poi.hssf.record.formula;
 
 import junit.framework.TestCase;
 
-import org.apache.poi.hssf.model.FormulaParser;
+import org.apache.poi.hssf.model.HSSFFormulaParser;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 
 /**
@@ -87,7 +87,7 @@ public final class TestAreaPtg extends TestCase {
        private static String shiftAllColumnsBy1(String  formula) {
                int letUsShiftColumn1By1Column=1;
                HSSFWorkbook wb = null;
-               Ptg[] ptgs = FormulaParser.parse(formula, wb);
+               Ptg[] ptgs = HSSFFormulaParser.parse(formula, wb);
                for(int i=0; i<ptgs.length; i++)
                {
                        Ptg ptg = ptgs[i];
@@ -98,7 +98,7 @@ public final class TestAreaPtg extends TestCase {
                                aptg.setLastColumn((short)(aptg.getLastColumn()+letUsShiftColumn1By1Column));
                        }
                }
-               String newFormula = FormulaParser.toFormulaString(wb, ptgs);
+               String newFormula = HSSFFormulaParser.toFormulaString(wb, ptgs);
                return newFormula;
        }
 }
index 53d51ca0f16c5cf73fbc6c2caae53e22f6e1f4de..7098dd1a71ebe4eca21f4aee12f2e98393a42b3d 100755 (executable)
@@ -24,7 +24,7 @@ import java.io.IOException;
 import junit.framework.TestCase;
 
 import org.apache.poi.hssf.HSSFTestDataSamples;
-import org.apache.poi.hssf.model.FormulaParser;
+import org.apache.poi.hssf.model.HSSFFormulaParser;
 import org.apache.poi.hssf.usermodel.HSSFCell;
 import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
 import org.apache.poi.hssf.usermodel.HSSFSheet;
@@ -52,7 +52,7 @@ public final class TestExternalFunctionFormulas extends TestCase {
        
        public void testParse() {
                HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("externalFunctionExample.xls");
-               Ptg[] ptgs = FormulaParser.parse("YEARFRAC(B1,C1)", wb);
+               Ptg[] ptgs = HSSFFormulaParser.parse("YEARFRAC(B1,C1)", wb);
                assertEquals(4, ptgs.length);
                assertEquals(NameXPtg.class, ptgs[0].getClass());
                
index 58ae55f26b6658fd46ab49c7b145185c178c0c37..ec9c3d1e8567a8b117c576df740f932f4e415c53 100644 (file)
@@ -17,7 +17,7 @@
 
 package org.apache.poi.hssf.record.formula;
 
-import org.apache.poi.hssf.model.FormulaParser;
+import org.apache.poi.hssf.model.HSSFFormulaParser;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 
 import junit.framework.AssertionFailedError;
@@ -36,7 +36,7 @@ public final class TestFuncVarPtg extends TestCase {
         */
        public void testOperandClass() {
                HSSFWorkbook book = new HSSFWorkbook();
-               Ptg[] ptgs = FormulaParser.parse("sum(A1:A2)", book);
+               Ptg[] ptgs = HSSFFormulaParser.parse("sum(A1:A2)", book);
                assertEquals(2, ptgs.length);
                assertEquals(AreaPtg.class, ptgs[0].getClass());
                
index 7030c5a508c0ca189a08780b4bd68e8a7e348847..95ad2ef53512069d8ab480d52b6ab63c881983bd 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.poi.hssf.record.formula.function;
 import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;
 
-import org.apache.poi.hssf.model.FormulaParser;
+import org.apache.poi.hssf.model.HSSFFormulaParser;
 import org.apache.poi.hssf.record.formula.AbstractFunctionPtg;
 import org.apache.poi.hssf.record.formula.FuncPtg;
 import org.apache.poi.hssf.record.formula.FuncVarPtg;
@@ -36,7 +36,7 @@ public final class TestParseMissingBuiltInFuncs extends TestCase {
 
        private static Ptg[] parse(String formula) {
                HSSFWorkbook book = new HSSFWorkbook();
-               return FormulaParser.parse(formula, book);
+               return HSSFFormulaParser.parse(formula, book);
        }
        private static void confirmFunc(String formula, int expPtgArraySize, boolean isVarArgFunc, int funcIx) {
                Ptg[] ptgs = parse(formula);
@@ -58,7 +58,7 @@ public final class TestParseMissingBuiltInFuncs extends TestCase {
                
                // check that parsed Ptg array converts back to formula text OK
                HSSFWorkbook book = new HSSFWorkbook();
-               String reRenderedFormula = FormulaParser.toFormulaString(book, ptgs);
+               String reRenderedFormula = HSSFFormulaParser.toFormulaString(book, ptgs);
                assertEquals(formula, reRenderedFormula);
        }
        
index 0c08ef5a543111abc26568d8a8d2c43f3cbdcc04..b93eb7a6c705736e8173fa9c5b8348ef838c5f4b 100644 (file)
@@ -76,7 +76,7 @@ public final class TestDate extends TestCase {
 
     private void confirm(String formulaText, double expectedResult) {
         cell11.setCellFormula(formulaText);
-        evaluator.clearCache();
+        evaluator.clearAllCachedResultValues();
         double actualValue = evaluator.evaluate(cell11).getNumberValue();
         assertEquals(expectedResult, actualValue, 0);
     }
diff --git a/src/testcases/org/apache/poi/ss/formula/FormulaParserTestHelper.java b/src/testcases/org/apache/poi/ss/formula/FormulaParserTestHelper.java
new file mode 100644 (file)
index 0000000..1440022
--- /dev/null
@@ -0,0 +1,48 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.ss.formula;\r
+\r
+import junit.framework.Assert;\r
+import junit.framework.AssertionFailedError;\r
+\r
+import org.apache.poi.ss.formula.FormulaParser;\r
+import org.apache.poi.ss.formula.FormulaParser.FormulaParseException;\r
+/**\r
+ * Avoids making {@link FormulaParser#FormulaParseException} public\r
+ * \r
+ * @author Josh Micich\r
+ */\r
+public class FormulaParserTestHelper {\r
+       public static void confirmParseException(RuntimeException e, String expectedMessage) {\r
+               checkType(e);\r
+               Assert.assertEquals(expectedMessage, e.getMessage());\r
+       }\r
+       public static void confirmParseException(RuntimeException e) {\r
+               checkType(e);\r
+               Assert.assertNotNull(e.getMessage());\r
+       }\r
+       private static void checkType(RuntimeException e) throws AssertionFailedError {\r
+               if (!(e instanceof FormulaParseException)) {\r
+               String failMsg = "Expected FormulaParseException, but got (" \r
+                       + e.getClass().getName() + "):";\r
+               System.err.println(failMsg);\r
+               e.printStackTrace();\r
+               throw new AssertionFailedError(failMsg);\r
+               }\r
+       }\r
+}\r