]> source.dussan.org Git - poi.git/commitdiff
Merged revisions 693591,693639,693658,693939,693941,693947,693990,694050,694065,69415...
authorNick Burch <nick@apache.org>
Sat, 13 Sep 2008 13:48:27 +0000 (13:48 +0000)
committerNick Burch <nick@apache.org>
Sat, 13 Sep 2008 13:48:27 +0000 (13:48 +0000)
https://svn.apache.org/repos/asf/poi/trunk

........
  r693591 | josh | 2008-09-09 21:25:16 +0100 (Tue, 09 Sep 2008) | 1 line

  Added support for parsing array constants in formulas. (Helping investigation for bug 45752)
........
  r693639 | josh | 2008-09-09 23:26:28 +0100 (Tue, 09 Sep 2008) | 1 line

  removed debug code accidentally submitted with r693591
........
  r693658 | josh | 2008-09-10 00:46:46 +0100 (Wed, 10 Sep 2008) | 2 lines

  Fixed special cases of INDEX function (single columns / single rows, and errors)
........
  r693939 | josh | 2008-09-10 20:23:43 +0100 (Wed, 10 Sep 2008) | 1 line

  Fixing error value handling for numeric functions. Refactored hierarchy.
........
  r693941 | josh | 2008-09-10 20:27:24 +0100 (Wed, 10 Sep 2008) | 1 line

  (Should have been submitted with 693939) Fixing error value handling for numeric functions. Refactored hierarchy.
........
  r693947 | josh | 2008-09-10 20:33:58 +0100 (Wed, 10 Sep 2008) | 1 line

  (Should have been submitted with 693939) Fixing error value handling for numeric functions. Refactored hierarchy.
........
  r693990 | josh | 2008-09-10 22:21:28 +0100 (Wed, 10 Sep 2008) | 1 line

  Refactored hierarchy of MultiOperandNumericFunction. Fixed error value handling.  Enabled error value check in TestFormulasFromSpreadsheet
........
  r694050 | josh | 2008-09-10 23:43:30 +0100 (Wed, 10 Sep 2008) | 1 line

  Refactored finance functions.
........
  r694065 | josh | 2008-09-11 00:37:22 +0100 (Thu, 11 Sep 2008) | 1 line

  fixed special cases of MODE function
........
  r694153 | josh | 2008-09-11 08:16:20 +0100 (Thu, 11 Sep 2008) | 1 line

  Refactoring MultiOperandNumericFunction - removed Ref2DEval.
........
  r694534 | josh | 2008-09-12 00:18:50 +0100 (Fri, 12 Sep 2008) | 1 line

  Fix for bug 45639 - cleaned up index logic inside ColumnInfoRecordsAggregate
........
  r694615 | josh | 2008-09-12 07:14:07 +0100 (Fri, 12 Sep 2008) | 1 line

  small tweak to unit test which was silently creating UnknownPtgs
........
  r694619 | josh | 2008-09-12 07:58:52 +0100 (Fri, 12 Sep 2008) | 1 line

  Removed trailing comma from output of HexDump.toHex()
........
  r694620 | josh | 2008-09-12 08:03:00 +0100 (Fri, 12 Sep 2008) | 1 line

  clarification of ArrayPtg size increment
........
  r694631 | josh | 2008-09-12 08:43:20 +0100 (Fri, 12 Sep 2008) | 1 line

  Extended support for cached results of formula cells
........
  r694643 | josh | 2008-09-12 09:18:54 +0100 (Fri, 12 Sep 2008) | 2 lines

  Made HSSFFormulaEvaluator no longer require initialisation with sheet or row.
........
  r694877 | josh | 2008-09-13 06:14:26 +0100 (Sat, 13 Sep 2008) | 1 line

  Refactored TextFunctions. Some minor fixes - test cases added.
........
  r694881 | josh | 2008-09-13 06:43:41 +0100 (Sat, 13 Sep 2008) | 1 line

  Added toString methods formatAsString to CellValue. Changed deprecation on CellValue.getRichTextStringValue
........

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

154 files changed:
src/documentation/content/xdocs/changes.xml
src/documentation/content/xdocs/status.xml
src/java/org/apache/poi/hssf/extractor/EventBasedExcelExtractor.java
src/java/org/apache/poi/hssf/extractor/ExcelExtractor.java
src/java/org/apache/poi/hssf/model/FormulaParser.java
src/java/org/apache/poi/hssf/model/OperandClassTransformer.java
src/java/org/apache/poi/hssf/model/Sheet.java
src/java/org/apache/poi/hssf/record/ColumnInfoRecord.java
src/java/org/apache/poi/hssf/record/FormulaRecord.java
src/java/org/apache/poi/hssf/record/RecordInputStream.java
src/java/org/apache/poi/hssf/record/aggregates/ColumnInfoRecordsAggregate.java
src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java
src/java/org/apache/poi/hssf/record/formula/Area2DPtgBase.java
src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java
src/java/org/apache/poi/hssf/record/formula/ErrPtg.java
src/java/org/apache/poi/hssf/record/formula/Ptg.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/ConcatEval.java
src/java/org/apache/poi/hssf/record/formula/eval/ExternalFunction.java
src/java/org/apache/poi/hssf/record/formula/eval/FunctionEval.java
src/java/org/apache/poi/hssf/record/formula/eval/LazyAreaEval.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/eval/LazyRefEval.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/eval/OperandResolver.java
src/java/org/apache/poi/hssf/record/formula/eval/Ref2DEval.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/eval/ValueEvalToNumericXlator.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/AggregateFunction.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/record/formula/functions/Atan2.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Avedev.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Average.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Ceiling.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Combin.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Concatenate.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Date.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/DateFunc.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/record/formula/functions/Devsq.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Even.java
src/java/org/apache/poi/hssf/record/formula/functions/Exact.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/FinanceFunction.java
src/java/org/apache/poi/hssf/record/formula/functions/Floor.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/FreeRefFunction.java
src/java/org/apache/poi/hssf/record/formula/functions/Fv.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Index.java
src/java/org/apache/poi/hssf/record/formula/functions/Indirect.java
src/java/org/apache/poi/hssf/record/formula/functions/Large.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Left.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Len.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Log.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Lower.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Max.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Maxa.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Median.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Mid.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Min.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Mina.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/MinaMaxa.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/record/formula/functions/Mod.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Mode.java
src/java/org/apache/poi/hssf/record/formula/functions/MultiOperandNumericFunction.java
src/java/org/apache/poi/hssf/record/formula/functions/Nper.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/NumericFunction.java
src/java/org/apache/poi/hssf/record/formula/functions/NumericFunctionOneArg.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Odd.java
src/java/org/apache/poi/hssf/record/formula/functions/Pmt.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Power.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Product.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Pv.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Replace.java
src/java/org/apache/poi/hssf/record/formula/functions/Right.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Round.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Rounddown.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Roundup.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Small.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/StatsLib.java
src/java/org/apache/poi/hssf/record/formula/functions/Stdev.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Substitute.java
src/java/org/apache/poi/hssf/record/formula/functions/Sum.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Sumsq.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/TextFunction.java
src/java/org/apache/poi/hssf/record/formula/functions/Trim.java [deleted file]
src/java/org/apache/poi/hssf/record/formula/functions/Upper.java [deleted file]
src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java
src/java/org/apache/poi/hssf/usermodel/HSSFRow.java
src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
src/java/org/apache/poi/hssf/usermodel/LazyAreaEval.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/usermodel/LazyRefEval.java [new file with mode: 0644]
src/java/org/apache/poi/ss/usermodel/EvaluationCycleDetector.java
src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java
src/java/org/apache/poi/ss/usermodel/LazyAreaEval.java [new file with mode: 0644]
src/java/org/apache/poi/ss/usermodel/LazyRefEval.java [new file with mode: 0644]
src/java/org/apache/poi/util/HexDump.java
src/java/org/apache/poi/util/HexRead.java
src/ooxml/interfaces-jdk14/org/apache/poi/ss/usermodel/Cell.java
src/ooxml/interfaces-jdk14/org/apache/poi/ss/usermodel/Comment.java
src/ooxml/interfaces-jdk15/org/apache/poi/ss/usermodel/Cell.java
src/testcases/org/apache/poi/ddf/TestEscherBSERecord.java
src/testcases/org/apache/poi/ddf/TestEscherBlipWMFRecord.java
src/testcases/org/apache/poi/ddf/TestEscherChildAnchorRecord.java
src/testcases/org/apache/poi/ddf/TestEscherClientAnchorRecord.java
src/testcases/org/apache/poi/ddf/TestEscherClientDataRecord.java
src/testcases/org/apache/poi/ddf/TestEscherContainerRecord.java
src/testcases/org/apache/poi/ddf/TestEscherDgRecord.java
src/testcases/org/apache/poi/ddf/TestEscherDggRecord.java
src/testcases/org/apache/poi/ddf/TestEscherOptRecord.java
src/testcases/org/apache/poi/ddf/TestEscherPropertyFactory.java
src/testcases/org/apache/poi/ddf/TestEscherSpRecord.java
src/testcases/org/apache/poi/ddf/TestEscherSpgrRecord.java
src/testcases/org/apache/poi/ddf/TestEscherSplitMenuColorsRecord.java
src/testcases/org/apache/poi/ddf/TestUnknownEscherRecord.java
src/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls
src/testcases/org/apache/poi/hssf/data/IndexFunctionTestCaseData.xls [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/data/testRVA.xls
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/TestRVA.java
src/testcases/org/apache/poi/hssf/model/TestSheet.java
src/testcases/org/apache/poi/hssf/model/TestSheetAdditional.java
src/testcases/org/apache/poi/hssf/record/TestDrawingGroupRecord.java
src/testcases/org/apache/poi/hssf/record/TestEscherAggregate.java
src/testcases/org/apache/poi/hssf/record/TestFormulaRecord.java
src/testcases/org/apache/poi/hssf/record/aggregates/TestColumnInfoRecordsAggregate.java
src/testcases/org/apache/poi/hssf/record/aggregates/TestFormulaRecordAggregate.java
src/testcases/org/apache/poi/hssf/record/formula/TestArrayPtg.java
src/testcases/org/apache/poi/hssf/record/formula/TestExternalFunctionFormulas.java
src/testcases/org/apache/poi/hssf/record/formula/atp/TestYearFracCalculatorFromSpreadsheet.java
src/testcases/org/apache/poi/hssf/record/formula/eval/TestCircularReferences.java
src/testcases/org/apache/poi/hssf/record/formula/eval/TestExternalFunction.java
src/testcases/org/apache/poi/hssf/record/formula/eval/TestFormulaBugs.java
src/testcases/org/apache/poi/hssf/record/formula/eval/TestFormulasFromSpreadsheet.java
src/testcases/org/apache/poi/hssf/record/formula/eval/TestPercentEval.java
src/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java
src/testcases/org/apache/poi/hssf/record/formula/functions/TestAverage.java
src/testcases/org/apache/poi/hssf/record/formula/functions/TestCountFuncs.java
src/testcases/org/apache/poi/hssf/record/formula/functions/TestDate.java
src/testcases/org/apache/poi/hssf/record/formula/functions/TestIndex.java
src/testcases/org/apache/poi/hssf/record/formula/functions/TestIndexFunctionFromSpreadsheet.java [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/record/formula/functions/TestIsBlank.java
src/testcases/org/apache/poi/hssf/record/formula/functions/TestLen.java
src/testcases/org/apache/poi/hssf/record/formula/functions/TestLookupFunctionsFromSpreadsheet.java
src/testcases/org/apache/poi/hssf/record/formula/functions/TestMid.java
src/testcases/org/apache/poi/hssf/record/formula/functions/TestPmt.java
src/testcases/org/apache/poi/hssf/record/formula/functions/TestRoundFuncs.java
src/testcases/org/apache/poi/hssf/record/formula/functions/TestStatsLib.java
src/testcases/org/apache/poi/hssf/record/formula/functions/TestTrim.java
src/testcases/org/apache/poi/hssf/usermodel/TestBug42464.java
src/testcases/org/apache/poi/hssf/usermodel/TestBug43093.java
src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java
src/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorBugs.java
src/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorDocs.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDataFormatter.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFFormulaEvaluator.java

index c1152ef3440df6c3481abd4ac0bc7b083ffb5493..6025906757e64bc1e0c14111427200544b39d27d 100644 (file)
            <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
         </release>
         <release version="3.1.1-alpha1" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="add">Made HSSFFormulaEvaluator no longer require initialisation with sheet or row</action>
+           <action dev="POI-DEVELOPERS" type="add">Extended support for cached results of formula cells</action>
+           <action dev="POI-DEVELOPERS" type="fix">45639 - Fixed AIOOBE due to bad index logic in ColumnInfoRecordsAggregate</action>
+           <action dev="POI-DEVELOPERS" type="fix">Fixed special cases of INDEX function (single column/single row, errors)</action>
            <action dev="POI-DEVELOPERS" type="add">45761 - Support for Very Hidden excel sheets in HSSF</action>
            <action dev="POI-DEVELOPERS" type="add">45738 - Initial HWPF support for Office Art Shapes</action>
            <action dev="POI-DEVELOPERS" type="fix">45720 - Fixed HSSFWorkbook.cloneSheet to correctly clone sheets with drawings</action>
index 155d522d7b9037a4a18a9a20e9518a89b147c223..9eaab3dad1f9a206f2cbcdcc188786b8e3e93b2e 100644 (file)
            <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
         </release>
         <release version="3.1.1-alpha1" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="add">Made HSSFFormulaEvaluator no longer require initialisation with sheet or row</action>
+           <action dev="POI-DEVELOPERS" type="add">Extended support for cached results of formula cells</action>
+           <action dev="POI-DEVELOPERS" type="fix">45639 - Fixed AIOOBE due to bad index logic in ColumnInfoRecordsAggregate</action>
+           <action dev="POI-DEVELOPERS" type="fix">Fixed special cases of INDEX function (single column/single row, errors)</action>
            <action dev="POI-DEVELOPERS" type="add">45761 - Support for Very Hidden excel sheets in HSSF</action>
            <action dev="POI-DEVELOPERS" type="add">45738 - Initial HWPF support for Office Art Shapes</action>
            <action dev="POI-DEVELOPERS" type="fix">45720 - Fixed HSSFWorkbook.cloneSheet to correctly clone sheets with drawings</action>
index 8f3eebb2d35b43fb2c999edf6ab4b0ccae8f630c..2ea35c773ed8a969490c8679035fbd15670f262a 100644 (file)
@@ -14,6 +14,7 @@
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
+
 package org.apache.poi.hssf.extractor;
 
 import java.io.IOException;
@@ -49,10 +50,10 @@ import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 /**
  * A text extractor for Excel files, that is based
  *  on the hssf eventusermodel api.
- * It will typically use less memory than 
+ * It will typically use less memory than
  *  {@link ExcelExtractor}, but may not provide
  *  the same richness of formatting.
- * Returns the textual content of the file, suitable for 
+ * Returns the textual content of the file, suitable for
  *  indexing by something like Lucene, but not really
  *  intended for display to the user.
  * To turn an excel file into a CSV or similar, then see
@@ -63,8 +64,8 @@ public class EventBasedExcelExtractor extends POIOLE2TextExtractor {
        private POIFSFileSystem fs;
        private boolean includeSheetNames = true;
        private boolean formulasNotResults = false;
-       
-       public EventBasedExcelExtractor(POIFSFileSystem fs) throws IOException {
+
+       public EventBasedExcelExtractor(POIFSFileSystem fs) {
                super(null);
                this.fs = fs;
        }
@@ -98,8 +99,8 @@ public class EventBasedExcelExtractor extends POIOLE2TextExtractor {
        public void setFormulasNotResults(boolean formulasNotResults) {
                this.formulasNotResults = formulasNotResults;
        }
-       
-       
+
+
        /**
         * Retreives the text contents of the file
         */
@@ -107,7 +108,7 @@ public class EventBasedExcelExtractor extends POIOLE2TextExtractor {
                String text = null;
                try {
                        TextListener tl = triggerExtraction();
-                       
+
                        text = tl.text.toString();
                        if(! text.endsWith("\n")) {
                                text = text + "\n";
@@ -115,37 +116,37 @@ public class EventBasedExcelExtractor extends POIOLE2TextExtractor {
                } catch(IOException e) {
                        throw new RuntimeException(e);
                }
-               
+
                return text;
        }
-       
+
        private TextListener triggerExtraction() throws IOException {
                TextListener tl = new TextListener();
                FormatTrackingHSSFListener ft = new FormatTrackingHSSFListener(tl);
                tl.ft = ft;
-               
+
                // Register and process
                HSSFEventFactory factory = new HSSFEventFactory();
                HSSFRequest request = new HSSFRequest();
                request.addListenerForAllRecords(ft);
-               
+
                factory.processWorkbookEvents(request, fs);
-               
+
                return tl;
        }
-       
+
        private class TextListener implements HSSFListener {
                private FormatTrackingHSSFListener ft;
                private SSTRecord sstRecord;
-               
+
                private List sheetNames = new ArrayList();
                private StringBuffer text = new StringBuffer();
                private int sheetNum = -1;
                private int rowNum;
-               
+
                private boolean outputNextStringValue = false;
                private int nextRow = -1;
-               
+
                public void processRecord(Record record) {
                        String thisText = null;
                        int thisRow = -1;
@@ -160,7 +161,7 @@ public class EventBasedExcelExtractor extends POIOLE2TextExtractor {
                                if(bof.getType() == BOFRecord.TYPE_WORKSHEET) {
                                        sheetNum++;
                                        rowNum = -1;
-                                       
+
                                        if(includeSheetNames) {
                                                if(text.length() > 0) text.append("\n");
                                                text.append(sheetNames.get(sheetNum));
@@ -170,60 +171,60 @@ public class EventBasedExcelExtractor extends POIOLE2TextExtractor {
                        case SSTRecord.sid:
                                sstRecord = (SSTRecord)record;
                                break;
-                       
-               case FormulaRecord.sid:
-                       FormulaRecord frec = (FormulaRecord) record;
-                       thisRow = frec.getRow();
-                       
-                       if(formulasNotResults) {
-                               thisText = FormulaParser.toFormulaString(null, frec.getParsedExpression());
-                       } else {
-                               if(Double.isNaN( frec.getValue() )) {
-                                       // Formula result is a string
-                                       // This is stored in the next record
-                                       outputNextStringValue = true;
-                               nextRow = frec.getRow();
-                               } else {
-                                       thisText = formatNumberDateCell(frec, frec.getValue());
-                               }
-                       }
-                   break;
-               case StringRecord.sid:
-                       if(outputNextStringValue) {
-                               // String for formula
-                               StringRecord srec = (StringRecord)record;
-                               thisText = srec.getString(); 
-                               thisRow = nextRow;
-                               outputNextStringValue = false;
-                       }
-                   break;
-               case LabelRecord.sid:
-                       LabelRecord lrec = (LabelRecord) record;
-                   thisRow = lrec.getRow();
-                   thisText = lrec.getValue();
-                   break;
-               case LabelSSTRecord.sid:
-                       LabelSSTRecord lsrec = (LabelSSTRecord) record;
-                   thisRow = lsrec.getRow();
-                   if(sstRecord == null) {
-                       throw new IllegalStateException("No SST record found");
-                   }
-                   thisText = sstRecord.getString(lsrec.getSSTIndex()).toString();
-                   break;
-               case NoteRecord.sid:
-                       NoteRecord nrec = (NoteRecord) record;
-                       thisRow = nrec.getRow();
-                       // TODO: Find object to match nrec.getShapeId()
-                   break;
-               case NumberRecord.sid:
-                   NumberRecord numrec = (NumberRecord) record;
-                   thisRow = numrec.getRow();
-                   thisText = formatNumberDateCell(numrec, numrec.getValue());
-                   break;
-               default:
-                       break;
+
+                       case FormulaRecord.sid:
+                               FormulaRecord frec = (FormulaRecord) record;
+                               thisRow = frec.getRow();
+
+                               if(formulasNotResults) {
+                                       thisText = FormulaParser.toFormulaString(null, frec.getParsedExpression());
+                               } else {
+                                       if(frec.hasCachedResultString()) {
+                                               // Formula result is a string
+                                               // This is stored in the next record
+                                               outputNextStringValue = true;
+                                               nextRow = frec.getRow();
+                                       } else {
+                                               thisText = formatNumberDateCell(frec, frec.getValue());
+                                       }
+                               }
+                               break;
+                       case StringRecord.sid:
+                               if(outputNextStringValue) {
+                                       // String for formula
+                                       StringRecord srec = (StringRecord)record;
+                                       thisText = srec.getString();
+                                       thisRow = nextRow;
+                                       outputNextStringValue = false;
+                               }
+                               break;
+                       case LabelRecord.sid:
+                               LabelRecord lrec = (LabelRecord) record;
+                               thisRow = lrec.getRow();
+                               thisText = lrec.getValue();
+                               break;
+                       case LabelSSTRecord.sid:
+                               LabelSSTRecord lsrec = (LabelSSTRecord) record;
+                               thisRow = lsrec.getRow();
+                               if(sstRecord == null) {
+                                       throw new IllegalStateException("No SST record found");
+                               }
+                               thisText = sstRecord.getString(lsrec.getSSTIndex()).toString();
+                               break;
+                       case NoteRecord.sid:
+                               NoteRecord nrec = (NoteRecord) record;
+                               thisRow = nrec.getRow();
+                               // TODO: Find object to match nrec.getShapeId()
+                               break;
+                       case NumberRecord.sid:
+                               NumberRecord numrec = (NumberRecord) record;
+                               thisRow = numrec.getRow();
+                               thisText = formatNumberDateCell(numrec, numrec.getValue());
+                               break;
+                       default:
+                               break;
                        }
-                       
+
                        if(thisText != null) {
                                if(thisRow != rowNum) {
                                        rowNum = thisRow;
@@ -235,42 +236,42 @@ public class EventBasedExcelExtractor extends POIOLE2TextExtractor {
                                text.append(thisText);
                        }
                }
-               
+
                /**
-                * Formats a number or date cell, be that a real number, or the 
+                * Formats a number or date cell, be that a real number, or the
                 *  answer to a formula
                 */
                private String formatNumberDateCell(CellValueRecordInterface cell, double value) {
-               // Get the built in format, if there is one
+                       // Get the built in format, if there is one
                        int formatIndex = ft.getFormatIndex(cell);
                        String formatString = ft.getFormatString(cell);
-                       
+
                        if(formatString == null) {
-                   return Double.toString(value);
-               } else {
-                       // Is it a date?
-                       if(HSSFDateUtil.isADateFormat(formatIndex,formatString) &&
-                                       HSSFDateUtil.isValidExcelDate(value)) {
-                               // Java wants M not m for month
-                               formatString = formatString.replace('m','M');
-                               // Change \- into -, if it's there
-                               formatString = formatString.replaceAll("\\\\-","-");
-                               
-                               // Format as a date
-                               Date d = HSSFDateUtil.getJavaDate(value, false);
-                               DateFormat df = new SimpleDateFormat(formatString);
-                           return df.format(d);
-                       } else {
-                               if(formatString == "General") {
-                                       // Some sort of wierd default
-                                       return Double.toString(value);
-                               }
-                               
-                               // Format as a number
-                           DecimalFormat df = new DecimalFormat(formatString);
-                           return df.format(value);
-                       }
-               }
+                               return Double.toString(value);
+                       } else {
+                               // Is it a date?
+                               if(HSSFDateUtil.isADateFormat(formatIndex,formatString) &&
+                                               HSSFDateUtil.isValidExcelDate(value)) {
+                                       // Java wants M not m for month
+                                       formatString = formatString.replace('m','M');
+                                       // Change \- into -, if it's there
+                                       formatString = formatString.replaceAll("\\\\-","-");
+
+                                       // Format as a date
+                                       Date d = HSSFDateUtil.getJavaDate(value, false);
+                                       DateFormat df = new SimpleDateFormat(formatString);
+                                       return df.format(d);
+                               } else {
+                                       if(formatString == "General") {
+                                               // Some sort of wierd default
+                                               return Double.toString(value);
+                                       }
+
+                                       // Format as a number
+                                       DecimalFormat df = new DecimalFormat(formatString);
+                                       return df.format(value);
+                               }
+                       }
                }
        }
 }
index b9750fc58a98781af32795fd8d00923fb2daed39..17bde6da5f68654d39affe64a6011b6f72eb28aa 100644 (file)
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
+
 package org.apache.poi.hssf.extractor;
 
 import java.io.IOException;
 
 import org.apache.poi.POIOLE2TextExtractor;
 import org.apache.poi.ss.usermodel.HeaderFooter;
+import org.apache.poi.hssf.record.formula.eval.ErrorEval;
 import org.apache.poi.hssf.usermodel.HSSFCell;
 import org.apache.poi.hssf.usermodel.HSSFComment;
 import org.apache.poi.hssf.usermodel.HSSFRichTextString;
@@ -112,40 +114,52 @@ public class ExcelExtractor extends POIOLE2TextExtractor implements org.apache.p
                                int lastCell = row.getLastCellNum();
                                for(int k=firstCell;k<lastCell;k++) {
                                        HSSFCell cell = row.getCell(k);
-                                       boolean outputContents = false;
                                        if(cell == null) { continue; }
+                                       boolean outputContents = true;
                                        
                                        switch(cell.getCellType()) {
+                                               case HSSFCell.CELL_TYPE_BLANK:
+                                                       outputContents = false;
+                                                       break;
                                                case HSSFCell.CELL_TYPE_STRING:
                                                        text.append(cell.getRichStringCellValue().getString());
-                                                       outputContents = true;
                                                        break;
                                                case HSSFCell.CELL_TYPE_NUMERIC:
                                                        // Note - we don't apply any formatting!
                                                        text.append(cell.getNumericCellValue());
-                                                       outputContents = true;
                                                        break;
                                                case HSSFCell.CELL_TYPE_BOOLEAN:
                                                        text.append(cell.getBooleanCellValue());
-                                                       outputContents = true;
+                                                       break;
+                                               case HSSFCell.CELL_TYPE_ERROR:
+                                                       text.append(ErrorEval.getText(cell.getErrorCellValue()));
                                                        break;
                                                case HSSFCell.CELL_TYPE_FORMULA:
                                                        if(formulasNotResults) {
                                                                text.append(cell.getCellFormula());
                                                        } else {
-                                                               // Try it as a string, if not as a number
-                                                               HSSFRichTextString str = 
-                                                                       cell.getRichStringCellValue();
-                                                               if(str != null && str.length() > 0) {
-                                                                       text.append(str.toString());
-                                                               } else {
-                                                                       // Try and treat it as a number
-                                                                       double val = cell.getNumericCellValue();
-                                                                       text.append(val);
+                                                               switch(cell.getCachedFormulaResultType()) {
+                                                                       case HSSFCell.CELL_TYPE_STRING:
+                                                                               HSSFRichTextString str = cell.getRichStringCellValue();
+                                                                               if(str != null && str.length() > 0) {
+                                                                                       text.append(str.toString());
+                                                                               }
+                                                                               break;
+                                                                       case HSSFCell.CELL_TYPE_NUMERIC:
+                                                                               text.append(cell.getNumericCellValue());
+                                                                               break;
+                                                                       case HSSFCell.CELL_TYPE_BOOLEAN:
+                                                                               text.append(cell.getBooleanCellValue());
+                                                                               break;
+                                                                       case HSSFCell.CELL_TYPE_ERROR:
+                                                                               text.append(ErrorEval.getText(cell.getErrorCellValue()));
+                                                                               break;
+                                                                               
                                                                }
                                                        }
-                                                       outputContents = true;
                                                        break;
+                                               default:
+                                                       throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
                                        }
                                        
                                        // Output the comment, if requested and exists
index 5fd30d06e04170ecb2c50a357f40b7b9ae89a154..32f38d40d1d75f35a402aeef436e2f45c2e0003e 100644 (file)
@@ -22,11 +22,14 @@ import java.util.List;
 import java.util.Stack;
 
 //import PTGs .. since we need everything, import *
+import org.apache.poi.hssf.record.UnicodeString;
+import org.apache.poi.hssf.record.constant.ErrorConstant;
 import org.apache.poi.hssf.record.formula.*;
 import org.apache.poi.hssf.record.formula.function.FunctionMetadata;
 import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
 import org.apache.poi.ss.usermodel.Name;
 import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
 import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 import org.apache.poi.hssf.util.AreaReference;
@@ -70,9 +73,9 @@ public final class FormulaParser {
     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 
+    // 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;    
+    public static final int FORMULA_TYPE_DATAVALIDATION_LIST = 5;
 
     private final String formulaString;
     private final int formulaLength;
@@ -140,9 +143,9 @@ public final class FormulaParser {
     /** 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 
+            msg = "The specified formula '" + formulaString
                 + "' starts with an equals sign which is not allowed.";
         } else {
             msg = "Parse error near char " + (pointer-1) + " '" + look + "'"
@@ -194,8 +197,8 @@ public final class FormulaParser {
     /**
      * 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 
+     * 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() {
@@ -251,7 +254,7 @@ public final class FormulaParser {
     }
 
     private Ptg parseNameOrReference(String name) {
-        
+
         AreaReference areaRef = parseArea(name);
         if (areaRef != null) {
             // will happen if dots are used instead of colon
@@ -373,30 +376,28 @@ public final class FormulaParser {
     private ParseNode function(String name) {
         Ptg nameToken = null;
         if(!AbstractFunctionPtg.isBuiltInFunctionName(name)) {
-               // user defined function
+            // user defined function
             // in the token tree, the name is more or less the first argument
-               
-        
-               int nameIndex = book.getNameIndex(name);
-               if (nameIndex >= 0) {
-                       Name 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(name, this.book);
-               } else {
+            int nameIndex = book.getNameIndex(name);
+            if (nameIndex >= 0) {
+                Name 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(name, this.book);
+            } else {
                                if(book instanceof HSSFWorkbook) {
                                        nameToken = ((HSSFWorkbook)book).getNameXPtg(name);
                                }
-                       if (nameToken == null) {
-                               throw new FormulaParseException("Name '" + name 
-                                               + "' is completely unknown in the current workbook");
-                       }
-               }
+                if (nameToken == null) {
+                    throw new FormulaParseException("Name '" + name
+                            + "' is completely unknown in the current workbook");
+                }
+            }
         }
 
         Match('(');
@@ -544,7 +545,7 @@ public final class FormulaParser {
         SkipWhite();
         switch(look) {
             case '#':
-                return new ParseNode(parseErrorLiteral());
+                return new ParseNode(ErrPtg.valueOf(parseErrorLiteral()));
             case '-':
                 Match('-');
                 return new ParseNode(UnaryMinusPtg.instance, powerFactor());
@@ -557,7 +558,12 @@ public final class FormulaParser {
                 Match(')');
                 return new ParseNode(ParenthesisPtg.instance, inside);
             case '"':
-                return new ParseNode(parseStringLiteral());
+                return new ParseNode(new StringPtg(parseStringLiteral()));
+            case '{':
+                Match('{');
+                ParseNode arrayNode = parseArray();
+                Match('}');
+                return arrayNode;
         }
         if (IsAlpha(look) || look == '\''){
             return parseFunctionReferenceOrName();
@@ -567,6 +573,95 @@ public final class FormulaParser {
     }
 
 
+    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;
@@ -603,7 +698,7 @@ public final class FormulaParser {
     }
 
 
-    private ErrPtg parseErrorLiteral() {
+    private int parseErrorLiteral() {
         Match('#');
         String part1 = parseIdentifier().toUpperCase();
 
@@ -611,13 +706,13 @@ public final class FormulaParser {
             case 'V':
                 if(part1.equals("VALUE")) {
                     Match('!');
-                    return ErrPtg.VALUE_INVALID;
+                    return HSSFErrorConstants.ERROR_VALUE;
                 }
                 throw expected("#VALUE!");
             case 'R':
                 if(part1.equals("REF")) {
                     Match('!');
-                    return ErrPtg.REF_INVALID;
+                    return HSSFErrorConstants.ERROR_REF;
                 }
                 throw expected("#REF!");
             case 'D':
@@ -625,21 +720,21 @@ public final class FormulaParser {
                     Match('/');
                     Match('0');
                     Match('!');
-                    return ErrPtg.DIV_ZERO;
+                    return HSSFErrorConstants.ERROR_DIV_0;
                 }
                 throw expected("#DIV/0!");
             case 'N':
                 if(part1.equals("NAME")) {
                     Match('?');  // only one that ends in '?'
-                    return ErrPtg.NAME_INVALID;
+                    return HSSFErrorConstants.ERROR_NAME;
                 }
                 if(part1.equals("NUM")) {
                     Match('!');
-                    return ErrPtg.NUM_ERROR;
+                    return HSSFErrorConstants.ERROR_NUM;
                 }
                 if(part1.equals("NULL")) {
                     Match('!');
-                    return ErrPtg.NULL_INTERSECTION;
+                    return HSSFErrorConstants.ERROR_NULL;
                 }
                 if(part1.equals("N")) {
                     Match('/');
@@ -648,7 +743,7 @@ public final class FormulaParser {
                     }
                     Match(look);
                     // Note - no '!' or '?' suffix
-                    return ErrPtg.N_A;
+                    return HSSFErrorConstants.ERROR_NA;
                 }
                 throw expected("#NAME?, #NUM!, #NULL! or #N/A");
 
@@ -701,7 +796,7 @@ public final class FormulaParser {
     }
 
 
-    private StringPtg parseStringLiteral() {
+    private String parseStringLiteral() {
         Match('"');
 
         StringBuffer token = new StringBuffer();
@@ -715,7 +810,7 @@ public final class FormulaParser {
             token.append(look);
             GetChar();
         }
-        return new StringPtg(token.toString());
+        return token.toString();
     }
 
     /** Parse and Translate a Math Term */
@@ -972,7 +1067,7 @@ end;
         }
         return result;
     }
-    
+
     private static String[] getOperands(Stack stack, int nOperands) {
         String[] operands = new String[nOperands];
 
index 9b5804f0c4a4ab2e8b845cd681424758640e6635..8b7b56638b6ee4397d4d5a01ef5a12a4a0fb83a4 100644 (file)
@@ -74,7 +74,7 @@ final class OperandClassTransformer {
                                                + _formulaType + ") not supported yet");
                
                }
-               transformNode(rootNode, rootNodeOperandClass, false, false);
+               transformNode(rootNode, rootNodeOperandClass, false);
        }
 
        /**
@@ -83,22 +83,35 @@ final class OperandClassTransformer {
         * the function return value).
         */
        private void transformNode(ParseNode node, byte desiredOperandClass,
-                       boolean callerForceArrayFlag, boolean isDirectChildOfValueOperator) {
+                       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++) {
-                               ParseNode child = children[i];
-                               transformNode(child, desiredOperandClass, callerForceArrayFlag, true);
+                               transformNode(children[i], localDesiredOperandClass, callerForceArrayFlag);
                        }
                        return;
                }
                if (token instanceof AbstractFunctionPtg) {
-                       transformFunctionNode((AbstractFunctionPtg) token, children, desiredOperandClass,
-                                       callerForceArrayFlag);
+                       transformFunctionNode((AbstractFunctionPtg) token, children, desiredOperandClass, callerForceArrayFlag);
                        return;
                }
                if (children.length > 0) {
@@ -109,15 +122,24 @@ final class OperandClassTransformer {
                        // nothing to do
                        return;
                }
-               if (isDirectChildOfValueOperator) {
-                       // 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.
-                       if (token.getPtgClass() == Ptg.CLASS_REF) {
-                               token.setClass(Ptg.CLASS_VALUE); 
+               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;
                }
-               token.setClass(transformClass(token.getPtgClass(), desiredOperandClass, callerForceArrayFlag));
+               return false;
        }
 
        private byte transformClass(byte currentOperandClass, byte desiredOperandClass,
@@ -185,6 +207,7 @@ final class OperandClassTransformer {
                                                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);
@@ -220,7 +243,17 @@ final class OperandClassTransformer {
                for (int i = 0; i < children.length; i++) {
                        ParseNode child = children[i];
                        byte paramOperandClass = afp.getParameterClass(i);
-                       transformNode(child, paramOperandClass, localForceArrayFlag, false);
+                       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); 
                }
        }
 }
index 388f1a6ee4f0225e5463bccfc2b9005a1118e678..8bbc30435a0e5e1797a5a39664bbcfaecceec8fa 100644 (file)
@@ -1055,7 +1055,7 @@ public final class Sheet implements Model {
 
         ColumnInfoRecord ci = _columnInfos.findColumnInfo(columnIndex);
         if (ci != null) {
-            return ci.getColumnWidth();
+            return (short)ci.getColumnWidth();
         }
         //default column width is measured in characters
         //multiply
@@ -1079,8 +1079,8 @@ public final class Sheet implements Model {
     public short getXFIndexForColAt(short columnIndex) {
         ColumnInfoRecord ci = _columnInfos.findColumnInfo(columnIndex);
         if (ci != null) {
-            return ci.getXFIndex();
-         }
+            return (short)ci.getXFIndex();
+        }
         return 0xF;
     }
 
@@ -1138,8 +1138,7 @@ public final class Sheet implements Model {
      * @param indent        if true the group will be indented by one level,
      *                      if false indenting will be removed by one level.
      */
-    public void groupColumnRange(short fromColumn, short toColumn, boolean indent)
-    {
+    public void groupColumnRange(int fromColumn, int toColumn, boolean indent) {
 
         // Set the level for each column
         _columnInfos.groupColumnRange( fromColumn, toColumn, indent);
@@ -1709,17 +1708,13 @@ public final class Sheet implements Model {
     }
 
 
-    public void setColumnGroupCollapsed( short columnNumber, boolean collapsed )
-    {
-        if (collapsed)
-        {
-            _columnInfos.collapseColumn( columnNumber );
-        }
-        else
-        {
-            _columnInfos.expandColumn( columnNumber );
-        }
-    }
+    public void setColumnGroupCollapsed(int columnNumber, boolean collapsed) {
+               if (collapsed) {
+                       _columnInfos.collapseColumn(columnNumber);
+               } else {
+                       _columnInfos.expandColumn(columnNumber);
+               }
+       }
 
     /**
      * protect a spreadsheet with a password (not encypted, just sets protect
index b77dca3e174663e1ecc363ec9ce3a773c84863b8..32aef3a6c32e0dc39c4ba7002427131b20d1ae0f 100644 (file)
@@ -17,6 +17,7 @@
 
 package org.apache.poi.hssf.record;
 
+import org.apache.poi.util.HexDump;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.BitField;
 import org.apache.poi.util.BitFieldFactory;
@@ -30,19 +31,24 @@ import org.apache.poi.util.BitFieldFactory;
  */
 public final class ColumnInfoRecord extends Record {
     public static final short     sid = 0x7d;
-    private short                 field_1_first_col;
-    private short                 field_2_last_col;
-    private short                 field_3_col_width;
-    private short                 field_4_xf_index;
-    private short                 field_5_options;
+    private int field_1_first_col;
+    private int field_2_last_col;
+    private int field_3_col_width;
+    private int field_4_xf_index;
+    private int field_5_options;
     private static final BitField hidden    = BitFieldFactory.getInstance(0x01);
     private static final BitField outlevel  = BitFieldFactory.getInstance(0x0700);
     private static final BitField collapsed = BitFieldFactory.getInstance(0x1000);
     // Excel seems write values 2, 10, and 260, even though spec says "must be zero"
     private short                 field_6_reserved;
 
-    public ColumnInfoRecord()
-    {
+    /**
+     * Creates a column info record with default width and format
+     */
+    public ColumnInfoRecord() {
+        setColumnWidth(2275);
+        field_5_options = 2; 
+        field_4_xf_index = 0x0f;
         field_6_reserved = 2; // seems to be the most common value
     }
 
@@ -90,7 +96,7 @@ public final class ColumnInfoRecord extends Record {
      * @param fc - the first column index (0-based)
      */
 
-    public void setFirstColumn(short fc)
+    public void setFirstColumn(int fc)
     {
         field_1_first_col = fc;
     }
@@ -100,7 +106,7 @@ public final class ColumnInfoRecord extends Record {
      * @param lc - the last column index (0-based)
      */
 
-    public void setLastColumn(short lc)
+    public void setLastColumn(int lc)
     {
         field_2_last_col = lc;
     }
@@ -110,7 +116,7 @@ public final class ColumnInfoRecord extends Record {
      * @param cw - column width
      */
 
-    public void setColumnWidth(short cw)
+    public void setColumnWidth(int cw)
     {
         field_3_col_width = cw;
     }
@@ -121,20 +127,11 @@ public final class ColumnInfoRecord extends Record {
      * @see org.apache.poi.hssf.record.ExtendedFormatRecord
      */
 
-    public void setXFIndex(short xfi)
+    public void setXFIndex(int xfi)
     {
         field_4_xf_index = xfi;
     }
 
-    /**
-     * set the options bitfield - use the bitsetters instead
-     * @param options - the bitfield raw value
-     */
-
-    public void setOptions(short options)
-    {
-        field_5_options = options;
-    }
 
     // start options bitfield
 
@@ -146,7 +143,7 @@ public final class ColumnInfoRecord extends Record {
 
     public void setHidden(boolean ishidden)
     {
-        field_5_options = hidden.setShortBoolean(field_5_options, ishidden);
+        field_5_options = hidden.setBoolean(field_5_options, ishidden);
     }
 
     /**
@@ -155,9 +152,9 @@ public final class ColumnInfoRecord extends Record {
      * @param olevel -outline level for the cells
      */
 
-    public void setOutlineLevel(short olevel)
+    public void setOutlineLevel(int olevel)
     {
-        field_5_options = outlevel.setShortValue(field_5_options, olevel);
+        field_5_options = outlevel.setValue(field_5_options, olevel);
     }
 
     /**
@@ -168,7 +165,7 @@ public final class ColumnInfoRecord extends Record {
 
     public void setCollapsed(boolean iscollapsed)
     {
-        field_5_options = collapsed.setShortBoolean(field_5_options,
+        field_5_options = collapsed.setBoolean(field_5_options,
                                                     iscollapsed);
     }
 
@@ -179,7 +176,7 @@ public final class ColumnInfoRecord extends Record {
      * @return the first column index (0-based)
      */
 
-    public short getFirstColumn()
+    public int getFirstColumn()
     {
         return field_1_first_col;
     }
@@ -189,7 +186,7 @@ public final class ColumnInfoRecord extends Record {
      * @return the last column index (0-based)
      */
 
-    public short getLastColumn()
+    public int getLastColumn()
     {
         return field_2_last_col;
     }
@@ -199,7 +196,7 @@ public final class ColumnInfoRecord extends Record {
      * @return column width
      */
 
-    public short getColumnWidth()
+    public int getColumnWidth()
     {
         return field_3_col_width;
     }
@@ -210,21 +207,18 @@ public final class ColumnInfoRecord extends Record {
      * @see org.apache.poi.hssf.record.ExtendedFormatRecord
      */
 
-    public short getXFIndex()
+    public int getXFIndex()
     {
         return field_4_xf_index;
     }
 
-    /**
-     * get the options bitfield - use the bitsetters instead
-     * @return the bitfield raw value
-     */
-
-    public short getOptions()
-    {
+    public int getOptions() {
         return field_5_options;
     }
-
+    public void setOptions(int field_5_options) {
+        this.field_5_options = field_5_options;
+    }
+    
     // start options bitfield
 
     /**
@@ -244,9 +238,9 @@ public final class ColumnInfoRecord extends Record {
      * @return outline level for the cells
      */
 
-    public short getOutlineLevel()
+    public int getOutlineLevel()
     {
-        return outlevel.getShortValue(field_5_options);
+        return outlevel.getValue(field_5_options);
     }
 
     /**
@@ -261,6 +255,31 @@ public final class ColumnInfoRecord extends Record {
     }
 
     // end options bitfield
+    
+    public boolean containsColumn(int columnIndex) {
+        return field_1_first_col <= columnIndex && columnIndex <= field_2_last_col; 
+    }
+    public boolean isAdjacentBefore(ColumnInfoRecord other) {
+        return field_2_last_col == other.field_1_first_col - 1;
+    }
+   
+    /**
+     * @return <code>true</code> if the format, options and column width match
+     */
+    public boolean formatMatches(ColumnInfoRecord other) {
+        if (field_4_xf_index != other.field_4_xf_index) {
+            return false;
+        }
+        if (field_5_options != other.field_5_options) {
+            return false;
+        }
+        if (field_3_col_width != other.field_3_col_width) {
+            return false;
+        }
+        return true;
+    }
+    
+    
     public short getSid()
     {
         return sid;
@@ -269,13 +288,13 @@ public final class ColumnInfoRecord extends Record {
     public int serialize(int offset, byte [] data)
     {
         LittleEndian.putShort(data, 0 + offset, sid);
-        LittleEndian.putShort(data, 2 + offset, ( short ) 12);
-        LittleEndian.putShort(data, 4 + offset, getFirstColumn());
-        LittleEndian.putShort(data, 6 + offset, getLastColumn());
-        LittleEndian.putShort(data, 8 + offset, getColumnWidth());
-        LittleEndian.putShort(data, 10 + offset, getXFIndex());
-        LittleEndian.putShort(data, 12 + offset, getOptions());
-        LittleEndian.putShort(data, 14 + offset, field_6_reserved);   
+        LittleEndian.putUShort(data, 2 + offset, 12);
+        LittleEndian.putUShort(data, 4 + offset, getFirstColumn());
+        LittleEndian.putUShort(data, 6 + offset, getLastColumn());
+        LittleEndian.putUShort(data, 8 + offset, getColumnWidth());
+        LittleEndian.putUShort(data, 10 + offset, getXFIndex());
+        LittleEndian.putUShort(data, 12 + offset, field_5_options);
+        LittleEndian.putUShort(data, 14 + offset, field_6_reserved);   
         return getRecordSize();
     }
 
@@ -286,24 +305,19 @@ public final class ColumnInfoRecord extends Record {
 
     public String toString()
     {
-        StringBuffer buffer = new StringBuffer();
-
-        buffer.append("[COLINFO]\n");
-        buffer.append("colfirst       = ").append(getFirstColumn())
-            .append("\n");
-        buffer.append("collast        = ").append(getLastColumn())
-            .append("\n");
-        buffer.append("colwidth       = ").append(getColumnWidth())
-            .append("\n");
-        buffer.append("xfindex        = ").append(getXFIndex()).append("\n");
-        buffer.append("options        = ").append(getOptions()).append("\n");
-        buffer.append("  hidden       = ").append(getHidden()).append("\n");
-        buffer.append("  olevel       = ").append(getOutlineLevel())
-            .append("\n");
-        buffer.append("  collapsed    = ").append(getCollapsed())
-            .append("\n");
-        buffer.append("[/COLINFO]\n");
-        return buffer.toString();
+        StringBuffer sb = new StringBuffer();
+
+        sb.append("[COLINFO]\n");
+        sb.append("  colfirst = ").append(getFirstColumn()).append("\n");
+        sb.append("  collast  = ").append(getLastColumn()).append("\n");
+        sb.append("  colwidth = ").append(getColumnWidth()).append("\n");
+        sb.append("  xfindex  = ").append(getXFIndex()).append("\n");
+        sb.append("  options  = ").append(HexDump.shortToHex(field_5_options)).append("\n");
+        sb.append("    hidden   = ").append(getHidden()).append("\n");
+        sb.append("    olevel   = ").append(getOutlineLevel()).append("\n");
+        sb.append("    collapsed= ").append(getCollapsed()).append("\n");
+        sb.append("[/COLINFO]\n");
+        return sb.toString();
     }
 
     public Object clone() {
index 46e8283dc20c30ebc6639bc95b0ed141caa4892d..b9616e0db739028ed7ca5c1a356c9d836215ea99 100644 (file)
@@ -18,6 +18,8 @@
 package org.apache.poi.hssf.record;
 
 import org.apache.poi.hssf.record.formula.Ptg;
+import org.apache.poi.hssf.record.formula.eval.ErrorEval;
+import org.apache.poi.hssf.usermodel.HSSFCell;
 import org.apache.poi.util.BitField;
 import org.apache.poi.util.BitFieldFactory;
 import org.apache.poi.util.HexDump;
@@ -32,264 +34,430 @@ import org.apache.poi.util.LittleEndian;
  */
 public final class FormulaRecord extends Record implements CellValueRecordInterface {
 
-    public static final short sid = 0x0006;   // docs say 406...because of a bug Microsoft support site article #Q184647)
-    private static int FIXED_SIZE = 22;
-
-    private static final BitField alwaysCalc = BitFieldFactory.getInstance(0x0001);
-    private static final BitField calcOnLoad = BitFieldFactory.getInstance(0x0002);
-    private static final BitField sharedFormula = BitFieldFactory.getInstance(0x0008);
-
-    private int               field_1_row;
-    private short             field_2_column;
-    private short             field_3_xf;
-    private double            field_4_value;
-    private short             field_5_options;
-    private int               field_6_zero;
-    private Ptg[]             field_8_parsed_expr;
-
-    /**
-     * Since the NaN support seems sketchy (different constants) we'll store and spit it out directly
-     */
-    private byte[]            value_data;
-
-    /** Creates new FormulaRecord */
-
-    public FormulaRecord() {
-        field_8_parsed_expr = Ptg.EMPTY_PTG_ARRAY;
-    }
-
-    /**
-     * Constructs a Formula record and sets its fields appropriately.
-     * Note - id must be 0x06 (NOT 0x406 see MSKB #Q184647 for an
-     * "explanation of this bug in the documentation) or an exception
-     *  will be throw upon validation
-     *
-     * @param in the RecordInputstream to read the record from
-     */
-
-    public FormulaRecord(RecordInputStream in) {
-        super(in);
-    }
-
-    protected void fillFields(RecordInputStream in) {
-          field_1_row            = in.readUShort();
-          field_2_column         = in.readShort();
-          field_3_xf             = in.readShort();
-          field_4_value          = in.readDouble();
-          field_5_options        = in.readShort();
-
-        if (Double.isNaN(field_4_value)) {
-            value_data = in.getNANData();
-        }
-
-          field_6_zero           = in.readInt();
-          int field_7_expression_len = in.readShort(); // this length does not include any extra array data
-          field_8_parsed_expr = Ptg.readTokens(field_7_expression_len, in);
-        if (in.remaining() == 10) {
-            // TODO - this seems to occur when IntersectionPtg is present
-            // 10 extra bytes are just 0x01 and 0x00
-            // This causes POI stderr: "WARN. Unread 10 bytes of record 0x6"
-        }
-    }
-
-    public void setRow(int row) {
-        field_1_row = row;
-    }
-
-    public void setColumn(short column) {
-        field_2_column = column;
-    }
-
-    public void setXFIndex(short xf) {
-        field_3_xf = xf;
-    }
-
-    /**
-     * set the calculated value of the formula
-     *
-     * @param value  calculated value
-     */
-    public void setValue(double value) {
-        field_4_value = value;
-    }
-
-    /**
-     * set the option flags
-     *
-     * @param options  bitmask
-     */
-    public void setOptions(short options) {
-        field_5_options = options;
-    }
-
-    public int getRow() {
-        return field_1_row;
-    }
-
-    public short getColumn() {
-        return field_2_column;
-    }
-
-    public short getXFIndex() {
-        return field_3_xf;
-    }
-
-    /**
-     * get the calculated value of the formula
-     *
-     * @return calculated value
-     */
-    public double getValue() {
-        return field_4_value;
-    }
-
-    /**
-     * get the option flags
-     *
-     * @return bitmask
-     */
-    public short getOptions() {
-        return field_5_options;
-    }
-
-    public boolean isSharedFormula() {
-        return sharedFormula.isSet(field_5_options);
-    }
-    public void setSharedFormula(boolean flag) {
-        field_5_options =
-            sharedFormula.setShortBoolean(field_5_options, flag);
-    }
-
-    public boolean isAlwaysCalc() {
-        return alwaysCalc.isSet(field_5_options);
-    }
-    public void setAlwaysCalc(boolean flag) {
-        field_5_options =
-            alwaysCalc.setShortBoolean(field_5_options, flag);
-    }
-
-    public boolean isCalcOnLoad() {
-        return calcOnLoad.isSet(field_5_options);
-    }
-    public void setCalcOnLoad(boolean flag) {
-        field_5_options =
-            calcOnLoad.setShortBoolean(field_5_options, flag);
-    }
-
-    /**
-     * @return the formula tokens. never <code>null</code>
-     */
-    public Ptg[] getParsedExpression() {
-        return (Ptg[]) field_8_parsed_expr.clone();
-    }
-
-    public void setParsedExpression(Ptg[] ptgs) {
-        field_8_parsed_expr = ptgs;
-    }
-
-    /**
-     * called by constructor, should throw runtime exception in the event of a
-     * record passed with a differing ID.
-     *
-     * @param id alleged id for this record
-     */
-    protected void validateSid(short id) {
-        if (id != sid) {
-            throw new RecordFormatException("NOT A FORMULA RECORD");
-        }
-    }
-
-    public short getSid() {
-        return sid;
-    }
-
-    private int getDataSize() {
-        return FIXED_SIZE + Ptg.getEncodedSize(field_8_parsed_expr);
-    }
-    public int serialize(int offset, byte [] data) {
-
-        int dataSize = getDataSize();
-
-        LittleEndian.putShort(data, 0 + offset, sid);
-        LittleEndian.putUShort(data, 2 + offset, dataSize);
-        LittleEndian.putUShort(data, 4 + offset, getRow());
-        LittleEndian.putShort(data, 6 + offset, getColumn());
-        LittleEndian.putShort(data, 8 + offset, getXFIndex());
-
-        //only reserialize if the value is still NaN and we have old nan data
-        if (Double.isNaN(getValue()) && value_data != null) {
-            System.arraycopy(value_data,0,data,10 + offset,value_data.length);
-        } else {
-            LittleEndian.putDouble(data, 10 + offset, field_4_value);
-        }
-
-        LittleEndian.putShort(data, 18 + offset, getOptions());
-
-        //when writing the chn field (offset 20), it's supposed to be 0 but ignored on read
-        //Microsoft Excel Developer's Kit Page 318
-        LittleEndian.putInt(data, 20 + offset, 0);
-        int formulaTokensSize = Ptg.getEncodedSizeWithoutArrayData(field_8_parsed_expr);
-        LittleEndian.putUShort(data, 24 + offset, formulaTokensSize);
-        Ptg.serializePtgs(field_8_parsed_expr, data, 26+offset);
-        return 4 + dataSize;
-    }
-
-    public int getRecordSize() {
-        return 4 + getDataSize();
-    }
-
-    public boolean isInValueSection() {
-        return true;
-    }
-
-    public boolean isValue() {
-        return true;
-    }
-
-    public String toString() {
-
-        StringBuffer sb = new StringBuffer();
-        sb.append("[FORMULA]\n");
-        sb.append("    .row       = ").append(HexDump.shortToHex(getRow())).append("\n");
-        sb.append("    .column    = ").append(HexDump.shortToHex(getColumn())).append("\n");
-        sb.append("    .xf        = ").append(HexDump.shortToHex(getXFIndex())).append("\n");
-        sb.append("    .value     = ");
-        if (Double.isNaN(this.getValue()) && value_data != null) {
-            sb.append("(NaN)").append(HexDump.dump(value_data,0,0)).append("\n");
-        } else {
-            sb.append(getValue()).append("\n");
-        }
-        sb.append("    .options   = ").append(HexDump.shortToHex(getOptions())).append("\n");
-        sb.append("    .alwaysCalc= ").append(alwaysCalc.isSet(getOptions())).append("\n");
-        sb.append("    .calcOnLoad= ").append(calcOnLoad.isSet(getOptions())).append("\n");
-        sb.append("    .shared    = ").append(sharedFormula.isSet(getOptions())).append("\n");
-        sb.append("    .zero      = ").append(HexDump.intToHex(field_6_zero)).append("\n");
-
-        for (int k = 0; k < field_8_parsed_expr.length; k++ ) {
-            sb.append("     Ptg[").append(k).append("]=");
-            Ptg ptg = field_8_parsed_expr[k];
-            sb.append(ptg.toString()).append(ptg.getRVAType()).append("\n");
-        }
-        sb.append("[/FORMULA]\n");
-        return sb.toString();
-    }
-
-    public Object clone() {
-      FormulaRecord rec = new FormulaRecord();
-      rec.field_1_row = field_1_row;
-      rec.field_2_column = field_2_column;
-      rec.field_3_xf = field_3_xf;
-      rec.field_4_value = field_4_value;
-      rec.field_5_options = field_5_options;
-      rec.field_6_zero = field_6_zero;
-      int nTokens = field_8_parsed_expr.length;
-      Ptg[] ptgs = new Ptg[nTokens];
-      for (int i=0; i< nTokens; i++) {
-        ptgs[i] = field_8_parsed_expr[i].copy();
-      }
-      rec.field_8_parsed_expr = ptgs;
-      rec.value_data = value_data;
-      return rec;
-    }
+       public static final short sid = 0x0006;   // docs say 406...because of a bug Microsoft support site article #Q184647)
+       private static int FIXED_SIZE = 22;
+
+       private static final BitField alwaysCalc = BitFieldFactory.getInstance(0x0001);
+       private static final BitField calcOnLoad = BitFieldFactory.getInstance(0x0002);
+       private static final BitField sharedFormula = BitFieldFactory.getInstance(0x0008);
+
+       /**
+        * Manages the cached formula result values of other types besides numeric.
+        * Excel encodes the same 8 bytes that would be field_4_value with various NaN
+        * values that are decoded/encoded by this class. 
+        */
+       private static final class SpecialCachedValue {
+               /** deliberately chosen by Excel in order to encode other values within Double NaNs */
+               private static final long BIT_MARKER = 0xFFFF000000000000L;
+               private static final int VARIABLE_DATA_LENGTH = 6;
+               private static final int DATA_INDEX = 2;
+
+               public static final int STRING = 0;
+               public static final int BOOLEAN = 1;
+               public static final int ERROR_CODE = 2;
+               public static final int EMPTY = 3;
+
+               private final byte[] _variableData;
+
+               private SpecialCachedValue(byte[] data) {
+                       _variableData = data;
+               }
+               public int getTypeCode() {
+                       return _variableData[0];
+               }
+
+               /**
+                * @return <code>null</code> if the double value encoded by <tt>valueLongBits</tt> 
+                * is a normal (non NaN) double value.
+                */
+               public static SpecialCachedValue create(long valueLongBits) {
+                       if ((BIT_MARKER & valueLongBits) != BIT_MARKER) {
+                               return null;
+                       }
+
+                       byte[] result = new byte[VARIABLE_DATA_LENGTH];
+                       long x = valueLongBits;
+                       for (int i=0; i<VARIABLE_DATA_LENGTH; i++) {
+                               result[i] = (byte) x;
+                               x >>= 8;
+                       }
+                       switch (result[0]) {
+                               case STRING:
+                               case BOOLEAN:
+                               case ERROR_CODE:
+                               case EMPTY:
+                                       break;
+                               default:
+                                       throw new RecordFormatException("Bad special value code (" + result[0] + ")");
+                       }
+                       return new SpecialCachedValue(result);
+               }
+               public void serialize(byte[] data, int offset) {
+                       System.arraycopy(_variableData, 0, data, offset, VARIABLE_DATA_LENGTH);
+                       LittleEndian.putUShort(data, offset+VARIABLE_DATA_LENGTH, 0xFFFF);
+               }
+               public String formatDebugString() {
+                       return formatValue() + ' ' + HexDump.toHex(_variableData);
+               }
+               private String formatValue() {
+                       int typeCode = getTypeCode();
+                       switch (typeCode) {
+                               case STRING:     return "<string>";
+                               case BOOLEAN:   return getDataValue() == 0 ? "FALSE" : "TRUE";
+                               case ERROR_CODE: return ErrorEval.getText(getDataValue());
+                               case EMPTY:       return "<empty>";
+                       }
+                       return "#error(type=" + typeCode + ")#";
+               }
+               private int getDataValue() {
+                       return _variableData[DATA_INDEX];
+               }
+               public static SpecialCachedValue createCachedEmptyValue() {
+                       return create(EMPTY, 0);
+               }
+               public static SpecialCachedValue createForString() {
+                       return create(STRING, 0);
+               }
+               public static SpecialCachedValue createCachedBoolean(boolean b) {
+                       return create(BOOLEAN, b ? 0 : 1);
+               }
+               public static SpecialCachedValue createCachedErrorCode(int errorCode) {
+                       return create(ERROR_CODE, errorCode);
+               }
+               private static SpecialCachedValue create(int code, int data) {
+                       byte[] vd = {
+                                       (byte) code,
+                                       0,
+                                       (byte) data,
+                                       0,
+                                       0,
+                                       0,
+                       };
+                       return new SpecialCachedValue(vd);
+               }
+               public String toString() {
+                       StringBuffer sb = new StringBuffer(64);
+                       sb.append(getClass().getName());
+                       sb.append('[').append(formatValue()).append(']');
+                       return sb.toString();
+               }
+               public int getValueType() {
+                       int typeCode = getTypeCode();
+                       switch (typeCode) {
+                               case STRING:     return HSSFCell.CELL_TYPE_STRING;
+                               case BOOLEAN:   return HSSFCell.CELL_TYPE_BOOLEAN;
+                               case ERROR_CODE: return HSSFCell.CELL_TYPE_ERROR;
+                               case EMPTY:       return HSSFCell.CELL_TYPE_STRING; // is this correct?
+                       }
+                       throw new IllegalStateException("Unexpected type id (" + typeCode + ")");
+               }
+               public boolean getBooleanValue() {
+                       if (getTypeCode() != BOOLEAN) {
+                               throw new IllegalStateException("Not a boolean cached value - " + formatValue());
+                       }
+                       return getDataValue() != 0;
+               }
+               public int getErrorValue() {
+                       if (getTypeCode() != ERROR_CODE) {
+                               throw new IllegalStateException("Not an error cached value - " + formatValue());
+                       }
+                       return getDataValue();
+               }
+       }
+
+
+
+       private int    field_1_row;
+       private short  field_2_column;
+       private short  field_3_xf;
+       private double field_4_value;
+       private short  field_5_options;
+       private int    field_6_zero;
+       private Ptg[]  field_8_parsed_expr;
+
+       /**
+        * Since the NaN support seems sketchy (different constants) we'll store and spit it out directly
+        */
+       private SpecialCachedValue specialCachedValue;
+
+       /** Creates new FormulaRecord */
+
+       public FormulaRecord() {
+               field_8_parsed_expr = Ptg.EMPTY_PTG_ARRAY;
+       }
+
+       /**
+        * Constructs a Formula record and sets its fields appropriately.
+        * Note - id must be 0x06 (NOT 0x406 see MSKB #Q184647 for an
+        * "explanation of this bug in the documentation) or an exception
+        *  will be throw upon validation
+        *
+        * @param in the RecordInputstream to read the record from
+        */
+
+       public FormulaRecord(RecordInputStream in) {
+               super(in);
+       }
+
+       protected void fillFields(RecordInputStream in) {
+               field_1_row      = in.readUShort();
+               field_2_column  = in.readShort();
+               field_3_xf        = in.readShort();
+               long valueLongBits  = in.readLong();
+               field_5_options = in.readShort();
+               specialCachedValue = SpecialCachedValue.create(valueLongBits);
+               if (specialCachedValue == null) {
+                       field_4_value = Double.longBitsToDouble(valueLongBits);
+               }
+
+               field_6_zero               = in.readInt();
+               int field_7_expression_len = in.readShort(); // this length does not include any extra array data
+               field_8_parsed_expr = Ptg.readTokens(field_7_expression_len, in);
+               if (in.remaining() == 10) {
+                       // TODO - this seems to occur when IntersectionPtg is present
+                       // 10 extra bytes are just 0x01 and 0x00
+                       // This causes POI stderr: "WARN. Unread 10 bytes of record 0x6"
+               }
+       }
+
+
+       public void setRow(int row) {
+               field_1_row = row;
+       }
+
+       public void setColumn(short column) {
+               field_2_column = column;
+       }
+
+       public void setXFIndex(short xf) {
+               field_3_xf = xf;
+       }
+
+       /**
+        * set the calculated value of the formula
+        *
+        * @param value  calculated value
+        */
+       public void setValue(double value) {
+               field_4_value = value;
+               specialCachedValue = null;
+       }
+
+       public void setCachedResultTypeEmptyString() {
+               specialCachedValue = SpecialCachedValue.createCachedEmptyValue();
+       }
+       public void setCachedResultTypeString() {
+               specialCachedValue = SpecialCachedValue.createForString();
+       }
+       public void setCachedResultErrorCode(int errorCode) {
+               specialCachedValue = SpecialCachedValue.createCachedErrorCode(errorCode);
+       }
+       public void setCachedResultBoolean(boolean value) {
+               specialCachedValue = SpecialCachedValue.createCachedBoolean(value);
+       }
+       /**
+        * @return <code>true</code> if this {@link FormulaRecord} is followed by a
+        *  {@link StringRecord} representing the cached text result of the formula
+        *  evaluation.
+        */
+       public boolean hasCachedResultString() {
+               if (specialCachedValue == null) {
+                       return false;
+               }
+               return specialCachedValue.getTypeCode() == SpecialCachedValue.STRING;
+       }
+
+       public int getCachedResultType() {
+               if (specialCachedValue == null) {
+                       return HSSFCell.CELL_TYPE_NUMERIC;
+               }
+               return specialCachedValue.getValueType();
+       }
+
+       public boolean getCachedBooleanValue() {
+               return specialCachedValue.getBooleanValue();
+       }
+       public int getCachedErrorValue() {
+               return specialCachedValue.getErrorValue();
+       }
+
+
+       /**
+        * set the option flags
+        *
+        * @param options  bitmask
+        */
+       public void setOptions(short options) {
+               field_5_options = options;
+       }
+
+       public int getRow() {
+               return field_1_row;
+       }
+
+       public short getColumn() {
+               return field_2_column;
+       }
+
+       public short getXFIndex() {
+               return field_3_xf;
+       }
+
+       /**
+        * get the calculated value of the formula
+        *
+        * @return calculated value
+        */
+       public double getValue() {
+               return field_4_value;
+       }
+
+       /**
+        * get the option flags
+        *
+        * @return bitmask
+        */
+       public short getOptions() {
+               return field_5_options;
+       }
+
+       public boolean isSharedFormula() {
+               return sharedFormula.isSet(field_5_options);
+       }
+       public void setSharedFormula(boolean flag) {
+               field_5_options =
+                       sharedFormula.setShortBoolean(field_5_options, flag);
+       }
+
+       public boolean isAlwaysCalc() {
+               return alwaysCalc.isSet(field_5_options);
+       }
+       public void setAlwaysCalc(boolean flag) {
+               field_5_options =
+                       alwaysCalc.setShortBoolean(field_5_options, flag);
+       }
+
+       public boolean isCalcOnLoad() {
+               return calcOnLoad.isSet(field_5_options);
+       }
+       public void setCalcOnLoad(boolean flag) {
+               field_5_options =
+                       calcOnLoad.setShortBoolean(field_5_options, flag);
+       }
+
+       /**
+        * @return the formula tokens. never <code>null</code>
+        */
+       public Ptg[] getParsedExpression() {
+               return (Ptg[]) field_8_parsed_expr.clone();
+       }
+
+       public void setParsedExpression(Ptg[] ptgs) {
+               field_8_parsed_expr = ptgs;
+       }
+
+       /**
+        * called by constructor, should throw runtime exception in the event of a
+        * record passed with a differing ID.
+        *
+        * @param id alleged id for this record
+        */
+       protected void validateSid(short id) {
+               if (id != sid) {
+                       throw new RecordFormatException("NOT A FORMULA RECORD");
+               }
+       }
+
+       public short getSid() {
+               return sid;
+       }
+
+       private int getDataSize() {
+               return FIXED_SIZE + Ptg.getEncodedSize(field_8_parsed_expr);
+       }
+       public int serialize(int offset, byte [] data) {
+
+               int dataSize = getDataSize();
+
+               LittleEndian.putShort(data, 0 + offset, sid);
+               LittleEndian.putUShort(data, 2 + offset, dataSize);
+               LittleEndian.putUShort(data, 4 + offset, getRow());
+               LittleEndian.putShort(data, 6 + offset, getColumn());
+               LittleEndian.putShort(data, 8 + offset, getXFIndex());
+
+               if (specialCachedValue == null) {
+                       LittleEndian.putDouble(data, 10 + offset, field_4_value);
+               } else {
+                       specialCachedValue.serialize(data, 10+offset);
+               }
+
+               LittleEndian.putShort(data, 18 + offset, getOptions());
+
+               //when writing the chn field (offset 20), it's supposed to be 0 but ignored on read
+               //Microsoft Excel Developer's Kit Page 318
+               LittleEndian.putInt(data, 20 + offset, 0);
+               int formulaTokensSize = Ptg.getEncodedSizeWithoutArrayData(field_8_parsed_expr);
+               LittleEndian.putUShort(data, 24 + offset, formulaTokensSize);
+               Ptg.serializePtgs(field_8_parsed_expr, data, 26+offset);
+               return 4 + dataSize;
+       }
+
+       public int getRecordSize() {
+               return 4 + getDataSize();
+       }
+
+       public boolean isInValueSection() {
+               return true;
+       }
+
+       public boolean isValue() {
+               return true;
+       }
+
+       public String toString() {
+
+               StringBuffer sb = new StringBuffer();
+               sb.append("[FORMULA]\n");
+               sb.append("     .row       = ").append(HexDump.shortToHex(getRow())).append("\n");
+               sb.append("     .column = ").append(HexDump.shortToHex(getColumn())).append("\n");
+               sb.append("     .xf             = ").append(HexDump.shortToHex(getXFIndex())).append("\n");
+               sb.append("     .value   = ");
+               if (specialCachedValue == null) {
+                       sb.append(field_4_value).append("\n");
+               } else {
+                       sb.append(specialCachedValue.formatDebugString()).append("\n");
+               }
+               sb.append("     .options   = ").append(HexDump.shortToHex(getOptions())).append("\n");
+               sb.append("     .alwaysCalc= ").append(alwaysCalc.isSet(getOptions())).append("\n");
+               sb.append("     .calcOnLoad= ").append(calcOnLoad.isSet(getOptions())).append("\n");
+               sb.append("     .shared = ").append(sharedFormula.isSet(getOptions())).append("\n");
+               sb.append("     .zero     = ").append(HexDump.intToHex(field_6_zero)).append("\n");
+
+               for (int k = 0; k < field_8_parsed_expr.length; k++ ) {
+                       sb.append("      Ptg[").append(k).append("]=");
+                       Ptg ptg = field_8_parsed_expr[k];
+                       sb.append(ptg.toString()).append(ptg.getRVAType()).append("\n");
+               }
+               sb.append("[/FORMULA]\n");
+               return sb.toString();
+       }
+
+       public Object clone() {
+               FormulaRecord rec = new FormulaRecord();
+               rec.field_1_row = field_1_row;
+               rec.field_2_column = field_2_column;
+               rec.field_3_xf = field_3_xf;
+               rec.field_4_value = field_4_value;
+               rec.field_5_options = field_5_options;
+               rec.field_6_zero = field_6_zero;
+               int nTokens = field_8_parsed_expr.length;
+               Ptg[] ptgs = new Ptg[nTokens];
+               for (int i = 0; i < nTokens; i++) {
+                       ptgs[i] = field_8_parsed_expr[i].copy();
+               }
+               rec.field_8_parsed_expr = ptgs;
+               rec.specialCachedValue = specialCachedValue;
+               return rec;
+       }
 }
 
index 12c818b183f5ae074114a97b01738da5f3a74748..fe6a4b2ea3b70d01b02901bb3d43acae2276fa78 100755 (executable)
@@ -209,30 +209,18 @@ public class RecordInputStream extends InputStream {
     return result;
   }
 
-  byte[] NAN_data = null;
   public double readDouble() {
-    checkRecordPosition();    
-    //Reset NAN data
-    NAN_data = null;
-    double result = LittleEndian.getDouble(data, recordOffset);
-    //Excel represents NAN in several ways, at this point in time we do not often
-    //know the sequence of bytes, so as a hack we store the NAN byte sequence
-    //so that it is not corrupted.
+    checkRecordPosition();
+    long valueLongBits = LittleEndian.getLong(data, recordOffset);
+    double result = Double.longBitsToDouble(valueLongBits);
     if (Double.isNaN(result)) {
-      NAN_data = new byte[8];
-      System.arraycopy(data, recordOffset, NAN_data, 0, 8);
+      throw new RuntimeException("Did not expect to read NaN");
     }
-    
     recordOffset += LittleEndian.DOUBLE_SIZE;
     pos += LittleEndian.DOUBLE_SIZE;
     return result;
   }
-  
-  public byte[] getNANData() {
-    if (NAN_data == null)
-      throw new RecordFormatException("Do NOT call getNANData without calling readDouble that returns NaN");
-    return NAN_data;
-  }
+
   
   public short[] readShortArray() {
     checkRecordPosition();
@@ -276,9 +264,6 @@ public class RecordInputStream extends InputStream {
   }
     
   public String readCompressedUnicode(int length) {
-    if(length == 0) {
-        return "";
-    }
     if ((length < 0) || ((remaining() < length) && !isContinueNext())) {
             throw new IllegalArgumentException("Illegal length " + length);
     }
@@ -291,9 +276,7 @@ public class RecordInputStream extends InputStream {
           if(compressByte != 0) throw new IllegalArgumentException("compressByte in continue records must be 0 while reading compressed unicode");
       }
       byte b = readByte();
-      //Typecast direct to char from byte with high bit set causes all ones
-      //in the high byte of the char (which is of course incorrect)
-      char ch = (char)( (short)0xff & (short)b );
+      char ch = (char)(0x00FF & b); // avoid sex
       buf.append(ch); 
     }
     return buf.toString();    
index b24d8c5b451a6236d0635aa1781862ed052f04e5..0f405bd1115b041551af5b186e708968a0d70ee5 100644 (file)
 package org.apache.poi.hssf.record.aggregates;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 
 import org.apache.poi.hssf.model.RecordStream;
 import org.apache.poi.hssf.record.ColumnInfoRecord;
-import org.apache.poi.hssf.record.Record;
 
 /**
  * @author Glen Stampoultzis
- * @version $Id$
  */
 public final class ColumnInfoRecordsAggregate extends RecordAggregate {
+       /**
+        * List of {@link ColumnInfoRecord}s assumed to be in order 
+        */
        private final List records;
+       
+       
+       private static final class CIRComparator implements Comparator {
+               public static final Comparator instance = new CIRComparator();
+               private CIRComparator() {
+                       // enforce singleton
+               }
+               public int compare(Object a, Object b) {
+                       return compareColInfos((ColumnInfoRecord)a, (ColumnInfoRecord)b);
+               }
+               public static int compareColInfos(ColumnInfoRecord a, ColumnInfoRecord b) {
+                       return a.getFirstColumn()-b.getFirstColumn();
+               }
+       }
 
        /**
         * Creates an empty aggregate
@@ -37,486 +54,470 @@ public final class ColumnInfoRecordsAggregate extends RecordAggregate {
        public ColumnInfoRecordsAggregate() {
                records = new ArrayList();
        }
-    public ColumnInfoRecordsAggregate(RecordStream rs) {
-        this();
-        
-        while(rs.peekNextClass() == ColumnInfoRecord.class) {
-               records.add(rs.getNext());
-        }
-        if (records.size() < 1) {
-               throw new RuntimeException("No column info records found");
-        }
-   }
-
-    /**
-     * Performs a deep clone of the record
-     */
-    public Object clone()
-    {
-        ColumnInfoRecordsAggregate rec = new ColumnInfoRecordsAggregate();
-        for (int k = 0; k < records.size(); k++)
-        {
-            ColumnInfoRecord ci = ( ColumnInfoRecord ) records.get(k);
-            ci=(ColumnInfoRecord) ci.clone();
-            rec.insertColumn( ci );
-        }
-        return rec;
-    }
-
-    /**
-     * Inserts a column into the aggregate (at the end of the list).
-     */
-    public void insertColumn( ColumnInfoRecord col )
-    {
-        records.add( col );
-    }
-
-    /**
-     * Inserts a column into the aggregate (at the position specified
-     * by <code>idx</code>.
-     */
-    public void insertColumn( int idx, ColumnInfoRecord col )
-    {
-        records.add( idx, col );
-    }
-
-    public int getNumColumns( )
-    {
-        return records.size();
-    }
-
-    public void visitContainedRecords(RecordVisitor rv) {
-       int nItems = records.size();
-       if (nItems < 1) {
-               return;
-       }
-       for(int i=0; i<nItems; i++) {
-               rv.visitRecord((Record)records.get(i));
-       }
-    }
-
-    public int findStartOfColumnOutlineGroup(int idx)
-    {
-        // Find the start of the group.
-        ColumnInfoRecord columnInfo = (ColumnInfoRecord) records.get( idx );
-        int level = columnInfo.getOutlineLevel();
-        while (idx != 0)
-        {
-            ColumnInfoRecord prevColumnInfo = (ColumnInfoRecord) records.get( idx - 1 );
-            if (columnInfo.getFirstColumn() - 1 == prevColumnInfo.getLastColumn())
-            {
-                if (prevColumnInfo.getOutlineLevel() < level)
-                {
-                    break;
-                }
-                idx--;
-                columnInfo = prevColumnInfo;
-            }
-            else
-            {
-                break;
-            }
-        }
-
-        return idx;
-    }
-
-    public int findEndOfColumnOutlineGroup(int idx)
-    {
-        // Find the end of the group.
-        ColumnInfoRecord columnInfo = (ColumnInfoRecord) records.get( idx );
-        int level = columnInfo.getOutlineLevel();
-        while (idx < records.size() - 1)
-        {
-            ColumnInfoRecord nextColumnInfo = (ColumnInfoRecord) records.get( idx + 1 );
-            if (columnInfo.getLastColumn() + 1 == nextColumnInfo.getFirstColumn())
-            {
-                if (nextColumnInfo.getOutlineLevel() < level)
-                {
-                    break;
-                }
-                idx++;
-                columnInfo = nextColumnInfo;
-            }
-            else
-            {
-                break;
-            }
-        }
-
-        return idx;
-    }
-
-    private ColumnInfoRecord getColInfo(int idx) {
-        return (ColumnInfoRecord) records.get( idx );
-    }
-
-    public ColumnInfoRecord writeHidden( ColumnInfoRecord columnInfo, int idx, boolean hidden )
-    {
-        int level = columnInfo.getOutlineLevel();
-        while (idx < records.size())
-        {
-            columnInfo.setHidden( hidden );
-            if (idx + 1 < records.size())
-            {
-                ColumnInfoRecord nextColumnInfo = getColInfo(idx + 1);
-                if (columnInfo.getLastColumn() + 1 == nextColumnInfo.getFirstColumn())
-                {
-                    if (nextColumnInfo.getOutlineLevel() < level)
-                        break;
-                    columnInfo = nextColumnInfo;
-                }
-                else
-                {
-                    break;
-                }
-            }
-            idx++;
-        }
-        return columnInfo;
-    }
-
-    public boolean isColumnGroupCollapsed( int idx )
-    {
-        int endOfOutlineGroupIdx = findEndOfColumnOutlineGroup( idx );
-        if (endOfOutlineGroupIdx >= records.size())
-            return false;
-        if (getColInfo(endOfOutlineGroupIdx).getLastColumn() + 1 != getColInfo(endOfOutlineGroupIdx + 1).getFirstColumn())
-            return false;
-        else
-            return getColInfo(endOfOutlineGroupIdx+1).getCollapsed();
-    }
-
-
-    public boolean isColumnGroupHiddenByParent( int idx )
-    {
-        // Look out outline details of end
-        int endLevel;
-        boolean endHidden;
-        int endOfOutlineGroupIdx = findEndOfColumnOutlineGroup( idx );
-        if (endOfOutlineGroupIdx >= records.size())
-        {
-            endLevel = 0;
-            endHidden = false;
-        }
-        else if (getColInfo(endOfOutlineGroupIdx).getLastColumn() + 1 != getColInfo(endOfOutlineGroupIdx + 1).getFirstColumn())
-        {
-            endLevel = 0;
-            endHidden = false;
-        }
-        else
-        {
-            endLevel = getColInfo( endOfOutlineGroupIdx + 1).getOutlineLevel();
-            endHidden = getColInfo( endOfOutlineGroupIdx + 1).getHidden();
-        }
-
-        // Look out outline details of start
-        int startLevel;
-        boolean startHidden;
-        int startOfOutlineGroupIdx = findStartOfColumnOutlineGroup( idx );
-        if (startOfOutlineGroupIdx <= 0)
-        {
-            startLevel = 0;
-            startHidden = false;
-        }
-        else if (getColInfo(startOfOutlineGroupIdx).getFirstColumn() - 1 != getColInfo(startOfOutlineGroupIdx - 1).getLastColumn())
-        {
-            startLevel = 0;
-            startHidden = false;
-        }
-        else
-        {
-            startLevel = getColInfo( startOfOutlineGroupIdx - 1).getOutlineLevel();
-            startHidden = getColInfo( startOfOutlineGroupIdx - 1 ).getHidden();
-        }
-
-        if (endLevel > startLevel)
-        {
-            return endHidden;
-        }
-        else
-        {
-            return startHidden;
-        }
-    }
-
-    public void collapseColumn( short columnNumber )
-    {
-        int idx = findColumnIdx( columnNumber, 0 );
-        if (idx == -1)
-            return;
-
-        // Find the start of the group.
-        ColumnInfoRecord columnInfo = getColInfo( findStartOfColumnOutlineGroup( idx ) );
-
-        // Hide all the columns until the end of the group
-        columnInfo = writeHidden( columnInfo, idx, true );
-
-        // Write collapse field
-        setColumn( (short) ( columnInfo.getLastColumn() + 1 ), null, null, null, null, Boolean.TRUE);
-    }
-
-    public void expandColumn( short columnNumber )
-    {
-        int idx = findColumnIdx( columnNumber, 0 );
-        if (idx == -1)
-            return;
-
-        // If it is already exapanded do nothing.
-        if (!isColumnGroupCollapsed(idx))
-            return;
-
-        // Find the start of the group.
-        int startIdx = findStartOfColumnOutlineGroup( idx );
-        ColumnInfoRecord columnInfo = getColInfo( startIdx );
-
-        // Find the end of the group.
-        int endIdx = findEndOfColumnOutlineGroup( idx );
-        ColumnInfoRecord endColumnInfo = getColInfo( endIdx );
-
-        // expand:
-        // colapsed bit must be unset
-        // hidden bit gets unset _if_ surrounding groups are expanded you can determine
-        //   this by looking at the hidden bit of the enclosing group.  You will have
-        //   to look at the start and the end of the current group to determine which
-        //   is the enclosing group
-        // hidden bit only is altered for this outline level.  ie.  don't uncollapse contained groups
-        if (!isColumnGroupHiddenByParent( idx ))
-        {
-            for (int i = startIdx; i <= endIdx; i++)
-            {
-                if (columnInfo.getOutlineLevel() == getColInfo(i).getOutlineLevel())
-                    getColInfo(i).setHidden( false );
-            }
-        }
-
-        // Write collapse field
-        setColumn( (short) ( columnInfo.getLastColumn() + 1 ), null, null, null, null, Boolean.FALSE);
-    }
-
-    /**
-     * creates the ColumnInfo Record and sets it to a default column/width
-     * @see org.apache.poi.hssf.record.ColumnInfoRecord
-     * @return record containing a ColumnInfoRecord
-     */
-    public static ColumnInfoRecord createColInfo()
-    {
-        ColumnInfoRecord retval = new ColumnInfoRecord();
-
-        retval.setColumnWidth(( short ) 2275);
-        // was:       retval.setOptions(( short ) 6);
-        retval.setOptions(( short ) 2);
-        retval.setXFIndex(( short ) 0x0f);
-        return retval;
-    }
-
-
-    public void setColumn(short column, Short xfIndex, Short width, Integer level, Boolean hidden, Boolean collapsed)
-    {
-        ColumnInfoRecord ci = null;
-        int              k  = 0;
-
-        for (k = 0; k < records.size(); k++)
-        {
-            ci = ( ColumnInfoRecord ) records.get(k);
-            if ((ci.getFirstColumn() <= column)
-                    && (column <= ci.getLastColumn()))
-            {
-                break;
-            }
-            ci = null;
-        }
-
-        if (ci != null)
-        {
-           boolean styleChanged = xfIndex != null && ci.getXFIndex() != xfIndex.shortValue();
-            boolean widthChanged = width != null && ci.getColumnWidth() != width.shortValue();
-            boolean levelChanged = level != null && ci.getOutlineLevel() != level.intValue();
-            boolean hiddenChanged = hidden != null && ci.getHidden() != hidden.booleanValue();
-            boolean collapsedChanged = collapsed != null && ci.getCollapsed() != collapsed.booleanValue();
-            boolean columnChanged = styleChanged || widthChanged || levelChanged || hiddenChanged || collapsedChanged;
-            if (!columnChanged)
-            {
-                // do nothing...nothing changed.
-            }
-            else if ((ci.getFirstColumn() == column)
-                     && (ci.getLastColumn() == column))
-            {                               // if its only for this cell then
-                setColumnInfoFields( ci, xfIndex, width, level, hidden, collapsed );
-            }
-            else if ((ci.getFirstColumn() == column)
-                     || (ci.getLastColumn() == column))
-            {
-                // okay so the width is different but the first or last column == the column we'return setting
-                // we'll just divide the info and create a new one
-                if (ci.getFirstColumn() == column)
-                {
-                    ci.setFirstColumn(( short ) (column + 1));
-                }
-                else
-                {
-                    ci.setLastColumn(( short ) (column - 1));
-                }
-                ColumnInfoRecord nci = ( ColumnInfoRecord ) createColInfo();
-
-                nci.setFirstColumn(column);
-                nci.setLastColumn(column);
-                nci.setOptions(ci.getOptions());
-                nci.setXFIndex(ci.getXFIndex());
-                setColumnInfoFields( nci, xfIndex, width, level, hidden, collapsed );
-
-                insertColumn(k, nci);
-            }
-            else
-            {
-                //split to 3 records
-                short lastcolumn = ci.getLastColumn();
-                ci.setLastColumn(( short ) (column - 1));
-
-                ColumnInfoRecord nci = ( ColumnInfoRecord ) createColInfo();
-                nci.setFirstColumn(column);
-                nci.setLastColumn(column);
-                nci.setOptions(ci.getOptions());
-                nci.setXFIndex(ci.getXFIndex());
-                setColumnInfoFields( nci, xfIndex, width, level, hidden, collapsed );
-                insertColumn(++k, nci);
-
-                nci = ( ColumnInfoRecord ) createColInfo();
-                nci.setFirstColumn((short)(column+1));
-                nci.setLastColumn(lastcolumn);
-                nci.setOptions(ci.getOptions());
-                nci.setXFIndex(ci.getXFIndex());
-                nci.setColumnWidth(ci.getColumnWidth());
-                insertColumn(++k, nci);
-            }
-        }
-        else
-        {
-
-            // okay so there ISN'T a column info record that cover's this column so lets create one!
-            ColumnInfoRecord nci = ( ColumnInfoRecord ) createColInfo();
-
-            nci.setFirstColumn(column);
-            nci.setLastColumn(column);
-            setColumnInfoFields( nci, xfIndex, width, level, hidden, collapsed );
-            insertColumn(k, nci);
-        }
-    }
-
-    /**
-     * Sets all non null fields into the <code>ci</code> parameter.
-     */
-    private void setColumnInfoFields( ColumnInfoRecord ci, Short xfStyle, Short width, Integer level, Boolean hidden, Boolean collapsed )
-    {
-       if (xfStyle != null)
-           ci.setXFIndex(xfStyle.shortValue());
-        if (width != null)
-            ci.setColumnWidth(width.shortValue());
-        if (level != null)
-            ci.setOutlineLevel( level.shortValue() );
-        if (hidden != null)
-            ci.setHidden( hidden.booleanValue() );
-        if (collapsed != null)
-            ci.setCollapsed( collapsed.booleanValue() );
-    }
-
-    private int findColumnIdx(int column, int fromIdx)
-    {
-        if (column < 0)
-            throw new IllegalArgumentException( "column parameter out of range: " + column );
-        if (fromIdx < 0)
-            throw new IllegalArgumentException( "fromIdx parameter out of range: " + fromIdx );
-
-        ColumnInfoRecord ci;
-        for (int k = fromIdx; k < records.size(); k++)
-        {
-            ci = getColInfo(k);
-            if ((ci.getFirstColumn() <= column)
-                    && (column <= ci.getLastColumn()))
-            {
-                return k;
-            }
-            ci = null;
-        }
-        return -1;
-    }
-
-    public void collapseColInfoRecords( int columnIdx )
-    {
-        if (columnIdx == 0)
-            return;
-        ColumnInfoRecord previousCol = getColInfo( columnIdx - 1);
-        ColumnInfoRecord currentCol = getColInfo( columnIdx );
-        boolean adjacentColumns = previousCol.getLastColumn() == currentCol.getFirstColumn() - 1;
-        if (!adjacentColumns)
-            return;
-
-        boolean columnsMatch =
-                previousCol.getXFIndex() == currentCol.getXFIndex() &&
-                previousCol.getOptions() == currentCol.getOptions() &&
-                previousCol.getColumnWidth() == currentCol.getColumnWidth();
-
-        if (columnsMatch)
-        {
-            previousCol.setLastColumn( currentCol.getLastColumn() );
-            records.remove( columnIdx );
-        }
-    }
-
-    /**
-     * Creates an outline group for the specified columns.
-     * @param fromColumn    group from this column (inclusive)
-     * @param toColumn      group to this column (inclusive)
-     * @param indent        if true the group will be indented by one level,
-     *                      if false indenting will be removed by one level.
-     */
-    public void groupColumnRange(short fromColumn, short toColumn, boolean indent)
-    {
-
-        // Set the level for each column
-        int fromIdx = 0;
-        for (int i = fromColumn; i <= toColumn; i++)
-        {
-            int level = 1;
-            int columnIdx = findColumnIdx( i, Math.max(0,fromIdx) );
-            if (columnIdx != -1)
-            {
-                level = getColInfo(columnIdx).getOutlineLevel();
-                if (indent) level++; else level--;
-                level = Math.max(0, level);
-                level = Math.min(7, level);
-                fromIdx = columnIdx - 1; // subtract 1 just in case this column is collapsed later.
-            }
-            setColumn((short)i, null, null, new Integer(level), null, null);
-            columnIdx = findColumnIdx( i, Math.max(0, fromIdx ) );
-            collapseColInfoRecords( columnIdx );
-        }
-
-    }
-    /**
-     * Finds the <tt>ColumnInfoRecord</tt> which contains the specified columnIndex
-     * @param columnIndex index of the column (not the index of the ColumnInfoRecord)
-     * @return <code>null</code> if no column info found for the specified column
-     */
+       public ColumnInfoRecordsAggregate(RecordStream rs) {
+               this();
+
+               boolean isInOrder = true;
+               ColumnInfoRecord cirPrev = null;
+               while(rs.peekNextClass() == ColumnInfoRecord.class) {
+                       ColumnInfoRecord cir = (ColumnInfoRecord) rs.getNext();
+                       records.add(cir);
+                       if (cirPrev != null && CIRComparator.compareColInfos(cirPrev, cir) > 0) {
+                               isInOrder = false;
+                       }
+                       cirPrev = cir;
+               }
+               if (records.size() < 1) {
+                       throw new RuntimeException("No column info records found");
+               }
+               if (!isInOrder) {
+                       Collections.sort(records, CIRComparator.instance);
+               }
+       }
+
+       /**
+        * Performs a deep clone of the record
+        */
+       public Object clone() {
+               ColumnInfoRecordsAggregate rec = new ColumnInfoRecordsAggregate();
+               for (int k = 0; k < records.size(); k++) {
+                       ColumnInfoRecord ci = ( ColumnInfoRecord ) records.get(k);
+                       rec.records.add(ci.clone());
+               }
+               return rec;
+       }
+
+       /**
+        * Inserts a column into the aggregate (at the end of the list).
+        */
+       public void insertColumn(ColumnInfoRecord col) {
+               records.add(col);
+               Collections.sort(records, CIRComparator.instance);
+       }
+
+       /**
+        * Inserts a column into the aggregate (at the position specified by
+        * <code>idx</code>.
+        */
+       private void insertColumn(int idx, ColumnInfoRecord col) {
+               records.add(idx, col);
+       }
+
+       /* package */ int getNumColumns() {
+               return records.size();
+       }
+
+       public void visitContainedRecords(RecordVisitor rv) {
+               int nItems = records.size();
+               if (nItems < 1) {
+                       return;
+               }
+               ColumnInfoRecord cirPrev = null;
+               for(int i=0; i<nItems; i++) {
+                       ColumnInfoRecord cir = (ColumnInfoRecord)records.get(i);
+                       rv.visitRecord(cir);
+                       if (cirPrev != null && CIRComparator.compareColInfos(cirPrev, cir) > 0) {
+                               // Excel probably wouldn't mind, but there is much logic in this class
+                               // that assumes the column info records are kept in order
+                               throw new RuntimeException("Column info records are out of order");
+                       }
+                       cirPrev = cir;
+               }
+       }
+
+       private int findStartOfColumnOutlineGroup(int pIdx) {
+               // Find the start of the group.
+               ColumnInfoRecord columnInfo = (ColumnInfoRecord) records.get(pIdx);
+               int level = columnInfo.getOutlineLevel();
+               int idx = pIdx;
+               while (idx != 0) {
+                       ColumnInfoRecord prevColumnInfo = (ColumnInfoRecord) records.get(idx - 1);
+                       if (!prevColumnInfo.isAdjacentBefore(columnInfo)) {
+                               break;
+                       }
+                       if (prevColumnInfo.getOutlineLevel() < level) {
+                               break;
+                       }
+                       idx--;
+                       columnInfo = prevColumnInfo;
+               }
+
+               return idx;
+       }
+
+       private int findEndOfColumnOutlineGroup(int colInfoIndex) {
+               // Find the end of the group.
+               ColumnInfoRecord columnInfo = (ColumnInfoRecord) records.get(colInfoIndex);
+               int level = columnInfo.getOutlineLevel();
+               int idx = colInfoIndex;
+               while (idx < records.size() - 1) {
+                       ColumnInfoRecord nextColumnInfo = (ColumnInfoRecord) records.get(idx + 1);
+                       if (!columnInfo.isAdjacentBefore(nextColumnInfo)) {
+                               break;
+                       }
+                       if (nextColumnInfo.getOutlineLevel() < level) {
+                               break;
+                       }
+                       idx++;
+                       columnInfo = nextColumnInfo;
+               }
+               return idx;
+       }
+
+       private ColumnInfoRecord getColInfo(int idx) {
+               return (ColumnInfoRecord) records.get( idx );
+       }
+
+       /**
+        * 'Collapsed' state is stored in a single column col info record immediately after the outline group
+        * @param idx
+        * @return
+        */
+       private boolean isColumnGroupCollapsed(int idx) {
+               int endOfOutlineGroupIdx = findEndOfColumnOutlineGroup(idx);
+               int nextColInfoIx = endOfOutlineGroupIdx+1;
+               if (nextColInfoIx >= records.size()) {
+                       return false;
+               }
+               ColumnInfoRecord nextColInfo = getColInfo(nextColInfoIx);
+               if (!getColInfo(endOfOutlineGroupIdx).isAdjacentBefore(nextColInfo)) {
+                       return false;
+               }
+               return nextColInfo.getCollapsed();
+       }
+
+
+       private boolean isColumnGroupHiddenByParent(int idx) {
+               // Look out outline details of end
+               int endLevel = 0;
+               boolean endHidden = false;
+               int endOfOutlineGroupIdx = findEndOfColumnOutlineGroup( idx );
+               if (endOfOutlineGroupIdx < records.size()) {
+                       ColumnInfoRecord nextInfo = getColInfo(endOfOutlineGroupIdx + 1);
+                       if (getColInfo(endOfOutlineGroupIdx).isAdjacentBefore(nextInfo)) {
+                               endLevel = nextInfo.getOutlineLevel();
+                               endHidden = nextInfo.getHidden();
+                       }
+               }
+               // Look out outline details of start
+               int startLevel = 0;
+               boolean startHidden = false;
+               int startOfOutlineGroupIdx = findStartOfColumnOutlineGroup( idx );
+               if (startOfOutlineGroupIdx > 0) {
+                       ColumnInfoRecord prevInfo = getColInfo(startOfOutlineGroupIdx - 1);
+                       if (prevInfo.isAdjacentBefore(getColInfo(startOfOutlineGroupIdx))) {
+                               startLevel = prevInfo.getOutlineLevel();
+                               startHidden = prevInfo.getHidden();
+                       }
+               }
+               if (endLevel > startLevel) {
+                       return endHidden;
+               }
+               return startHidden;
+       }
+
+       public void collapseColumn(int columnIndex) {
+               int colInfoIx = findColInfoIdx(columnIndex, 0);
+               if (colInfoIx == -1) {
+                       return;
+               }
+
+               // Find the start of the group.
+               int groupStartColInfoIx = findStartOfColumnOutlineGroup(colInfoIx);
+               ColumnInfoRecord columnInfo = getColInfo(groupStartColInfoIx);
+
+               // Hide all the columns until the end of the group
+               int lastColIx = setGroupHidden(groupStartColInfoIx, columnInfo.getOutlineLevel(), true);
+
+               // Write collapse field
+               setColumn(lastColIx + 1, null, null, null, null, Boolean.TRUE);
+       }
+       /**
+        * Sets all adjacent columns of the same outline level to the specified hidden status.
+        * @param pIdx the col info index of the start of the outline group
+        * @return the column index of the last column in the outline group
+        */
+       private int setGroupHidden(int pIdx, int level, boolean hidden) {
+               int idx = pIdx;
+               ColumnInfoRecord columnInfo = getColInfo(idx);
+               while (idx < records.size()) {
+                       columnInfo.setHidden(hidden);
+                       if (idx + 1 < records.size()) {
+                               ColumnInfoRecord nextColumnInfo = getColInfo(idx + 1);
+                               if (!columnInfo.isAdjacentBefore(nextColumnInfo)) {
+                                       break;
+                               }
+                               if (nextColumnInfo.getOutlineLevel() < level) {
+                                       break;
+                               }
+                               columnInfo = nextColumnInfo;
+                       }
+                       idx++;
+               }
+               return columnInfo.getLastColumn();
+       }
+
+
+       public void expandColumn(int columnIndex) {
+               int idx = findColInfoIdx(columnIndex, 0);
+               if (idx == -1) {
+                       return;
+               }
+
+               // If it is already expanded do nothing.
+               if (!isColumnGroupCollapsed(idx)) {
+                       return;
+               }
+
+               // Find the start/end of the group.
+               int startIdx = findStartOfColumnOutlineGroup(idx);
+               int endIdx = findEndOfColumnOutlineGroup(idx);
+
+               // expand:
+               // colapsed bit must be unset
+               // hidden bit gets unset _if_ surrounding groups are expanded you can determine
+               //   this by looking at the hidden bit of the enclosing group.  You will have
+               //   to look at the start and the end of the current group to determine which
+               //   is the enclosing group
+               // hidden bit only is altered for this outline level.  ie.  don't uncollapse contained groups
+               ColumnInfoRecord columnInfo = getColInfo(endIdx);
+               if (!isColumnGroupHiddenByParent(idx)) {
+                       int outlineLevel = columnInfo.getOutlineLevel();
+                       for (int i = startIdx; i <= endIdx; i++) {
+                               ColumnInfoRecord ci = getColInfo(i);
+                               if (outlineLevel == ci.getOutlineLevel())
+                                       ci.setHidden(false);
+                       }
+               }
+
+               // Write collapse flag (stored in a single col info record after this outline group)
+               setColumn(columnInfo.getLastColumn() + 1, null, null, null, null, Boolean.FALSE);
+       }
+
+       private static ColumnInfoRecord copyColInfo(ColumnInfoRecord ci) {
+               return (ColumnInfoRecord) ci.clone();
+       }
+
+
+       public void setColumn(int targetColumnIx, Short xfIndex, Short width, 
+                                       Integer level, Boolean hidden, Boolean collapsed) {
+               ColumnInfoRecord ci = null;
+               int k  = 0;
+
+               for (k = 0; k < records.size(); k++) {
+                       ColumnInfoRecord tci = (ColumnInfoRecord) records.get(k);
+                       if (tci.containsColumn(targetColumnIx)) {
+                               ci = tci;
+                               break;
+                       }
+                       if (tci.getFirstColumn() > targetColumnIx) {
+                               // call column infos after k are for later columns
+                               break; // exit now so k will be the correct insert pos
+                       }
+               }
+
+               if (ci == null) {
+                       // okay so there ISN'T a column info record that covers this column so lets create one!
+                       ColumnInfoRecord nci = new ColumnInfoRecord();
+
+                       nci.setFirstColumn(targetColumnIx);
+                       nci.setLastColumn(targetColumnIx);
+                       setColumnInfoFields( nci, xfIndex, width, level, hidden, collapsed );
+                       insertColumn(k, nci);
+                       attemptMergeColInfoRecords(k);
+                       return;
+               }
+
+               boolean styleChanged = xfIndex != null && ci.getXFIndex() != xfIndex.shortValue();
+               boolean widthChanged = width != null && ci.getColumnWidth() != width.shortValue();
+               boolean levelChanged = level != null && ci.getOutlineLevel() != level.intValue();
+               boolean hiddenChanged = hidden != null && ci.getHidden() != hidden.booleanValue();
+               boolean collapsedChanged = collapsed != null && ci.getCollapsed() != collapsed.booleanValue();
+
+               boolean columnChanged = styleChanged || widthChanged || levelChanged || hiddenChanged || collapsedChanged;
+               if (!columnChanged) {
+                       // do nothing...nothing changed.
+                       return;
+               }
+
+               if (ci.getFirstColumn() == targetColumnIx && ci.getLastColumn() == targetColumnIx) {
+                       // ColumnInfo ci for a single column, the target column
+                       setColumnInfoFields(ci, xfIndex, width, level, hidden, collapsed);
+                       attemptMergeColInfoRecords(k);
+                       return;
+               }
+
+               if (ci.getFirstColumn() == targetColumnIx || ci.getLastColumn() == targetColumnIx) {
+                       // The target column is at either end of the multi-column ColumnInfo ci
+                       // we'll just divide the info and create a new one
+                       if (ci.getFirstColumn() == targetColumnIx) {
+                               ci.setFirstColumn(targetColumnIx + 1);
+                       } else {
+                               ci.setLastColumn(targetColumnIx - 1);
+                               k++; // adjust insert pos to insert after
+                       }
+                       ColumnInfoRecord nci = copyColInfo(ci);
+
+                       nci.setFirstColumn(targetColumnIx);
+                       nci.setLastColumn(targetColumnIx);
+                       setColumnInfoFields( nci, xfIndex, width, level, hidden, collapsed );
+
+                       insertColumn(k, nci);
+                       attemptMergeColInfoRecords(k);
+               } else {
+                       //split to 3 records
+                       ColumnInfoRecord ciStart = ci;
+                       ColumnInfoRecord ciMid = copyColInfo(ci);
+                       ColumnInfoRecord ciEnd = copyColInfo(ci);
+                       int lastcolumn = ci.getLastColumn();
+                       
+                       ciStart.setLastColumn(targetColumnIx - 1);
+
+                       ciMid.setFirstColumn(targetColumnIx);
+                       ciMid.setLastColumn(targetColumnIx);
+                       setColumnInfoFields(ciMid, xfIndex, width, level, hidden, collapsed);
+                       insertColumn(++k, ciMid);
+                       
+                       ciEnd.setFirstColumn(targetColumnIx+1);
+                       ciEnd.setLastColumn(lastcolumn);
+                       insertColumn(++k, ciEnd);
+                       // no need to attemptMergeColInfoRecords because we 
+                       // know both on each side are different
+               }
+       }
+
+       /**
+        * Sets all non null fields into the <code>ci</code> parameter.
+        */
+       private static void setColumnInfoFields( ColumnInfoRecord ci, Short xfStyle, Short width, 
+                               Integer level, Boolean hidden, Boolean collapsed ) {
+               if (xfStyle != null) {
+                       ci.setXFIndex(xfStyle.shortValue());
+               }
+               if (width != null) {
+                       ci.setColumnWidth(width.shortValue());
+               }
+               if (level != null) {
+                       ci.setOutlineLevel( level.shortValue() );
+               }
+               if (hidden != null) {
+                       ci.setHidden( hidden.booleanValue() );
+               }
+               if (collapsed != null) {
+                       ci.setCollapsed( collapsed.booleanValue() );
+               }
+       }
+
+       private int findColInfoIdx(int columnIx, int fromColInfoIdx) {
+               if (columnIx < 0) {
+                       throw new IllegalArgumentException( "column parameter out of range: " + columnIx );
+               }
+               if (fromColInfoIdx < 0) {
+                       throw new IllegalArgumentException( "fromIdx parameter out of range: " + fromColInfoIdx );
+               }
+
+               for (int k = fromColInfoIdx; k < records.size(); k++) {
+                       ColumnInfoRecord ci = getColInfo(k);
+                       if (ci.containsColumn(columnIx)) {
+                               return k;
+                       }
+                       if (ci.getFirstColumn() > columnIx) {
+                               break;
+                       }
+               }
+               return -1;
+       }
+
+       /**
+        * Attempts to merge the col info record at the specified index 
+        * with either or both of its neighbours
+        */
+       private void attemptMergeColInfoRecords(int colInfoIx) {
+               int nRecords = records.size();
+               if (colInfoIx < 0 || colInfoIx >= nRecords) {
+                       throw new IllegalArgumentException("colInfoIx " + colInfoIx 
+                                       + " is out of range (0.." + (nRecords-1) + ")");
+               }
+               ColumnInfoRecord currentCol = getColInfo(colInfoIx);
+               int nextIx = colInfoIx+1;
+               if (nextIx < nRecords) {
+                       if (mergeColInfoRecords(currentCol, getColInfo(nextIx))) {
+                       records.remove(nextIx);
+                       }
+               }
+               if (colInfoIx > 0) {
+                       if (mergeColInfoRecords(getColInfo(colInfoIx - 1), currentCol)) {
+                       records.remove(colInfoIx);
+               }
+               }
+       }
+       /**
+        * merges two column info records (if they are adjacent and have the same formatting, etc)
+        * @return <code>false</code> if the two column records could not be merged
+        */
+       private static boolean mergeColInfoRecords(ColumnInfoRecord ciA, ColumnInfoRecord ciB) {
+               if (ciA.isAdjacentBefore(ciB) && ciA.formatMatches(ciB)) {
+                       ciA.setLastColumn(ciB.getLastColumn());
+                       return true;
+               }
+               return false;
+       }
+       /**
+        * Creates an outline group for the specified columns, by setting the level
+        * field for each col info record in the range. {@link ColumnInfoRecord}s
+        * may be created, split or merged as a result of this operation.
+        * 
+        * @param fromColumnIx
+        *            group from this column (inclusive)
+        * @param toColumnIx
+        *            group to this column (inclusive)
+        * @param indent
+        *            if <code>true</code> the group will be indented by one
+        *            level, if <code>false</code> indenting will be decreased by
+        *            one level.
+        */
+       public void groupColumnRange(int fromColumnIx, int toColumnIx, boolean indent) {
+
+               int colInfoSearchStartIdx = 0; // optimization to speed up the search for col infos
+               for (int i = fromColumnIx; i <= toColumnIx; i++) {
+                       int level = 1;
+                       int colInfoIdx = findColInfoIdx(i, colInfoSearchStartIdx);
+                       if (colInfoIdx != -1) {
+                               level = getColInfo(colInfoIdx).getOutlineLevel();
+                               if (indent) {
+                                       level++;
+                               } else {
+                                       level--;
+                               }
+                               level = Math.max(0, level);
+                               level = Math.min(7, level);
+                               colInfoSearchStartIdx = Math.max(0, colInfoIdx - 1); // -1 just in case this column is collapsed later.
+                       }
+                       setColumn(i, null, null, new Integer(level), null, null);
+               }
+       }
+       /**
+        * Finds the <tt>ColumnInfoRecord</tt> which contains the specified columnIndex
+        * @param columnIndex index of the column (not the index of the ColumnInfoRecord)
+        * @return <code>null</code> if no column info found for the specified column
+        */
        public ColumnInfoRecord findColumnInfo(int columnIndex) {
                int nInfos = records.size();
                for(int i=0; i< nInfos; i++) {
                        ColumnInfoRecord ci = getColInfo(i);
-                       if (ci.getFirstColumn() <= columnIndex && columnIndex <= ci.getLastColumn()) {
+                       if (ci.containsColumn(columnIndex)) {
                                return ci;
                        }
                }
                return null;
        }
        public int getMaxOutlineLevel() {
-        int result = 0;
-        int count=records.size();
-        for (int i=0; i<count; i++) {
-            ColumnInfoRecord columnInfoRecord = getColInfo(i);
-            result = Math.max(columnInfoRecord.getOutlineLevel(), result);
-        }
-        return result;
+               int result = 0;
+               int count=records.size();
+               for (int i=0; i<count; i++) {
+                       ColumnInfoRecord columnInfoRecord = getColInfo(i);
+                       result = Math.max(columnInfoRecord.getOutlineLevel(), result);
+               }
+               return result;
        }
-
-
 }
index 68d5f453dce300e1abb1a2119fd767fc18846f6a..06bb53a38dcdc24fcf0690029963da11625f81c2 100644 (file)
@@ -20,6 +20,7 @@ package org.apache.poi.hssf.record.aggregates;
 import org.apache.poi.hssf.record.CellValueRecordInterface;
 import org.apache.poi.hssf.record.FormulaRecord;
 import org.apache.poi.hssf.record.Record;
+import org.apache.poi.hssf.record.RecordFormatException;
 import org.apache.poi.hssf.record.StringRecord;
 
 /**
@@ -34,9 +35,9 @@ public final class FormulaRecordAggregate extends RecordAggregate implements Cel
     private SharedValueManager _sharedValueManager;
     /** caches the calculated result of the formula */
     private StringRecord _stringRecord;
-    
+
     /**
-     * @param stringRec may be <code>null</code> if this formula does not have a cached text 
+     * @param stringRec may be <code>null</code> if this formula does not have a cached text
      * value.
      * @param svm the {@link SharedValueManager} for the current sheet
      */
@@ -44,6 +45,14 @@ public final class FormulaRecordAggregate extends RecordAggregate implements Cel
         if (svm == null) {
             throw new IllegalArgumentException("sfm must not be null");
         }
+        boolean hasStringRec = stringRec != null;
+        boolean hasCachedStringFlag = formulaRec.hasCachedResultString();
+               if (hasStringRec != hasCachedStringFlag) {
+                       throw new RecordFormatException("String record was "
+                                       + (hasStringRec ? "": "not ") + " supplied but formula record flag is "
+                                       + (hasCachedStringFlag ? "" : "not ") + " set");
+               }
+               
         if (formulaRec.isSharedFormula()) {
             svm.convertSharedFormulaRecord(formulaRec);
         }
@@ -52,18 +61,18 @@ public final class FormulaRecordAggregate extends RecordAggregate implements Cel
         _stringRecord = stringRec;
     }
 
-    public void setStringRecord(StringRecord stringRecord) {
-        _stringRecord = stringRecord;
-    }
-    
     public FormulaRecord getFormulaRecord() {
         return _formulaRecord;
     }
 
+    /**
+     * debug only
+     * TODO - encapsulate
+     */
     public StringRecord getStringRecord() {
         return _stringRecord;
     }
-    
+
     public short getXFIndex() {
         return _formulaRecord.getXFIndex();
     }
@@ -91,7 +100,7 @@ public final class FormulaRecordAggregate extends RecordAggregate implements Cel
     public String toString() {
         return _formulaRecord.toString();
     }
-   
+
     public void visitContainedRecords(RecordVisitor rv) {
          rv.visitRecord(_formulaRecord);
          Record sharedFormulaRecord = _sharedValueManager.getRecordForFirstCell(_formulaRecord);
@@ -102,11 +111,33 @@ public final class FormulaRecordAggregate extends RecordAggregate implements Cel
              rv.visitRecord(_stringRecord);
          }
     }
-   
+
     public String getStringValue() {
         if(_stringRecord==null) {
             return null;
         }
         return _stringRecord.getString();
     }
+
+    public void setCachedStringResult(String value) {
+
+        // Save the string into a String Record, creating one if required
+        if(_stringRecord == null) {
+            _stringRecord = new StringRecord();
+        }
+        _stringRecord.setString(value);
+        if (value.length() < 1) {
+            _formulaRecord.setCachedResultTypeEmptyString();
+        } else {
+            _formulaRecord.setCachedResultTypeString();
+        }
+    }
+    public void setCachedBooleanResult(boolean value) {
+        _stringRecord = null;
+        _formulaRecord.setCachedResultBoolean(value);
+    }
+    public void setCachedErrorResult(int errorCode) {
+        _stringRecord = null;
+        _formulaRecord.setCachedResultErrorCode(errorCode);
+    }
 }
index 2193854f47c9e76439e2168ddeebc97f55f82e25..68ae5b48c1a49ce30c68d10623bea1e1f88861dd 100644 (file)
@@ -18,7 +18,7 @@
 package org.apache.poi.hssf.record.formula;\r
 \r
 import org.apache.poi.hssf.record.RecordInputStream;\r
-import org.apache.poi.hssf.usermodel.HSSFWorkbook;\r
+import org.apache.poi.ss.usermodel.Workbook;\r
 import org.apache.poi.util.LittleEndian;\r
 \r
 /**\r
@@ -45,7 +45,7 @@ public abstract class Area2DPtgBase extends AreaPtgBase {
        public final int getSize() {\r
                return SIZE;\r
        }\r
-       public final String toFormulaString(HSSFWorkbook book) {\r
+       public final String toFormulaString(Workbook book) {\r
        return formatReferenceAsString();\r
        }\r
     public final String toString() {\r
index 49994c059752037b79d8b4162a21095e28cc7cb3..48acaac20564f9b3798f49b1a309d1ecc76f3bce 100644 (file)
@@ -44,8 +44,11 @@ public final class ArrayPtg extends Ptg {
         * (not including the data which comes after all formula tokens)
         */
        public static final int PLAIN_TOKEN_SIZE = 1+RESERVED_FIELD_LEN;
+
+       private static final byte[] DEFAULT_RESERVED_DATA = new byte[RESERVED_FIELD_LEN];
+
        // TODO - fix up field visibility and subclasses
-       private byte[] field_1_reserved;
+       private final byte[] field_1_reserved;
        
        // data from these fields comes after the Ptg data of all tokens in current formula
        private short  token_1_columns;
@@ -59,8 +62,42 @@ public final class ArrayPtg extends Ptg {
                        field_1_reserved[i] = in.readByte();
                }
        }
-       public Object[] getTokenArrayValues() {
-               return (Object[]) token_3_arrayValues.clone();
+       /**
+        * @param values2d array values arranged in rows
+        */
+       public ArrayPtg(Object[][] values2d) {
+               int nColumns = values2d[0].length;
+               int nRows = values2d.length;
+               // convert 2-d to 1-d array (row by row according to getValueIndex())
+               token_1_columns = (short) nColumns;
+               token_2_rows = (short) nRows;
+
+               Object[] vv = new Object[token_1_columns * token_2_rows];
+               for (int r=0; r<nRows; r++) {
+                       Object[] rowData = values2d[r];
+                       for (int c=0; c<nColumns; c++) {
+                               vv[getValueIndex(c, r)] = rowData[c];
+                       }
+               }
+               
+               token_3_arrayValues = vv;
+               field_1_reserved = DEFAULT_RESERVED_DATA;
+       }
+       /**
+        * @return 2-d array (inner index is rowIx, outer index is colIx)
+        */
+       public Object[][] getTokenArrayValues() {
+               if (token_3_arrayValues == null) {
+                       throw new IllegalStateException("array values not read yet");
+               }
+               Object[][] result = new Object[token_2_rows][token_1_columns];
+               for (int r = 0; r < token_2_rows; r++) {
+                       Object[] rowData = result[r];
+                       for (int c = 0; c < token_1_columns; c++) {
+                               rowData[c] = token_3_arrayValues[getValueIndex(c, r)];
+                       }
+               }
+               return result;
        }
        
        public boolean isBaseToken() {
@@ -88,27 +125,21 @@ public final class ArrayPtg extends Ptg {
                token_3_arrayValues = ConstantValueParser.parse(in, totalCount);
        }
 
-       public String toString()
-       {
-               StringBuffer buffer = new StringBuffer("[ArrayPtg]\n");
+       public String toString() {
+               StringBuffer sb = new StringBuffer("[ArrayPtg]\n");
 
-               buffer.append("columns = ").append(getColumnCount()).append("\n");
-               buffer.append("rows = ").append(getRowCount()).append("\n");
+               sb.append("nRows = ").append(getRowCount()).append("\n");
+               sb.append("nCols = ").append(getColumnCount()).append("\n");
                if (token_3_arrayValues == null) {
-                       buffer.append("  #values#uninitialised#\n");
+                       sb.append("  #values#uninitialised#\n");
                } else {
-                       for (int x=0;x<getColumnCount();x++) {
-                               for (int y=0;y<getRowCount();y++) {
-                                       Object o = token_3_arrayValues[getValueIndex(x, y)];
-                                       buffer.append("[").append(x).append("][").append(y).append("] = ").append(o).append("\n"); 
-                               }
-                       }
+                       sb.append("  ").append(formatAsString());
                }
-               return buffer.toString();
+               return sb.toString();
        }
 
        /**
-        * Note - (2D) array elements are stored column by column 
+        * Note - (2D) array elements are stored row by row
         * @return the index into the internal 1D array for the specified column and row
         */
        /* package */ int getValueIndex(int colIx, int rowIx) {
@@ -120,7 +151,7 @@ public final class ArrayPtg extends Ptg {
                        throw new IllegalArgumentException("Specified rowIx (" + rowIx 
                                        + ") is outside the allowed range (0.." + (token_2_rows-1) + ")");
                }
-               return rowIx + token_2_rows * colIx;
+               return rowIx * token_1_columns + colIx;
        }
 
        public void writeBytes(byte[] data, int offset) {
@@ -153,16 +184,15 @@ public final class ArrayPtg extends Ptg {
                        + ConstantValueParser.getEncodedSize(token_3_arrayValues);
        }
 
-       public String toFormulaString(Workbook book)
-       {
+       public String formatAsString() {
                StringBuffer b = new StringBuffer();
                b.append("{");
-               for (int x=0;x<getColumnCount();x++) {
-                       if (x > 0) {
+               for (int y=0;y<getRowCount();y++) {
+                       if (y > 0) {
                                b.append(";");
                        }
-                       for (int y=0;y<getRowCount();y++) {
-                               if (y > 0) {
+                       for (int x=0;x<getColumnCount();x++) {
+                               if (x > 0) {
                                        b.append(",");
                                }
                                Object o = token_3_arrayValues[getValueIndex(x, y)];
@@ -172,11 +202,14 @@ public final class ArrayPtg extends Ptg {
                b.append("}");
                return b.toString();
        }
+       public String toFormulaString(Workbook book) {
+               return formatAsString();
+       }
        
        private static String getConstantText(Object o) {
 
                if (o == null) {
-                       return ""; // TODO - how is 'empty value' represented in formulas?
+                       throw new RuntimeException("Array item cannot be null");
                }
                if (o instanceof UnicodeString) {
                        return "\"" + ((UnicodeString)o).getString() + "\"";
@@ -196,11 +229,4 @@ public final class ArrayPtg extends Ptg {
        public byte getDefaultOperandClass() {
                return Ptg.CLASS_ARRAY;
        }
-       
-       public Object clone() {
-         ArrayPtg ptg = (ArrayPtg) super.clone();
-         ptg.field_1_reserved = (byte[]) field_1_reserved.clone();
-         ptg.token_3_arrayValues = (Object[]) token_3_arrayValues.clone();
-         return ptg;
-       }
 }
index aef580d5e430124445b747d3d92e2991819a1262..d94f2a38072658d53fe51d91203c5240473e02b2 100644 (file)
@@ -24,12 +24,12 @@ import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
  * @author Daniel Noll (daniel at nuix dot com dot au)
  */
 public final class ErrPtg extends ScalarConstantPtg {
-    
+
     // convenient access to namespace
     private static final HSSFErrorConstants EC = null;
-    
+
     /** <b>#NULL!</b>  - Intersection of two cell ranges is empty */
-    public static final ErrPtg NULL_INTERSECTION = new ErrPtg(EC.ERROR_NULL); 
+    public static final ErrPtg NULL_INTERSECTION = new ErrPtg(EC.ERROR_NULL);
     /** <b>#DIV/0!</b> - Division by zero */
     public static final ErrPtg DIV_ZERO = new ErrPtg(EC.ERROR_DIV_0);
     /** <b>#VALUE!</b> - Wrong type of operand */
@@ -37,28 +37,28 @@ public final class ErrPtg extends ScalarConstantPtg {
     /** <b>#REF!</b> - Illegal or deleted cell reference */
     public static final ErrPtg REF_INVALID = new ErrPtg(EC.ERROR_REF);
     /** <b>#NAME?</b> - Wrong function or range name */
-    public static final ErrPtg NAME_INVALID = new ErrPtg(EC.ERROR_NAME); 
+    public static final ErrPtg NAME_INVALID = new ErrPtg(EC.ERROR_NAME);
     /** <b>#NUM!</b> - Value range overflow */
     public static final ErrPtg NUM_ERROR = new ErrPtg(EC.ERROR_NUM);
     /** <b>#N/A</b> - Argument or function not available */
     public static final ErrPtg N_A = new ErrPtg(EC.ERROR_NA);
-    
-    
+
+
     public static final short sid  = 0x1c;
     private static final int  SIZE = 2;
     private final int field_1_error_code;
 
     /** Creates new ErrPtg */
 
-    public ErrPtg(int errorCode) {
+    private ErrPtg(int errorCode) {
         if(!HSSFErrorConstants.isValidCode(errorCode)) {
             throw new IllegalArgumentException("Invalid error code (" + errorCode + ")");
         }
         field_1_error_code = errorCode;
     }
-    public ErrPtg(RecordInputStream in) {
-        this(in.readByte());
+
+    public static ErrPtg read(RecordInputStream in) {
+        return valueOf(in.readByte());
     }
 
     public void writeBytes(byte [] array, int offset)
@@ -78,4 +78,17 @@ public final class ErrPtg extends ScalarConstantPtg {
     public int getErrorCode() {
         return field_1_error_code;
     }
+
+    public static ErrPtg valueOf(int code) {
+        switch(code) {
+            case HSSFErrorConstants.ERROR_DIV_0: return DIV_ZERO;
+            case HSSFErrorConstants.ERROR_NA: return N_A;
+            case HSSFErrorConstants.ERROR_NAME: return NAME_INVALID;
+            case HSSFErrorConstants.ERROR_NULL: return NULL_INTERSECTION;
+            case HSSFErrorConstants.ERROR_NUM: return NUM_ERROR;
+            case HSSFErrorConstants.ERROR_REF: return REF_INVALID;
+            case HSSFErrorConstants.ERROR_VALUE: return VALUE_INVALID;
+        }
+        throw new RuntimeException("Unexpected error code (" + code + ")");
+    }
 }
index 296b691cda291dabad0284a6531917c66ad140d7..630803c3cbe915ab2ee7b750eda3accc03bae0db 100644 (file)
@@ -56,20 +56,21 @@ public abstract class Ptg implements Cloneable {
        /**
         * @deprecated - use readTokens()
         */
-       public static Stack createParsedExpressionTokens(short size, RecordInputStream in)
-       {
+       public static Stack createParsedExpressionTokens(short size, RecordInputStream in) {
                Stack stack = new Stack();
                int pos = 0;
                List arrayPtgs = null;
-               while ( pos < size )
-               {
+               while (pos < size) {
                        Ptg ptg = Ptg.createPtg( in );
                        if (ptg instanceof ArrayPtg) {
-                               if (arrayPtgs == null)
+                               if (arrayPtgs == null) {
                                        arrayPtgs = new ArrayList(5);
+                               }
                                arrayPtgs.add(ptg);
-                               pos += 8;
-                       } else pos += ptg.getSize();
+                               pos += ArrayPtg.PLAIN_TOKEN_SIZE;
+                       } else {
+                               pos += ptg.getSize();
+                       }
                        stack.push( ptg );
                }
                if(pos != size) {
@@ -109,25 +110,25 @@ public abstract class Ptg implements Cloneable {
                int baseId = id & 0x1F | 0x20;
                
                switch (baseId) {
-                       case ArrayPtg.sid:     return new ArrayPtg(in);    // 0x20, 0x40, 0x60
-                        case FuncPtg.sid:     return new FuncPtg(in);     // 0x21, 0x41, 0x61
-                        case FuncVarPtg.sid:  return new FuncVarPtg(in);  // 0x22, 0x42, 0x62
-                        case NamePtg.sid:     return new NamePtg(in);     // 0x23, 0x43, 0x63
-                        case RefPtg.sid:      return new RefPtg(in);      // 0x24, 0x44, 0x64
-                        case AreaPtg.sid:     return new AreaPtg(in);     // 0x25, 0x45, 0x65
-                        case MemAreaPtg.sid:  return new MemAreaPtg(in);  // 0x26, 0x46, 0x66
-                        case MemErrPtg.sid:   return new MemErrPtg(in);   // 0x27, 0x47, 0x67
-                        case MemFuncPtg.sid:  return new MemFuncPtg(in);  // 0x29, 0x49, 0x69
-                        case RefErrorPtg.sid: return  new RefErrorPtg(in);// 0x2a, 0x4a, 0x6a
-                        case AreaErrPtg.sid:  return new AreaErrPtg(in);  // 0x2b, 0x4b, 0x6b
-                        case RefNPtg.sid:     return new RefNPtg(in);     // 0x2c, 0x4c, 0x6c
-                        case AreaNPtg.sid:    return new AreaNPtg(in);    // 0x2d, 0x4d, 0x6d
+                       case ArrayPtg.sid:    return new ArrayPtg(in);    // 0x20, 0x40, 0x60
+                       case FuncPtg.sid:     return new FuncPtg(in);     // 0x21, 0x41, 0x61
+                       case FuncVarPtg.sid:  return new FuncVarPtg(in);  // 0x22, 0x42, 0x62
+                       case NamePtg.sid:     return new NamePtg(in);     // 0x23, 0x43, 0x63
+                       case RefPtg.sid:      return new RefPtg(in);      // 0x24, 0x44, 0x64
+                       case AreaPtg.sid:     return new AreaPtg(in);     // 0x25, 0x45, 0x65
+                       case MemAreaPtg.sid:  return new MemAreaPtg(in);  // 0x26, 0x46, 0x66
+                       case MemErrPtg.sid:   return new MemErrPtg(in);   // 0x27, 0x47, 0x67
+                       case MemFuncPtg.sid:  return new MemFuncPtg(in);  // 0x29, 0x49, 0x69
+                       case RefErrorPtg.sid: return new RefErrorPtg(in); // 0x2a, 0x4a, 0x6a
+                       case AreaErrPtg.sid:  return new AreaErrPtg(in);  // 0x2b, 0x4b, 0x6b
+                       case RefNPtg.sid:     return new RefNPtg(in);     // 0x2c, 0x4c, 0x6c
+                       case AreaNPtg.sid:    return new AreaNPtg(in);    // 0x2d, 0x4d, 0x6d
 
-                        case NameXPtg.sid:    return new NameXPtg(in);    // 0x39, 0x49, 0x79
-                        case Ref3DPtg.sid:    return  new Ref3DPtg(in);   // 0x3a, 0x5a, 0x7a
-                        case Area3DPtg.sid:   return new Area3DPtg(in);   // 0x3b, 0x5b, 0x7b
-                        case DeletedRef3DPtg.sid:  return new DeletedRef3DPtg(in);   // 0x3c, 0x5c, 0x7c
-                        case DeletedArea3DPtg.sid: return  new DeletedArea3DPtg(in); // 0x3d, 0x5d, 0x7d
+                       case NameXPtg.sid:    return new NameXPtg(in);    // 0x39, 0x49, 0x79
+                       case Ref3DPtg.sid:    return new Ref3DPtg(in);    // 0x3a, 0x5a, 0x7a
+                       case Area3DPtg.sid:   return new Area3DPtg(in);   // 0x3b, 0x5b, 0x7b
+                       case DeletedRef3DPtg.sid:  return new DeletedRef3DPtg(in);   // 0x3c, 0x5c, 0x7c
+                       case DeletedArea3DPtg.sid: return  new DeletedArea3DPtg(in); // 0x3d, 0x5d, 0x7d
                }
                throw new UnsupportedOperationException(" Unknown Ptg in Formula: 0x"+
                                   Integer.toHexString(id) + " (" + ( int ) id + ")");
@@ -162,7 +163,7 @@ public abstract class Ptg implements Cloneable {
                        case StringPtg.sid:       return new StringPtg(in);       // 0x17
                        case AttrPtg.sid:                
                        case 0x1a:        return new AttrPtg(in); // 0x19
-                       case ErrPtg.sid:          return new ErrPtg(in);          // 0x1c
+                       case ErrPtg.sid:          return ErrPtg.read(in);         // 0x1c
                        case BoolPtg.sid:         return new BoolPtg(in);         // 0x1d
                        case IntPtg.sid:          return new IntPtg(in);          // 0x1e
                        case NumberPtg.sid:       return new NumberPtg(in);       // 0x1f
@@ -262,11 +263,14 @@ public abstract class Ptg implements Cloneable {
 
                        ptg.writeBytes(array, pos + offset);
                        if (ptg instanceof ArrayPtg) {
-                         if (arrayPtgs == null)
-                                 arrayPtgs = new ArrayList(5);
-                         arrayPtgs.add(ptg);
-                         pos += 8;
-                       } else pos += ptg.getSize();
+                               if (arrayPtgs == null) {
+                                       arrayPtgs = new ArrayList(5);
+                               }
+                               arrayPtgs.add(ptg);
+                               pos += ArrayPtg.PLAIN_TOKEN_SIZE;
+                       } else {
+                               pos += ptg.getSize();
+                       }
                }
                if (arrayPtgs != null) {
                        for (int i=0;i<arrayPtgs.size();i++) {
index 87a6462b7deb2477e27f79f60f539cfd46b51690..efeb5c484b0882258e2ead6ad53623e3917826d0 100644 (file)
@@ -24,15 +24,14 @@ 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.ss.usermodel.Sheet;
 import org.apache.poi.ss.usermodel.Workbook;
 
 public final class AnalysisToolPak {
 
        private static final FreeRefFunction NotImplemented = new FreeRefFunction() {
 
-               public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol,
-                               Workbook workbook, Sheet sheet) {
+               public ValueEval evaluate(Eval[] args, Workbook workbook, int srcCellSheet,
+                               int srcCellRow, int srcCellCol) {
                        return ErrorEval.FUNCTION_NOT_IMPLEMENTED;
                }
        };
index 71cfa03591e2d086f5a6888d090f0108e9a6373a..b76ca9601912503bcfcaa09ff47a400aed8a1c49 100644 (file)
@@ -42,8 +42,8 @@ final class ParityFunction implements FreeRefFunction {
                _desiredParity = desiredParity;
        }
 
-       public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, Workbook workbook,
-                       Sheet sheet) {
+       public ValueEval evaluate(Eval[] args, Workbook workbook, int srcCellSheet, int srcCellRow,
+                       int srcCellCol) {
                if (args.length != 1) {
                        return ErrorEval.VALUE_INVALID;  
                }
@@ -58,8 +58,8 @@ final class ParityFunction implements FreeRefFunction {
                return BoolEval.valueOf(val == _desiredParity);
        }
 
-       private static int evaluateArgParity(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException {
-               ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
+       private static int evaluateArgParity(Eval arg, int srcCellRow, int srcCellCol) throws EvaluationException {
+               ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, (short)srcCellCol);
                
                double d = OperandResolver.coerceValueToDouble(ve);
                if (d < 0) {
index f5bc3fe8f41d0e167c28e03d03b6047546cc3053..59c6aea89d9798b998381b6e55f5fe13ba4b2d0c 100644 (file)
@@ -61,8 +61,8 @@ final class YearFrac implements FreeRefFunction {
                // enforce singleton
        }
 
-       public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, Workbook workbook,
-                       Sheet sheet) {
+       public ValueEval evaluate(Eval[] args, Workbook workbook, int srcCellSheet, int srcCellRow,
+                       int srcCellCol) {
 
                double result;
                try {
@@ -85,8 +85,8 @@ final class YearFrac implements FreeRefFunction {
                return new NumberEval(result);
        }
 
-       private static double evaluateDateArg(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException {
-               ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
+       private static double evaluateDateArg(Eval arg, int srcCellRow, int srcCellCol) throws EvaluationException {
+               ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, (short) srcCellCol);
 
                if (ve instanceof StringEval) {
                        String strVal = ((StringEval) ve).getStringValue();
@@ -155,8 +155,8 @@ final class YearFrac implements FreeRefFunction {
                return cal;
        }
 
-       private static int evaluateIntArg(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException {
-               ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
+       private static int evaluateIntArg(Eval arg, int srcCellRow, int srcCellCol) throws EvaluationException {
+               ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, (short) srcCellCol);
                return OperandResolver.coerceValueToInt(ve);
        }
 }
index e54cd483f1972238d06d5fd74be2135be449c1f9..6755cfafef926465fb0f9b59204f65f3850b62aa 100644 (file)
@@ -1,19 +1,19 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
 
 package org.apache.poi.hssf.record.formula.eval;
 
@@ -24,7 +24,7 @@ import org.apache.poi.hssf.record.formula.Ptg;
  * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
  *  
  */
-public final class ConcatEval extends StringOperationEval {
+public final class ConcatEval implements OperationEval {
 
     private ConcatPtg delegate;
 
@@ -37,20 +37,23 @@ public final class ConcatEval extends StringOperationEval {
                return ErrorEval.VALUE_INVALID;
        }
         StringBuffer sb = new StringBuffer();
-        for (int i = 0; i < 2; i++) { 
-            
-            ValueEval ve = singleOperandEvaluate(args[i], srcRow, srcCol);
-            if (ve instanceof StringValueEval) {
-                StringValueEval sve = (StringValueEval) ve;
-                sb.append(sve.getStringValue());
-            }
-            else if (ve instanceof BlankEval) {
-                // do nothing
-            }
-            else { // must be an error eval
-                return ve;
-            }
-        }
+        try {
+                       for (int i = 0; i < 2; i++) { 
+                           
+                           ValueEval ve = OperandResolver.getSingleValue(args[i], srcRow, srcCol);
+                           if (ve instanceof StringValueEval) {
+                               StringValueEval sve = (StringValueEval) ve;
+                               sb.append(sve.getStringValue());
+                           } else if (ve == BlankEval.INSTANCE) {
+                               // do nothing
+                           } else { // must be an error eval
+                               throw new RuntimeException("Unexpected value type (" 
+                                               + ve.getClass().getName() + ")");
+                           }
+                       }
+               } catch (EvaluationException e) {
+                       return e.getErrorEval();
+               }
         
         return new StringEval(sb.toString());
     }
index 95dd0b545b11f6ed9a9010e243c88a173bb05f02..3782c6c4de5fe90ba95edb2473fbda7e80ec469f 100755 (executable)
@@ -19,7 +19,6 @@ 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.ss.usermodel.Sheet;
 import org.apache.poi.ss.usermodel.Workbook;
 /**
  * 
@@ -31,7 +30,8 @@ import org.apache.poi.ss.usermodel.Workbook;
  */
 final class ExternalFunction implements FreeRefFunction {
 
-       public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, Workbook workbook, Sheet sheet) {
+       public ValueEval evaluate(Eval[] args, Workbook workbook, 
+                       int srcCellSheet, int srcCellRow,int srcCellCol) {
                
                int nIncomingArgs = args.length;
                if(nIncomingArgs < 1) {
@@ -55,7 +55,7 @@ final class ExternalFunction implements FreeRefFunction {
                int nOutGoingArgs = nIncomingArgs -1;
                Eval[] outGoingArgs = new Eval[nOutGoingArgs];
                System.arraycopy(args, 1, outGoingArgs, 0, nOutGoingArgs);
-               return targetFunc.evaluate(outGoingArgs, srcCellRow, srcCellCol, workbook, sheet);
+               return targetFunc.evaluate(outGoingArgs, workbook, srcCellSheet, srcCellRow, srcCellCol);
        }
 
        private FreeRefFunction findExternalUserDefinedFunction(Workbook workbook,
index a1c7356fa90984d98422aa9783cf6823e9455b16..e68b8ee67dd7b7dd76163ae1fbac5818dc7dceda 100644 (file)
@@ -1,23 +1,20 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 8, 2005
- *
- */
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
 package org.apache.poi.hssf.record.formula.eval;
 
 import java.util.HashMap;
@@ -81,42 +78,42 @@ public abstract class FunctionEval implements OperationEval {
         retval[1] = new If(); // IF
         retval[2] = new IsNa(); // ISNA
         retval[3] = new IsError(); // ISERROR
-        retval[4] = new Sum(); // SUM
-        retval[5] = new Average(); // AVERAGE
-        retval[6] = new Min(); // MIN
-        retval[7] = new Max(); // MAX
+        retval[4] = AggregateFunction.SUM;
+        retval[5] = AggregateFunction.AVERAGE;
+        retval[6] = AggregateFunction.MIN;
+        retval[7] = AggregateFunction.MAX;
         retval[8] = new Row(); // ROW
         retval[9] = new Column(); // COLUMN
         retval[10] = new Na(); // NA
         retval[11] = new Npv(); // NPV
-        retval[12] = new Stdev(); // STDEV
-        retval[13] = NumericFunctionOneArg.DOLLAR;
+        retval[12] = AggregateFunction.STDEV;
+        retval[13] = NumericFunction.DOLLAR;
         retval[14] = new Fixed(); // FIXED
-        retval[15] = NumericFunctionOneArg.SIN;
-        retval[16] = NumericFunctionOneArg.COS;
-        retval[17] = NumericFunctionOneArg.TAN;
-        retval[18] = NumericFunctionOneArg.ATAN;
+        retval[15] = NumericFunction.SIN;
+        retval[16] = NumericFunction.COS;
+        retval[17] = NumericFunction.TAN;
+        retval[18] = NumericFunction.ATAN;
         retval[19] = new Pi(); // PI
-        retval[20] = NumericFunctionOneArg.SQRT;
-        retval[21] = NumericFunctionOneArg.EXP;
-        retval[22] = NumericFunctionOneArg.LN;
-        retval[23] = NumericFunctionOneArg.LOG10;
-        retval[24] = NumericFunctionOneArg.ABS;
-        retval[25] = NumericFunctionOneArg.INT;
-        retval[26] = NumericFunctionOneArg.SIGN;
-        retval[27] = new Round(); // ROUND
+        retval[20] = NumericFunction.SQRT;
+        retval[21] = NumericFunction.EXP;
+        retval[22] = NumericFunction.LN;
+        retval[23] = NumericFunction.LOG10;
+        retval[24] = NumericFunction.ABS;
+        retval[25] = NumericFunction.INT;
+        retval[26] = NumericFunction.SIGN;
+        retval[27] = NumericFunction.ROUND;
         retval[28] = new Lookup(); // LOOKUP
         retval[29] = new Index(); // INDEX
         retval[30] = new Rept(); // REPT
-        retval[31] = new Mid(); // MID
-        retval[32] = new Len(); // LEN
+        retval[31] = TextFunction.MID;
+        retval[32] = TextFunction.LEN;
         retval[33] = new Value(); // VALUE
         retval[34] = new True(); // TRUE
         retval[35] = new False(); // FALSE
         retval[36] = new And(); // AND
         retval[37] = new Or(); // OR
         retval[38] = new Not(); // NOT
-        retval[39] = new Mod(); // MOD
+        retval[39] = NumericFunction.MOD;
         retval[40] = new Dcount(); // DCOUNT
         retval[41] = new Dsum(); // DSUM
         retval[42] = new Daverage(); // DAVERAGE
@@ -132,16 +129,16 @@ public abstract class FunctionEval implements OperationEval {
         retval[52] = new Growth(); // GROWTH
         retval[53] = new Goto(); // GOTO
         retval[54] = new Halt(); // HALT
-        retval[56] = new Pv(); // PV
-        retval[57] = new Fv(); // FV
-        retval[58] = new Nper(); // NPER
-        retval[59] = new Pmt(); // PMT
+        retval[56] = FinanceFunction.PV;
+        retval[57] = FinanceFunction.FV;
+        retval[58] = FinanceFunction.NPER;
+        retval[59] = FinanceFunction.PMT;
         retval[60] = new Rate(); // RATE
         retval[61] = new Mirr(); // MIRR
         retval[62] = new Irr(); // IRR
         retval[63] = new Rand(); // RAND
         retval[64] = new Match(); // MATCH
-        retval[65] = new Date(); // DATE
+        retval[65] = DateFunc.instance; // DATE
         retval[66] = new Time(); // TIME
         retval[67] = CalendarFieldFunction.DAY; // DAY
         retval[68] = CalendarFieldFunction.MONTH; // MONTH
@@ -173,9 +170,9 @@ public abstract class FunctionEval implements OperationEval {
         retval[94] = new Activecell(); // ACTIVECELL
         retval[95] = new NotImplementedFunction(); // SELECTION
         retval[96] = new Result(); // RESULT
-        retval[97] = new Atan2(); // ATAN2
-        retval[98] = NumericFunctionOneArg.ASIN;
-        retval[99] = NumericFunctionOneArg.ACOS;
+        retval[97] = NumericFunction.ATAN2;
+        retval[98] = NumericFunction.ASIN;
+        retval[99] = NumericFunction.ACOS;
         retval[100] = new Choose(); // CHOOSE
         retval[101] = new Hlookup(); // HLOOKUP
         retval[102] = new Vlookup(); // VLOOKUP
@@ -185,16 +182,16 @@ public abstract class FunctionEval implements OperationEval {
         retval[106] = new NotImplementedFunction(); // GETFORMULA
         retval[107] = new NotImplementedFunction(); // GETNAME
         retval[108] = new Setvalue(); // SETVALUE
-        retval[109] = new Log(); // LOG
+        retval[109] = NumericFunction.LOG;
         retval[110] = new Exec(); // EXEC
         retval[111] = new Char(); // CHAR
-        retval[112] = new Lower(); // LOWER
-        retval[113] = new Upper(); // UPPER
+        retval[112] = TextFunction.LOWER;
+        retval[113] = TextFunction.UPPER;
         retval[114] = new Proper(); // PROPER
-        retval[115] = new Left(); // LEFT
-        retval[116] = new Right(); // RIGHT
-        retval[117] = new Exact(); // EXACT
-        retval[118] = new Trim(); // TRIM
+        retval[115] = TextFunction.LEFT;
+        retval[116] = TextFunction.RIGHT;
+        retval[117] = TextFunction.EXACT;
+        retval[118] = TextFunction.TRIM;
         retval[119] = new Replace(); // REPLACE
         retval[120] = new Substitute(); // SUBSTITUTE
         retval[121] = new Code(); // CODE
@@ -255,8 +252,8 @@ public abstract class FunctionEval implements OperationEval {
         retval[180] = new NotImplementedFunction(); // RESTART
         retval[181] = new Help(); // HELP
         retval[182] = new NotImplementedFunction(); // GETBAR
-        retval[183] = new Product(); // PRODUCT
-        retval[184] = NumericFunctionOneArg.FACT;
+        retval[183] = AggregateFunction.PRODUCT;
+        retval[184] = NumericFunction.FACT;
         retval[185] = new NotImplementedFunction(); // GETCELL
         retval[186] = new NotImplementedFunction(); // GETWORKSPACE
         retval[187] = new NotImplementedFunction(); // GETWINDOW
@@ -282,8 +279,8 @@ public abstract class FunctionEval implements OperationEval {
         retval[209] = new Rightb(); // RIGHTB
         retval[210] = new Midb(); // MIDB
         retval[211] = new Lenb(); // LENB
-        retval[212] = new Roundup(); // ROUNDUP
-        retval[213] = new Rounddown(); // ROUNDDOWN
+        retval[212] = NumericFunction.ROUNDUP;
+        retval[213] = NumericFunction.ROUNDDOWN;
         retval[214] = new Asc(); // ASC
         retval[215] = new Dbcs(); // DBCS
         retval[216] = new Rank(); // RANK
@@ -291,14 +288,14 @@ public abstract class FunctionEval implements OperationEval {
         retval[220] = new Days360(); // DAYS360
         retval[221] = new Today(); // TODAY
         retval[222] = new Vdb(); // VDB
-        retval[227] = new Median(); // MEDIAN
+        retval[227] = AggregateFunction.MEDIAN;
         retval[228] = new Sumproduct(); // SUMPRODUCT
-        retval[229] = NumericFunctionOneArg.SINH;
-        retval[230] = NumericFunctionOneArg.COSH;
-        retval[231] = NumericFunctionOneArg.TANH;
-        retval[232] = NumericFunctionOneArg.ASINH;
-        retval[233] = NumericFunctionOneArg.ACOSH;
-        retval[234] = NumericFunctionOneArg.ATANH;
+        retval[229] = NumericFunction.SINH;
+        retval[230] = NumericFunction.COSH;
+        retval[231] = NumericFunction.TANH;
+        retval[232] = NumericFunction.ASINH;
+        retval[233] = NumericFunction.ACOSH;
+        retval[234] = NumericFunction.ATANH;
         retval[235] = new Dget(); // DGET
         retval[236] = new NotImplementedFunction(); // CREATEOBJECT
         retval[237] = new Volatile(); // VOLATILE
@@ -331,14 +328,14 @@ public abstract class FunctionEval implements OperationEval {
         retval[266] = new NotImplementedFunction(); // PRESSTOOL
         retval[267] = new NotImplementedFunction(); // REGISTERID
         retval[268] = new NotImplementedFunction(); // GETWORKBOOK
-        retval[269] = new Avedev(); // AVEDEV
+        retval[269] = AggregateFunction.AVEDEV;
         retval[270] = new Betadist(); // BETADIST
         retval[271] = new Gammaln(); // GAMMALN
         retval[272] = new Betainv(); // BETAINV
         retval[273] = new Binomdist(); // BINOMDIST
         retval[274] = new Chidist(); // CHIDIST
         retval[275] = new Chiinv(); // CHIINV
-        retval[276] = new Combin(); // COMBIN
+        retval[276] = NumericFunction.COMBIN;
         retval[277] = new Confidence(); // CONFIDENCE
         retval[278] = new Critbinom(); // CRITBINOM
         retval[279] = new Even(); // EVEN
@@ -347,10 +344,10 @@ public abstract class FunctionEval implements OperationEval {
         retval[282] = new Finv(); // FINV
         retval[283] = new Fisher(); // FISHER
         retval[284] = new Fisherinv(); // FISHERINV
-        retval[285] = new Floor(); // FLOOR
+        retval[285] = NumericFunction.FLOOR;
         retval[286] = new Gammadist(); // GAMMADIST
         retval[287] = new Gammainv(); // GAMMAINV
-        retval[288] = new Ceiling(); // CEILING
+        retval[288] = NumericFunction.CEILING;
         retval[289] = new Hypgeomdist(); // HYPGEOMDIST
         retval[290] = new Lognormdist(); // LOGNORMDIST
         retval[291] = new Loginv(); // LOGINV
@@ -380,15 +377,15 @@ public abstract class FunctionEval implements OperationEval {
         retval[315] = new Slope(); // SLOPE
         retval[316] = new Ttest(); // TTEST
         retval[317] = new Prob(); // PROB
-        retval[318] = new Devsq(); // DEVSQ
+        retval[318] = AggregateFunction.DEVSQ;
         retval[319] = new Geomean(); // GEOMEAN
         retval[320] = new Harmean(); // HARMEAN
-        retval[321] = new Sumsq(); // SUMSQ
+        retval[321] = AggregateFunction.SUMSQ;
         retval[322] = new Kurt(); // KURT
         retval[323] = new Skew(); // SKEW
         retval[324] = new Ztest(); // ZTEST
-        retval[325] = new Large(); // LARGE
-        retval[326] = new Small(); // SMALL
+        retval[325] = AggregateFunction.LARGE;
+        retval[326] = AggregateFunction.SMALL;
         retval[327] = new Quartile(); // QUARTILE
         retval[328] = new Percentile(); // PERCENTILE
         retval[329] = new Percentrank(); // PERCENTRANK
@@ -397,14 +394,14 @@ public abstract class FunctionEval implements OperationEval {
         retval[332] = new Tinv(); // TINV
         retval[334] = new NotImplementedFunction(); // MOVIECOMMAND
         retval[335] = new NotImplementedFunction(); // GETMOVIE
-        retval[336] = new Concatenate(); // CONCATENATE
-        retval[337] = new Power(); // POWER
+        retval[336] = TextFunction.CONCATENATE;
+        retval[337] = NumericFunction.POWER;
         retval[338] = new NotImplementedFunction(); // PIVOTADDDATA
         retval[339] = new NotImplementedFunction(); // GETPIVOTTABLE
         retval[340] = new NotImplementedFunction(); // GETPIVOTFIELD
         retval[341] = new NotImplementedFunction(); // GETPIVOTITEM
-        retval[342] = NumericFunctionOneArg.RADIANS;
-        retval[343] = NumericFunctionOneArg.DEGREES;
+        retval[342] = NumericFunction.RADIANS;
+        retval[343] = NumericFunction.DEGREES;
         retval[344] = new Subtotal(); // SUBTOTAL
         retval[345] = new Sumif(); // SUMIF
         retval[346] = new Countif(); // COUNTIF
@@ -423,8 +420,8 @@ public abstract class FunctionEval implements OperationEval {
         retval[359] = new Hyperlink(); // HYPERLINK
         retval[360] = new NotImplementedFunction(); // PHONETIC
         retval[361] = new Averagea(); // AVERAGEA
-        retval[362] = new Maxa(); // MAXA
-        retval[363] = new Mina(); // MINA
+        retval[362] = MinaMaxa.MAXA;
+        retval[363] = MinaMaxa.MINA;
         retval[364] = new Stdevpa(); // STDEVPA
         retval[365] = new Varpa(); // VARPA
         retval[366] = new Stdeva(); // STDEVA
diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/LazyAreaEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/LazyAreaEval.java
deleted file mode 100644 (file)
index 9f38305..0000000
+++ /dev/null
@@ -1,80 +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.record.formula.eval;
-
-import org.apache.poi.hssf.record.formula.AreaI;
-import org.apache.poi.hssf.record.formula.AreaI.OffsetArea;
-import org.apache.poi.ss.usermodel.Cell;
-import org.apache.poi.ss.usermodel.FormulaEvaluator;
-import org.apache.poi.ss.usermodel.Row;
-import org.apache.poi.ss.usermodel.Sheet;
-import org.apache.poi.ss.usermodel.Workbook;
-import org.apache.poi.ss.util.CellReference;
-
-/**
- *
- * @author Josh Micich 
- */
-public final class LazyAreaEval extends AreaEvalBase {
-
-       private final Sheet _sheet;
-       private FormulaEvaluator _evaluator;
-
-       public LazyAreaEval(AreaI ptg, Sheet sheet, FormulaEvaluator evaluator) {
-               super(ptg);
-               _sheet = sheet;
-               _evaluator = evaluator;
-       }
-
-       public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) { 
-               
-               int rowIx = (relativeRowIndex + getFirstRow() ) & 0xFFFF;
-               int colIx = (relativeColumnIndex + getFirstColumn() ) & 0x00FF;
-               
-               Row row = _sheet.getRow(rowIx);
-               if (row == null) {
-                       return BlankEval.INSTANCE;
-               }
-               Cell cell = row.getCell(colIx);
-               if (cell == null) {
-                       return BlankEval.INSTANCE;
-               }
-               return _evaluator.getEvalForCell(cell, _sheet);
-       }
-
-       public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) {
-               AreaI area = new OffsetArea(getFirstRow(), getFirstColumn(),
-                               relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);
-
-               return new LazyAreaEval(area, _sheet, _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/record/formula/eval/LazyRefEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/LazyRefEval.java
deleted file mode 100644 (file)
index b458011..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.record.formula.eval;\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.ss.usermodel.Cell;\r
-import org.apache.poi.ss.usermodel.FormulaEvaluator;\r
-import org.apache.poi.ss.usermodel.Row;\r
-import org.apache.poi.ss.usermodel.Sheet;\r
-import org.apache.poi.ss.usermodel.Workbook;\r
-import org.apache.poi.ss.util.CellReference;\r
-\r
-/**\r
-*\r
-* @author Josh Micich \r
-*/\r
-public final class LazyRefEval extends RefEvalBase {\r
-       private final Sheet _sheet;\r
-       private final FormulaEvaluator _evaluator;\r
-\r
-\r
-       public LazyRefEval(RefPtg ptg, Sheet sheet, FormulaEvaluator evaluator) {\r
-               super(ptg.getRow(), ptg.getColumn());\r
-               _sheet = sheet;\r
-               _evaluator = evaluator;\r
-       }\r
-       public LazyRefEval(Ref3DPtg ptg, Sheet sheet, FormulaEvaluator 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
-               Row row = _sheet.getRow(rowIx);\r
-               if (row == null) {\r
-                       return BlankEval.INSTANCE;\r
-               }\r
-               Cell cell = row.getCell(colIx);\r
-               if (cell == null) {\r
-                       return BlankEval.INSTANCE;\r
-               }\r
-               return _evaluator.getEvalForCell(cell, _sheet);\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
index 87f45236bfac4a5086ff7206e55962c92842091f..09be70477f0694c8a16845ea9ad488f9524ed46c 100755 (executable)
@@ -272,12 +272,7 @@ public final class OperandResolver {
                        StringValueEval sve = (StringValueEval) ve;
                        return sve.getStringValue();
                }
-               if (ve instanceof NumberEval) {
-                       NumberEval neval = (NumberEval) ve;
-                       return neval.getStringValue();
-               } 
-
-               if (ve instanceof BlankEval) {
+               if (ve == BlankEval.INSTANCE) {
                        return "";
                }
                throw new IllegalArgumentException("Unexpected eval class (" + ve.getClass().getName() + ")");
@@ -289,7 +284,7 @@ public final class OperandResolver {
         */
        public static Boolean coerceValueToBoolean(ValueEval ve, boolean stringsAreBlanks) throws EvaluationException {
 
-               if (ve == null || ve instanceof BlankEval) {
+               if (ve == null || ve == BlankEval.INSTANCE) {
                        // TODO - remove 've == null' condition once AreaEval is fixed
                        return null;
                }
@@ -297,7 +292,7 @@ public final class OperandResolver {
                        return Boolean.valueOf(((BoolEval) ve).getBooleanValue());
                }
 
-               if (ve instanceof BlankEval) {
+               if (ve == BlankEval.INSTANCE) {
                        return null;
                }
 
diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/Ref2DEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/Ref2DEval.java
deleted file mode 100644 (file)
index e4bb47a..0000000
+++ /dev/null
@@ -1,53 +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.record.formula.eval;
-
-import org.apache.poi.hssf.record.formula.RefPtg;
-
-/**
- * @author adeshmukh
- *  
- */
-public final class Ref2DEval implements RefEval {
-
-    private final ValueEval value;
-    private final RefPtg delegate;
-    
-    public Ref2DEval(RefPtg ptg, ValueEval ve) {
-        if(ve == null) {
-            throw new IllegalArgumentException("ve must not be null");
-        }
-        if(false && ptg == null) { // TODO - fix dodgy code in MultiOperandNumericFunction
-            throw new IllegalArgumentException("ptg must not be null");
-        }
-        value = ve;
-        delegate = ptg;
-    }
-    public ValueEval getInnerValueEval() {
-        return value;
-    }
-    public int getRow() {
-        return delegate.getRow();
-    }
-    public int getColumn() {
-        return delegate.getColumn();
-    }
-    public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) {
-        throw new RuntimeException("should not be called"); // TODO - delete this whole class
-    }
-}
diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/ValueEvalToNumericXlator.java b/src/java/org/apache/poi/hssf/record/formula/eval/ValueEvalToNumericXlator.java
deleted file mode 100644 (file)
index 46aa0dd..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 14, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.eval;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *
- */
-public final class ValueEvalToNumericXlator {
-
-    public static final int STRING_IS_PARSED = 0x0001;
-    public static final int BOOL_IS_PARSED = 0x0002;
-    public static final int BLANK_IS_PARSED = 0x0004; // => blanks are not ignored, converted to 0
-    
-    public static final int REF_STRING_IS_PARSED = 0x0008;
-    public static final int REF_BOOL_IS_PARSED = 0x0010;
-    public static final int REF_BLANK_IS_PARSED = 0x0020;
-    
-    public static final int STRING_IS_INVALID_VALUE = 0x0800;
-    
-    private final int flags;
-    
-    
-    public ValueEvalToNumericXlator(int flags) {
-       
-       if (false) { // uncomment to see who is using this class
-                       System.err.println(new Throwable().getStackTrace()[1].getClassName() + "\t0x"
-                                       + Integer.toHexString(flags).toUpperCase());
-               }
-               this.flags = flags;
-    }
-    
-    /**
-     * returned value can be either A NumericValueEval, BlankEval or ErrorEval.
-     * The params can be either NumberEval, BoolEval, StringEval, or
-     * RefEval
-     * @param eval
-     */
-    public ValueEval attemptXlateToNumeric(ValueEval eval) {
-        
-        if (eval == null) {
-            throw new IllegalArgumentException("eval must not be null");
-        }
-        
-        // most common case - least worries :)
-        if (eval instanceof NumberEval) {
-            return eval; 
-        }
-        
-        if (eval instanceof BoolEval) {
-            return ((flags & BOOL_IS_PARSED) > 0)
-                ? (NumericValueEval) eval
-                : xlateBlankEval(BLANK_IS_PARSED);
-        } 
-        
-        if (eval instanceof StringEval) {
-            return xlateStringEval((StringEval) eval); // TODO: recursive call needed
-        }
-        
-        if (eval instanceof RefEval) {
-            return xlateRefEval((RefEval) eval);
-        }
-        
-        if (eval instanceof ErrorEval) {
-            return eval;
-        }
-        
-        if (eval instanceof BlankEval) {
-            return xlateBlankEval(BLANK_IS_PARSED);
-        }
-        
-        // probably AreaEval? then not acceptable.
-        throw new RuntimeException("Invalid ValueEval type passed for conversion: " + eval.getClass());
-    }
-    
-    /**
-     * no args are required since BlankEval has only one 
-     * instance. If flag is set, a zero
-     * valued numbereval is returned, else BlankEval.INSTANCE
-     * is returned.
-     */
-    private ValueEval xlateBlankEval(int flag) {
-        return ((flags & flag) > 0)
-                ? (ValueEval) NumberEval.ZERO
-                : BlankEval.INSTANCE;
-    }
-    
-    /**
-     * uses the relevant flags to decode the supplied RefVal
-     * @param eval
-     */
-    private ValueEval xlateRefEval(RefEval reval) {
-        ValueEval eval = reval.getInnerValueEval();
-        
-        // most common case - least worries :)
-        if (eval instanceof NumberEval) {
-            return eval;
-        }
-        
-        if (eval instanceof BoolEval) {
-            return ((flags & REF_BOOL_IS_PARSED) > 0)
-                    ? (ValueEval) eval
-                    : BlankEval.INSTANCE;
-        } 
-        
-        if (eval instanceof StringEval) {
-            return xlateRefStringEval((StringEval) eval);
-        }
-        
-        if (eval instanceof ErrorEval) {
-            return eval;
-        }
-        
-        if (eval instanceof BlankEval) {
-            return xlateBlankEval(REF_BLANK_IS_PARSED);
-        }
-        
-        throw new RuntimeException("Invalid ValueEval type passed for conversion: ("
-                       + eval.getClass().getName() + ")");
-    }
-    
-    /**
-     * uses the relevant flags to decode the StringEval
-     * @param eval
-     */
-    private ValueEval xlateStringEval(StringEval eval) {
-
-        if ((flags & STRING_IS_PARSED) > 0) {
-            String s = eval.getStringValue();
-            Double d = OperandResolver.parseDouble(s);
-            if(d == null) {
-                return ErrorEval.VALUE_INVALID;
-            }
-            return new NumberEval(d.doubleValue());
-        }
-        // strings are errors?
-        if ((flags & STRING_IS_INVALID_VALUE) > 0) {
-            return ErrorEval.VALUE_INVALID;
-        }
-        
-        // ignore strings
-        return xlateBlankEval(BLANK_IS_PARSED);
-    }
-    
-    /**
-     * uses the relevant flags to decode the StringEval
-     * @param eval
-     */
-    private ValueEval xlateRefStringEval(StringEval sve) {
-        if ((flags & REF_STRING_IS_PARSED) > 0) {
-            String s = sve.getStringValue();
-            Double d = OperandResolver.parseDouble(s);
-            if(d == null) {
-                return ErrorEval.VALUE_INVALID;
-            }
-            return new NumberEval(d.doubleValue());
-        }
-        // strings are blanks
-        return BlankEval.INSTANCE;
-    }
-}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/AggregateFunction.java b/src/java/org/apache/poi/hssf/record/formula/functions/AggregateFunction.java
new file mode 100644 (file)
index 0000000..0495ee3
--- /dev/null
@@ -0,0 +1,113 @@
+/* ====================================================================
+   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.EvaluationException;
+
+/**
+ * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
+ *
+ */
+public abstract class AggregateFunction extends MultiOperandNumericFunction {
+
+       protected AggregateFunction() {
+               super(false, false);
+       }
+
+       /* ---------------------------------------------------------------------- */
+
+       public static final Function AVEDEV = new AggregateFunction() {
+               protected double evaluate(double[] values) {
+                       return StatsLib.avedev(values);
+               }
+       };
+       public static final Function AVERAGE = new AggregateFunction() {
+               protected double evaluate(double[] values) throws EvaluationException {
+                       if (values.length < 1) {
+                               throw new EvaluationException(ErrorEval.DIV_ZERO);
+                       }
+                       return MathX.average(values);
+               }
+       };
+       public static final Function DEVSQ = new AggregateFunction() {
+               protected double evaluate(double[] values) {
+                       return StatsLib.devsq(values);
+               }
+       };
+       public static final Function LARGE = new AggregateFunction() {
+               protected double evaluate(double[] ops) throws EvaluationException {
+                       if (ops.length < 2) {
+                               throw new EvaluationException(ErrorEval.NUM_ERROR);
+                       }
+                       double[] values = new double[ops.length-1];
+                       int k = (int) ops[ops.length-1];
+                       System.arraycopy(ops, 0, values, 0, values.length);
+                       return StatsLib.kthLargest(values, k);
+               }
+       };
+       public static final Function MAX = new AggregateFunction() {
+               protected double evaluate(double[] values) {
+                       return values.length > 0 ? MathX.max(values) : 0;
+               }
+       };
+       public static final Function MEDIAN = new AggregateFunction() {
+               protected double evaluate(double[] values) {
+                       return StatsLib.median(values);
+               }
+       };
+       public static final Function MIN = new AggregateFunction() {
+               protected double evaluate(double[] values) {
+                       return values.length > 0 ? MathX.min(values) : 0;
+               }
+       };
+       public static final Function PRODUCT = new AggregateFunction() {
+               protected double evaluate(double[] values) {
+                       return MathX.product(values);
+               }
+       };
+       public static final Function SMALL = new AggregateFunction() {
+               protected double evaluate(double[] ops) throws EvaluationException {
+                       if (ops.length < 2) {
+                               throw new EvaluationException(ErrorEval.NUM_ERROR);
+                       }
+                       double[] values = new double[ops.length-1];
+                       int k = (int) ops[ops.length-1];
+                       System.arraycopy(ops, 0, values, 0, values.length);
+                       return StatsLib.kthSmallest(values, k);
+               }
+       };
+       public static final Function STDEV = new AggregateFunction() {
+               protected double evaluate(double[] values) throws EvaluationException {
+                       if (values.length < 1) {
+                               throw new EvaluationException(ErrorEval.DIV_ZERO);
+                       }
+                       return StatsLib.stdev(values);
+               }
+       };
+       public static final Function SUM = new AggregateFunction() {
+               protected double evaluate(double[] values) {
+                       return MathX.sum(values);
+               }
+       };
+       public static final Function SUMSQ = new AggregateFunction() {
+               protected double evaluate(double[] values) {
+                       return MathX.sumsq(values);
+               }
+       };
+}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Atan2.java b/src/java/org/apache/poi/hssf/record/formula/functions/Atan2.java
deleted file mode 100644 (file)
index a0ff7b0..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 6, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.functions;
-
-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.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *
- */
-public class Atan2 extends NumericFunction {
-
-    public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
-        double d0 = 0;
-        double d1 = 0;
-        ValueEval retval = null;
-
-        switch (operands.length) {
-        default:
-            retval = ErrorEval.VALUE_INVALID;
-            break;
-        case 2:
-            ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
-            if (ve instanceof NumericValueEval) {
-                NumericValueEval ne = (NumericValueEval) ve;
-                d0 = ne.getNumberValue();
-            }
-            else if (ve instanceof BlankEval) {
-                // do nothing
-            }
-            else {
-                retval = ErrorEval.NUM_ERROR;
-            }
-
-            if (retval == null) {
-                ve = singleOperandEvaluate(operands[1], srcRow, srcCol);
-                if (ve instanceof NumericValueEval) {
-                    NumericValueEval ne = (NumericValueEval) ve;
-                    d1 = ne.getNumberValue();
-                }
-                else if (ve instanceof BlankEval) {
-                    // do nothing
-                }
-                else {
-                    retval = ErrorEval.NUM_ERROR;
-                }
-            }
-        }
-
-        if (retval == null) {
-            double d = (d0 == d1 && d1 == 0)
-                    ? Double.NaN
-                    : Math.atan2(d1, d0);
-            retval = (Double.isNaN(d) || Double.isInfinite(d))
-                    ? (ValueEval) ErrorEval.NUM_ERROR
-                    : new NumberEval(d);
-        }
-        return retval;
-    }
-
-}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Avedev.java b/src/java/org/apache/poi/hssf/record/formula/functions/Avedev.java
deleted file mode 100644 (file)
index bf888b9..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
-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.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *
- */
-public class Avedev extends MultiOperandNumericFunction {
-
-    private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
-        new ValueEvalToNumericXlator((short) (
-                  ValueEvalToNumericXlator.BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED  
-                | ValueEvalToNumericXlator.STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
-                ));
-    
-    /**
-     * this is the default impl for the factory method getXlator
-     * of the super class NumericFunction. Subclasses can override this method
-     * if they desire to return a different ValueEvalToNumericXlator instance
-     * than the default.
-     */
-    protected ValueEvalToNumericXlator getXlator() {
-        return DEFAULT_NUM_XLATOR;
-    }
-
-    
-    
-    public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
-        ValueEval retval = null;
-        double[] values = getNumberArray(operands, srcCellRow, srcCellCol);
-        if (values == null) {
-            retval = ErrorEval.VALUE_INVALID;
-        }
-        else {
-            double d = StatsLib.avedev(values);
-            retval = (Double.isNaN(d) || Double.isInfinite(d))
-                    ? (ValueEval) ErrorEval.NUM_ERROR
-                    : new NumberEval(d);
-        }
-        
-        return retval;
-    }
-}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Average.java b/src/java/org/apache/poi/hssf/record/formula/functions/Average.java
deleted file mode 100644 (file)
index 4043040..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
-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.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *
- */
-public class Average extends MultiOperandNumericFunction {
-
-    private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
-        new ValueEvalToNumericXlator((short) (
-                    ValueEvalToNumericXlator.BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED  
-                | ValueEvalToNumericXlator.STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
-                ));
-    
-    /**
-     * this is the default impl for the factory method getXlator
-     * of the super class NumericFunction. Subclasses can override this method
-     * if they desire to return a different ValueEvalToNumericXlator instance
-     * than the default.
-     */
-    protected ValueEvalToNumericXlator getXlator() {
-        return DEFAULT_NUM_XLATOR;
-    }
-
-    
-    
-    public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
-        ValueEval retval = null;
-        double[] values = getNumberArray(operands, srcCellRow, srcCellCol);
-        if (values == null) {
-            retval = ErrorEval.VALUE_INVALID;
-        }
-        else {
-            double d = MathX.average(values);
-            retval = (Double.isNaN(d) || Double.isInfinite(d))
-                    ? (ValueEval) ErrorEval.NUM_ERROR
-                    : new NumberEval(d);
-        }
-        
-        return retval;
-    }
-}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Ceiling.java b/src/java/org/apache/poi/hssf/record/formula/functions/Ceiling.java
deleted file mode 100644 (file)
index a158b85..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.functions;
-
-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.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *
- */
-public class Ceiling extends NumericFunction {
-
-    public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
-        double d0 = 0;
-        double d1 = 0;
-        ValueEval retval = null;
-
-        switch (operands.length) {
-        default:
-            retval = ErrorEval.VALUE_INVALID;
-            break;
-        case 2:
-            ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
-            if (ve instanceof NumericValueEval) {
-                NumericValueEval ne = (NumericValueEval) ve;
-                d0 = ne.getNumberValue();
-            }
-            else if (ve instanceof BlankEval) {
-                // do nothing
-            }
-            else {
-                retval = ErrorEval.NUM_ERROR;
-            }
-
-            if (retval == null) {
-                ve = singleOperandEvaluate(operands[1], srcRow, srcCol);
-                if (ve instanceof NumericValueEval) {
-                    NumericValueEval ne = (NumericValueEval) ve;
-                    d1 = ne.getNumberValue();
-                }
-                else if (ve instanceof BlankEval) {
-                    // do nothing
-                }
-                else {
-                    retval = ErrorEval.NUM_ERROR;
-                }
-            }
-        }
-
-        if (retval == null) {
-            double d = MathX.ceiling(d0, d1);
-            retval = (Double.isNaN(d) || Double.isInfinite(d))
-                    ? (ValueEval) ErrorEval.NUM_ERROR
-                    : new NumberEval(d);
-        }
-        return retval;
-    }
-
-}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Combin.java b/src/java/org/apache/poi/hssf/record/formula/functions/Combin.java
deleted file mode 100644 (file)
index 72d6d46..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.functions;
-
-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.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *
- */
-public class Combin extends NumericFunction {
-
-    public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
-        double d0 = 0;
-        double d1 = 0;
-        ValueEval retval = null;
-
-        switch (operands.length) {
-        default:
-            retval = ErrorEval.VALUE_INVALID;
-            break;
-        case 2:
-            ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
-            if (ve instanceof NumericValueEval) {
-                NumericValueEval ne = (NumericValueEval) ve;
-                d0 = ne.getNumberValue();
-            }
-            else if (ve instanceof BlankEval) {
-                // do nothing
-            }
-            else {
-                retval = ErrorEval.NUM_ERROR;
-            }
-
-            if (retval == null) {
-                ve = singleOperandEvaluate(operands[1], srcRow, srcCol);
-                if (ve instanceof NumericValueEval) {
-                    NumericValueEval ne = (NumericValueEval) ve;
-                    d1 = ne.getNumberValue();
-                }
-                else if (ve instanceof BlankEval) {
-                    // do nothing
-                }
-                else {
-                    retval = ErrorEval.NUM_ERROR;
-                }
-            }
-        }
-
-        if (retval == null) {
-            if (d0 > Integer.MAX_VALUE || d1 > Integer.MAX_VALUE) {
-                retval = ErrorEval.NUM_ERROR;
-            }
-            else {
-                double d =  MathX.nChooseK((int) d0, (int) d1);
-                retval = (Double.isNaN(d) || Double.isInfinite(d))
-                        ? (ValueEval) ErrorEval.NUM_ERROR
-                        : new NumberEval(d);
-            }
-        }
-        return retval;
-    }
-
-}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Concatenate.java b/src/java/org/apache/poi/hssf/record/formula/functions/Concatenate.java
deleted file mode 100644 (file)
index be961c1..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.functions;
-
-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.StringEval;
-import org.apache.poi.hssf.record.formula.eval.StringValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *
- */
-public class Concatenate extends TextFunction {
-
-
-    public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
-        ValueEval retval = null;
-        StringBuffer sb = new StringBuffer();
-        
-        for (int i=0, iSize=operands.length; i<iSize; i++) {
-            ValueEval ve = singleOperandEvaluate(operands[i], srcCellRow, srcCellCol);
-            if (ve instanceof StringValueEval) {
-                StringValueEval sve = (StringValueEval) ve;
-                sb.append(sve.getStringValue());
-            }
-            else if (ve instanceof BlankEval) {}
-            else {
-                retval = ErrorEval.VALUE_INVALID;
-                break;
-            }
-        }
-        
-        if (retval == null) {
-            retval = new StringEval(sb.toString());
-        }
-        
-        return retval;
-    }
-}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Date.java b/src/java/org/apache/poi/hssf/record/formula/functions/Date.java
deleted file mode 100644 (file)
index 6861a02..0000000
+++ /dev/null
@@ -1,114 +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.record.formula.functions;
-
-import java.util.Calendar;
-import java.util.GregorianCalendar;
-
-import org.apache.poi.hssf.usermodel.HSSFDateUtil;
-
-import org.apache.poi.hssf.record.formula.eval.Eval;
-import org.apache.poi.hssf.record.formula.eval.RefEval;
-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.ValueEval;
-import org.apache.poi.hssf.record.formula.eval.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
-
-/**
- * @author Pavel Krupets (pkrupets at palmtreebusiness dot com)
- */
-public class Date extends NumericFunction {
-    /**
-     * @see org.apache.poi.hssf.record.formula.functions.Function#evaluate(org.apache.poi.hssf.record.formula.eval.Eval[], int, short)
-     */
-    public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
-        if (operands.length == 3) {
-            ValueEval ve[] = new ValueEval[3];
-            
-            ve[0] = singleOperandEvaluate(operands[0], srcCellRow, srcCellCol);
-            ve[1] = singleOperandEvaluate(operands[1], srcCellRow, srcCellCol);
-            ve[2] = singleOperandEvaluate(operands[2], srcCellRow, srcCellCol);
-            
-            if (validValues(ve)) {
-                int year = getYear(ve[0]);
-                int month = (int) ((NumericValueEval) ve[1]).getNumberValue() - 1;
-                int day = (int) ((NumericValueEval) ve[2]).getNumberValue();
-                
-                if (year < 0 || month < 0 || day < 0) {
-                    return ErrorEval.VALUE_INVALID;
-                }
-                
-                if (year == 1900 && month == Calendar.FEBRUARY && day == 29) {
-                    return new NumberEval(60.0);
-                }
-                
-                if (year == 1900) {
-                    if ((month == Calendar.JANUARY && day >= 60) ||
-                        (month == Calendar.FEBRUARY && day >= 30))
-                    {
-                        day--;
-                    }
-                }
-                
-                Calendar c = new GregorianCalendar();
-                
-                c.set(year, month, day, 0, 0, 0);
-                c.set(Calendar.MILLISECOND, 0);
-                
-                return new NumberEval(HSSFDateUtil.getExcelDate(c.getTime(), false)); // XXX fix 1900/1904 problem
-            }
-        }
-        
-        return ErrorEval.VALUE_INVALID;
-    }
-    
-    private int getYear(ValueEval ve) {
-        int year = (int) ((NumericValueEval) ve).getNumberValue();
-        
-        if (year < 0) {
-            return -1;
-        }
-        
-        return year < 1900 ? 1900 + year : year;
-    }
-    
-    private boolean validValues(ValueEval[] values) {
-        for (int i = 0; i < values.length; i++) {
-            ValueEval value =  values[i];
-            
-            if (value instanceof RefEval) {
-                RefEval re = (RefEval) value;
-                ValueEval ive = re.getInnerValueEval();
-                
-                if (ive instanceof BlankEval) {
-                    value = new NumberEval(0);
-                } else if (ive instanceof NumericValueEval) {
-                    value = ive;
-                } else {
-                    return false;
-                }
-            }
-            
-            if (!(value instanceof NumericValueEval)) {
-                return false;
-            }
-        }
-        
-        return true;
-    }
-}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/DateFunc.java b/src/java/org/apache/poi/hssf/record/formula/functions/DateFunc.java
new file mode 100644 (file)
index 0000000..6cbe841
--- /dev/null
@@ -0,0 +1,76 @@
+/* ====================================================================
+   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 java.util.Calendar;
+import java.util.GregorianCalendar;
+
+import org.apache.poi.hssf.record.formula.eval.ErrorEval;
+import org.apache.poi.hssf.record.formula.eval.EvaluationException;
+import org.apache.poi.hssf.usermodel.HSSFDateUtil;
+
+/**
+ * @author Pavel Krupets (pkrupets at palmtreebusiness dot com)
+ */
+public final class DateFunc extends NumericFunction.MultiArg {
+       
+       public static final Function instance = new DateFunc();
+       
+       private DateFunc() {
+               super(3,3);
+       }
+       
+       protected double evaluate(double[] ds) throws EvaluationException {
+               int year = getYear(ds[0]);
+        int month = (int) ds[1] - 1;
+        int day = (int) ds[2];
+        
+        if (year < 0 || month < 0 || day < 0) {
+            throw new EvaluationException(ErrorEval.VALUE_INVALID);
+        }
+        
+        if (year == 1900 && month == Calendar.FEBRUARY && day == 29) {
+            return 60.0;
+        }
+        
+        if (year == 1900) {
+            if ((month == Calendar.JANUARY && day >= 60) ||
+                (month == Calendar.FEBRUARY && day >= 30))
+            {
+                day--;
+            }
+        }
+        
+        Calendar c = new GregorianCalendar();
+        
+        c.set(year, month, day, 0, 0, 0);
+        c.set(Calendar.MILLISECOND, 0);
+        
+        return HSSFDateUtil.getExcelDate(c.getTime(), false); // XXX fix 1900/1904 problem
+     }
+    
+    private static int getYear(double d) {
+        int year = (int)d;
+        
+        if (year < 0) {
+            return -1;
+        }
+        
+        return year < 1900 ? 1900 + year : year;
+    }
+}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Devsq.java b/src/java/org/apache/poi/hssf/record/formula/functions/Devsq.java
deleted file mode 100644 (file)
index a198ff8..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
-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.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *
- */
-public class Devsq extends MultiOperandNumericFunction {
-
-    private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
-        new ValueEvalToNumericXlator((short) (0
-                | ValueEvalToNumericXlator.BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED  
-                | ValueEvalToNumericXlator.STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
-              //| ValueEvalToNumericXlator.EVALUATED_REF_BLANK_IS_PARSED
-                ));
-    
-    /**
-     * this is the default impl for the factory method getXlator
-     * of the super class NumericFunction. Subclasses can override this method
-     * if they desire to return a different ValueEvalToNumericXlator instance
-     * than the default.
-     */
-    protected ValueEvalToNumericXlator getXlator() {
-        return DEFAULT_NUM_XLATOR;
-    }
-
-    
-    
-    public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
-        ValueEval retval = null;
-        double[] values = getNumberArray(operands, srcCellRow, srcCellCol);
-        if (values == null) {
-            retval = ErrorEval.VALUE_INVALID;
-        }
-        else {
-            double d = StatsLib.devsq(values);
-            retval = (Double.isNaN(d) || Double.isInfinite(d))
-                    ? (ValueEval) ErrorEval.NUM_ERROR
-                    : new NumberEval(d);
-        }
-        
-        return retval;
-    }
-}
index 272863908c0a5d20b47c185d51c2a08516b6c81d..eaeed89247b23e9e57cd0db1fcd00bcfda950a85 100644 (file)
@@ -22,7 +22,7 @@ package org.apache.poi.hssf.record.formula.functions;
  * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
  *  
  */
-public final class Even extends NumericFunctionOneArg {
+public final class Even extends NumericFunction.OneArg {
 
        private static final long PARITY_MASK = 0xFFFFFFFFFFFFFFFEL;
 
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Exact.java b/src/java/org/apache/poi/hssf/record/formula/functions/Exact.java
deleted file mode 100644 (file)
index 63dc18d..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.functions;
-
-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.StringEval;
-import org.apache.poi.hssf.record.formula.eval.StringValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *
- */
-public class Exact extends TextFunction {
-
-
-    public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
-        ValueEval retval = null;
-        String s0 = null;
-        String s1 = null;
-        
-        switch (operands.length) {
-        default:
-            retval = ErrorEval.VALUE_INVALID;
-            break;
-        case 2:
-            ValueEval ve = singleOperandEvaluate(operands[0], srcCellRow, srcCellCol);
-            if (ve instanceof StringValueEval) {
-                StringValueEval sve = (StringValueEval) ve;
-                s0 = sve.getStringValue();
-            }
-            else if (ve instanceof BlankEval) {
-                s0 = StringEval.EMPTY_INSTANCE.getStringValue();
-            }
-            else {
-                retval = ErrorEval.VALUE_INVALID;
-                break;
-            }
-
-            if (retval == null) {
-                ve = singleOperandEvaluate(operands[1], srcCellRow, srcCellCol);
-                if (ve instanceof StringValueEval) {
-                    StringValueEval sve = (StringValueEval) ve;
-                    s1 = sve.getStringValue();
-                }
-                else if (ve instanceof BlankEval) {
-                    s1 = StringEval.EMPTY_INSTANCE.getStringValue();
-                }
-                else {
-                    retval = ErrorEval.VALUE_INVALID;
-                    break;
-                }
-            }
-        }
-        
-        if (retval == null) {
-            boolean b = s0.equals(s1);
-            retval = b ? BoolEval.TRUE : BoolEval.FALSE;
-        }
-        
-        return retval;
-    }
-}
index c054c6dac4dcc5b6839eab4fc980b89015dc6809..01fcf9ef0b1791c05b40bcaac0a101a92866d441 100644 (file)
@@ -1,71 +1,77 @@
-/*
-* 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
 
-package org.apache.poi.hssf.record.formula.functions;
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
 
-import org.apache.poi.hssf.record.formula.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.NumericValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
+package org.apache.poi.hssf.record.formula.functions;
 
+import org.apache.poi.hssf.record.formula.eval.EvaluationException;
 
 /**
  * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
  * Super class for all Evals for financial function evaluation.
- * 
+ *
  */
-public abstract class FinanceFunction extends NumericFunction {
-    private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
-        new ValueEvalToNumericXlator((short) (0
-                | ValueEvalToNumericXlator.BOOL_IS_PARSED  
-                | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED  
-                | ValueEvalToNumericXlator.STRING_IS_PARSED  
-                | ValueEvalToNumericXlator.REF_STRING_IS_PARSED  
-                | ValueEvalToNumericXlator.BLANK_IS_PARSED  
-                | ValueEvalToNumericXlator.REF_BLANK_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
-                ));
-    
-    /**
-     * this is the default impl of the factory(ish) method getXlator.
-     * Subclasses can override this method
-     * if they desire to return a different ValueEvalToNumericXlator instance
-     * than the default.
-     */
-    protected final ValueEvalToNumericXlator getXlator() {
-        return DEFAULT_NUM_XLATOR;
-    }
-    
-    protected final ValueEval singleOperandNumericAsBoolean(Eval eval, int srcRow, short srcCol) {
-        ValueEval retval = null;
-        retval = singleOperandEvaluate(eval, srcRow, srcCol);
-        if (retval instanceof NumericValueEval) {
-            NumericValueEval nve = (NumericValueEval) retval;
-            retval = (nve.getNumberValue() == 0)
-                    ? BoolEval.FALSE
-                    : BoolEval.TRUE;
-        }
-        else {
-            retval = ErrorEval.VALUE_INVALID;
-        }
-        return retval;
-    }
+public abstract class FinanceFunction extends NumericFunction.MultiArg {
+
+       protected FinanceFunction() {
+               super (3, 5);
+       }
+
+       protected double evaluate(double[] ds) throws EvaluationException {
+               // All finance functions have 3 to 5 args, first 4 are numbers, last is boolean
+               // default for last 2 args are 0.0 and false
+               // Text boolean literals are not valid for the last arg
+               
+               double arg3 = 0.0;
+               double arg4 = 0.0;
+
+               switch(ds.length) {
+                       case 5:
+                               arg4 = ds[4];
+                       case 4:
+                               arg3 = ds[3];
+                       case 3:
+                               break;
+                       default:
+                               throw new IllegalStateException("Wrong number of arguments");
+               }
+               return evaluate(ds[0], ds[1], ds[2], arg3, arg4!=0.0);
+       }
+
+       protected abstract double evaluate(double rate, double arg1, double arg2, double arg3, boolean type) throws EvaluationException ;
+
+
+       public static final Function FV = new FinanceFunction() {
+               protected double evaluate(double rate, double arg1, double arg2, double arg3, boolean type) {
+                       return FinanceLib.fv(rate, arg1, arg2, arg3, type);
+               }
+       };
+       public static final Function NPER = new FinanceFunction() {
+               protected double evaluate(double rate, double arg1, double arg2, double arg3, boolean type) {
+                       return FinanceLib.nper(rate, arg1, arg2, arg3, type);
+               }
+       };
+       public static final Function PMT = new FinanceFunction() {
+               protected double evaluate(double rate, double arg1, double arg2, double arg3, boolean type) {
+                       return FinanceLib.pmt(rate, arg1, arg2, arg3, type);
+               }
+       };
+       public static final Function PV = new FinanceFunction() {
+               protected double evaluate(double rate, double arg1, double arg2, double arg3, boolean type) {
+                       return FinanceLib.pv(rate, arg1, arg2, arg3, type);
+               }
+       };
 }
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Floor.java b/src/java/org/apache/poi/hssf/record/formula/functions/Floor.java
deleted file mode 100644 (file)
index 8698ce9..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.functions;
-
-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.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *
- */
-public class Floor extends NumericFunction {
-
-    public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
-        double d0 = 0;
-        double d1 = 0;
-        ValueEval retval = null;
-
-        switch (operands.length) {
-        default:
-            retval = ErrorEval.VALUE_INVALID;
-            break;
-        case 2:
-            ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
-            if (ve instanceof NumericValueEval) {
-                NumericValueEval ne = (NumericValueEval) ve;
-                d0 = ne.getNumberValue();
-            }
-            else if (ve instanceof BlankEval) {
-                // do nothing
-            }
-            else {
-                retval = ErrorEval.NUM_ERROR;
-            }
-
-            if (retval == null) {
-                ve = singleOperandEvaluate(operands[1], srcRow, srcCol);
-                if (ve instanceof NumericValueEval) {
-                    NumericValueEval ne = (NumericValueEval) ve;
-                    d1 = ne.getNumberValue();
-                }
-                else if (ve instanceof BlankEval) {
-                    // do nothing
-                }
-                else {
-                    retval = ErrorEval.NUM_ERROR;
-                }
-            }
-        }
-
-        if (retval == null) {
-            double d = MathX.floor(d0, d1);
-            retval = (Double.isNaN(d) || Double.isInfinite(d))
-                    ? (ValueEval) ErrorEval.NUM_ERROR
-                    : new NumberEval(d);
-        }
-        return retval;
-    }
-
-}
index 2163d8f5ed004105a91b2e84821e15b5e4b98187..3a3478ca528b105e4aa84568aa715be3b65340e5 100755 (executable)
@@ -45,13 +45,13 @@ public interface FreeRefFunction {
         * 
         * @param args the pre-evaluated arguments for this function. args is never <code>null</code>,
         *                nor are any of its elements.
+        * @param srcCellSheet zero based sheet index of the cell containing the currently evaluating formula
         * @param srcCellRow zero based row index of the cell containing the currently evaluating formula 
         * @param srcCellCol zero based column index of the cell containing the currently evaluating formula 
         * @param workbook is the workbook containing the formula/cell being evaluated
-        * @param sheet is the sheet containing the formula/cell being evaluated
         * @return never <code>null</code>. Possibly an instance of <tt>ErrorEval</tt> in the case of 
         * a specified Excel error (Exceptions are never thrown to represent Excel errors).
         * 
         */
-       ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, Workbook workbook, Sheet sheet);
+       ValueEval evaluate(Eval[] args, Workbook workbook, int srcCellSheet, int srcCellRow, int srcCellCol);
 }
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Fv.java b/src/java/org/apache/poi/hssf/record/formula/functions/Fv.java
deleted file mode 100644 (file)
index fd0335c..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.functions;
-
-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.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-
-public class Fv extends FinanceFunction {
-
-    public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
-        double rate = 0, nper = 0, pmt = 0, pv = 0, d = 0;
-        boolean type = false;
-        ValueEval retval = null;
-        ValueEval ve = null;
-        
-        switch (operands.length) {
-        default:
-            retval = ErrorEval.VALUE_INVALID;
-            break;
-        case 5:
-            ve = singleOperandNumericAsBoolean(operands[4], srcRow, srcCol);
-            if (ve instanceof ErrorEval) { retval = ErrorEval.VALUE_INVALID; break; }
-            type = ((BoolEval) ve).getBooleanValue();
-        case 4:
-            ve = singleOperandEvaluate(operands[3], srcRow, srcCol);
-            if (ve instanceof NumericValueEval) pv   = ((NumericValueEval) ve).getNumberValue();
-            else { retval = ErrorEval.VALUE_INVALID; break; }
-        case 3:
-            ve = singleOperandEvaluate(operands[1], srcRow, srcCol);
-            if (ve instanceof NumericValueEval) nper = ((NumericValueEval) ve).getNumberValue();
-            else { retval = ErrorEval.VALUE_INVALID; break; }
-
-            ve = singleOperandEvaluate(operands[2], srcRow, srcCol);
-            if (ve instanceof NumericValueEval) pmt  = ((NumericValueEval) ve).getNumberValue();
-            else { retval = ErrorEval.VALUE_INVALID; break; }
-
-            ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
-            if (ve instanceof NumericValueEval) rate = ((NumericValueEval) ve).getNumberValue();
-            else { retval = ErrorEval.VALUE_INVALID; break; }
-        }
-        
-        if (retval == null) {
-            d = FinanceLib.fv(rate, nper, pmt, pv, type);
-            retval = (Double.isNaN(d))
-                    ? (ValueEval) ErrorEval.VALUE_INVALID
-                    : (Double.isInfinite(d)) 
-                        ? (ValueEval) ErrorEval.NUM_ERROR 
-                        : new NumberEval(d);
-        }
-        return retval;
-    }
-
-}
index 3c93c0846bdab7b908876774d4d8fde4a2fd4d80..1c149dbdfce7a9c3c79cf8198b7f032498cb8bfe 100644 (file)
@@ -22,6 +22,7 @@ import org.apache.poi.hssf.record.formula.eval.ErrorEval;
 import org.apache.poi.hssf.record.formula.eval.Eval;
 import org.apache.poi.hssf.record.formula.eval.EvaluationException;
 import org.apache.poi.hssf.record.formula.eval.OperandResolver;
+import org.apache.poi.hssf.record.formula.eval.RefEval;
 import org.apache.poi.hssf.record.formula.eval.ValueEval;
 
 /**
@@ -51,6 +52,10 @@ public final class Index implements Function {
                        return ErrorEval.VALUE_INVALID;
                }
                Eval firstArg = args[0];
+               if (firstArg instanceof RefEval) {
+                       // convert to area ref for simpler code in getValueFromArea()
+                       firstArg = ((RefEval)firstArg).offset(0, 0, 0, 0);
+               }
                if(!(firstArg instanceof AreaEval)) {
                        
                        // else the other variation of this function takes an array as the first argument
@@ -84,16 +89,63 @@ public final class Index implements Function {
                                        // too many arguments
                                        return ErrorEval.VALUE_INVALID;
                        }
-                       return getValueFromArea(reference, rowIx, columnIx);
+                       return getValueFromArea(reference, rowIx, columnIx, nArgs);
                } catch (EvaluationException e) {
                        return e.getErrorEval();
                }
        }
        
-       private static ValueEval getValueFromArea(AreaEval ae, int rowIx, int columnIx) throws EvaluationException {
+       /**
+        * @param nArgs - needed because error codes are slightly different when only 2 args are passed 
+        */
+       private static ValueEval getValueFromArea(AreaEval ae, int pRowIx, int pColumnIx, int nArgs) throws EvaluationException {
+               int rowIx;
+               int columnIx;
+               
+               // when the area ref is a single row or a single column,
+               // there are special rules for conversion of rowIx and columnIx
+               if (ae.isRow()) {
+                       if (ae.isColumn()) {
+                               rowIx = pRowIx == -1 ? 0 : pRowIx;
+                               columnIx = pColumnIx == -1 ? 0 : pColumnIx;
+                       } else {
+                               if (nArgs == 2) {
+                                       rowIx = 0;
+                                       columnIx = pRowIx;
+                               } else {
+                                       rowIx = pRowIx == -1 ? 0 : pRowIx;
+                                       columnIx = pColumnIx;
+                               }
+                       }
+                       if (rowIx < -1 || columnIx < -1) {
+                               throw new EvaluationException(ErrorEval.VALUE_INVALID);
+                       }
+               } else if (ae.isColumn()) {
+                       if (nArgs == 2) {
+                               rowIx = pRowIx;
+                               columnIx = 0;
+                       } else {
+                               rowIx = pRowIx;
+                               columnIx = pColumnIx == -1 ? 0 : pColumnIx;
+                       }
+                       if (rowIx < -1 || columnIx < -1) {
+                               throw new EvaluationException(ErrorEval.VALUE_INVALID);
+                       }
+               } else {
+                       if (nArgs == 2) {
+                               // always an error with 2-D area refs
+                               if (pRowIx < -1) {
+                                       throw new EvaluationException(ErrorEval.VALUE_INVALID);
+                               }
+                               throw new EvaluationException(ErrorEval.REF_INVALID);
+                       }
+                       // Normal case - area ref is 2-D, and both index args were provided
+                       rowIx = pRowIx;
+                       columnIx = pColumnIx;
+               }
+               
                int width = ae.getWidth();
                int height = ae.getHeight();
-               
                // Slightly irregular logic for bounds checking errors
                if (rowIx >= height || columnIx >= width) {
                        throw new EvaluationException(ErrorEval.REF_INVALID);
index 8d7d4463e5dc97c75f5c14c0a2b7d8fe08e035af..be63d70275ec2fa6f32a2f3515797d0b124aa7cf 100644 (file)
@@ -41,7 +41,7 @@ import org.apache.poi.ss.usermodel.Workbook;
  */
 public final class Indirect implements FreeRefFunction {
 
-       public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, Workbook workbook, Sheet sheet) {
+       public ValueEval evaluate(Eval[] args, Workbook workbook, int srcCellSheet, int srcCellRow, int srcCellCol) {
                // TODO - implement INDIRECT()
                return ErrorEval.FUNCTION_NOT_IMPLEMENTED;
        }
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Large.java b/src/java/org/apache/poi/hssf/record/formula/functions/Large.java
deleted file mode 100644 (file)
index 3b9aed9..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
-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.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *
- */
-public class Large extends MultiOperandNumericFunction {
-    private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
-        new ValueEvalToNumericXlator((short) (0
-                | ValueEvalToNumericXlator.BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED  
-                | ValueEvalToNumericXlator.STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
-              //| ValueEvalToNumericXlator.EVALUATED_REF_BLANK_IS_PARSED
-              //| ValueEvalToNumericXlator.REF_BLANK_IS_PARSED
-              //| ValueEvalToNumericXlator.BLANK_IS_PARSED
-                ));
-    
-    /**
-     * this is the default impl for the factory method getXlator
-     * of the super class NumericFunction. Subclasses can override this method
-     * if they desire to return a different ValueEvalToNumericXlator instance
-     * than the default.
-     */
-    protected ValueEvalToNumericXlator getXlator() {
-        return DEFAULT_NUM_XLATOR;
-    }
-
-    
-    
-    public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
-        ValueEval retval = null;
-        double[] ops = getNumberArray(operands, srcCellRow, srcCellCol);
-        if (ops == null || ops.length < 2) {
-            retval = ErrorEval.VALUE_INVALID;
-        }
-        else {
-            double[] values = new double[ops.length-1];
-            int k = (int) ops[ops.length-1];
-            System.arraycopy(ops, 0, values, 0, values.length);
-            double d = StatsLib.kthLargest(values, k);
-            retval = (Double.isNaN(d) || Double.isInfinite(d))
-                    ? (ValueEval) ErrorEval.NUM_ERROR
-                    : new NumberEval(d);
-        }
-        
-        return retval;
-    }
-}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Left.java b/src/java/org/apache/poi/hssf/record/formula/functions/Left.java
deleted file mode 100644 (file)
index ecb14a7..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.functions;
-
-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.NumberEval;
-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;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *
- */
-public class Left extends TextFunction {
-
-    public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
-        Eval retval = ErrorEval.VALUE_INVALID;
-        int index = 1;
-        switch (operands.length) {
-        default:
-            break;
-        case 2:
-            Eval indexEval = operands[1];
-            index = evaluateAsInteger(indexEval);
-            if (index < 0) {
-                break;
-            }
-        case 1:
-            ValueEval veval = singleOperandEvaluate(operands[0], srcCellRow, srcCellCol);
-            String str = null;
-            if (veval instanceof StringEval) {
-                StringEval stringEval = (StringEval) veval;
-                str = stringEval.getStringValue();
-            }
-            else if (veval instanceof BoolEval) {
-                BoolEval beval = (BoolEval) veval;
-                str = beval.getBooleanValue() ? "TRUE" : "FALSE";
-            }
-            else if (veval instanceof NumberEval) {
-                NumberEval neval = (NumberEval) veval;
-                str = neval.getStringValue();
-            }
-            if (null != str) {
-                str = str.substring(0, Math.min(str.length(), index));
-                retval = new StringEval(str);
-            }
-        }
-        return retval;
-    }
-    
-    protected int evaluateAsInteger(Eval eval) {
-        int numval = -1;
-        if (eval instanceof NumberEval) {
-            NumberEval neval = (NumberEval) eval;
-            double d = neval.getNumberValue();
-            numval = (int) d;
-        }
-        else if (eval instanceof StringEval) {
-            StringEval seval = (StringEval) eval;
-            String s = seval.getStringValue();
-            try { 
-                double d = Double.parseDouble(s);
-                numval = (int) d;
-            } 
-            catch (Exception e) {
-            }
-        }
-        else if (eval instanceof BoolEval) {
-            BoolEval beval = (BoolEval) eval;
-            numval = beval.getBooleanValue() ? 1 : 0;
-        }
-        else if (eval instanceof RefEval) {
-            numval = evaluateAsInteger(xlateRefEval((RefEval) eval));
-        }
-        return numval;
-    }
-    
-    protected Eval xlateRefEval(RefEval reval) {
-        Eval retval = reval.getInnerValueEval();
-        
-        if (retval instanceof RefEval) {
-            retval = xlateRefEval((RefEval) retval);
-        }
-        return retval;
-    }
-}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Len.java b/src/java/org/apache/poi/hssf/record/formula/functions/Len.java
deleted file mode 100644 (file)
index 0bc49b4..0000000
+++ /dev/null
@@ -1,49 +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.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.EvaluationException;
-import org.apache.poi.hssf.record.formula.eval.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.OperandResolver;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *
- */
-public final class Len extends TextFunction {
-
-       public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
-               
-               if(args.length != 1) {
-                       return ErrorEval.VALUE_INVALID;
-               }
-               
-               try {
-                       ValueEval veval = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
-
-                       String str = OperandResolver.coerceValueToString(veval);
-                       
-                       return new NumberEval(str.length());
-               } catch (EvaluationException e) {
-                       return e.getErrorEval();
-               }
-       }
-}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Log.java b/src/java/org/apache/poi/hssf/record/formula/functions/Log.java
deleted file mode 100644 (file)
index 7727c35..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 6, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.functions;
-
-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.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- * Log: LOG(number,[base])
- */
-public class Log extends NumericFunction {
-
-    private static final double DEFAULT_BASE = 10;
-
-    public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
-        double d = 0;
-        double base = DEFAULT_BASE;
-        double num = 0;
-        ValueEval retval = null;
-
-        switch (operands.length) {
-        default:
-            retval = ErrorEval.VALUE_INVALID;
-            break;
-        case 2: // second arg is base
-            ValueEval ve = singleOperandEvaluate(operands[1], srcRow, srcCol);
-            if (ve instanceof NumericValueEval) {
-                NumericValueEval ne = (NumericValueEval) ve;
-                base = ne.getNumberValue();
-            }
-            else if (ve instanceof BlankEval) {
-                // do nothing
-            }
-            else {
-                retval = ErrorEval.NUM_ERROR;
-            }
-
-        case 1: // first arg is number
-            if (retval == null) {
-                ValueEval vev = singleOperandEvaluate(operands[0], srcRow, srcCol);
-                if (vev instanceof NumericValueEval) {
-                    NumericValueEval ne = (NumericValueEval) vev;
-                    num = ne.getNumberValue();
-                }
-                else if (vev instanceof BlankEval) {
-                    // do nothing
-                }
-                else {
-                    retval = ErrorEval.NUM_ERROR;
-                }
-            }
-        }
-
-        if (retval == null) {
-            d = (base == E)
-                ? Math.log(num)
-                : Math.log(num) / Math.log(base);
-            retval = (Double.isNaN(d) || Double.isInfinite(d)) ? (ValueEval) ErrorEval.VALUE_INVALID : new NumberEval(d);
-        }
-        return retval;
-    }
-
-}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Lower.java b/src/java/org/apache/poi/hssf/record/formula/functions/Lower.java
deleted file mode 100644 (file)
index edf7e27..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.functions;
-
-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.StringEval;
-import org.apache.poi.hssf.record.formula.eval.StringValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *
- */
-public class Lower extends TextFunction {
-
-
-    public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
-        ValueEval retval = null;
-        String s = null;
-        
-        switch (operands.length) {
-        default:
-            retval = ErrorEval.VALUE_INVALID;
-            break;
-        case 1:
-            ValueEval ve = singleOperandEvaluate(operands[0], srcCellRow, srcCellCol);
-            if (ve instanceof StringValueEval) {
-                StringValueEval sve = (StringValueEval) ve;
-                s = sve.getStringValue();
-            }
-            else if (ve instanceof BlankEval) {}
-            else {
-                retval = ErrorEval.VALUE_INVALID;
-                break;
-            }
-        }
-        
-        if (retval == null) {
-            s = (s == null) ? EMPTY_STRING : s;
-            retval = new StringEval(s.toLowerCase());
-        }
-        
-        return retval;
-    }
-}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Max.java b/src/java/org/apache/poi/hssf/record/formula/functions/Max.java
deleted file mode 100644 (file)
index d5b3971..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
-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.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt; 
- *
- */
-public class Max extends MultiOperandNumericFunction {
-    private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
-        new ValueEvalToNumericXlator((short) (
-                  ValueEvalToNumericXlator.BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED  
-                | ValueEvalToNumericXlator.STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE  
-                ));
-    
-    protected ValueEvalToNumericXlator getXlator() {
-        return DEFAULT_NUM_XLATOR;
-    }
-
-    
-    public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
-        ValueEval retval = null;
-        double[] values = getNumberArray(operands, srcCellRow, srcCellCol);
-        if (values == null) {
-            retval = ErrorEval.VALUE_INVALID;
-        }
-        else {
-            double d = values.length > 0 ? MathX.max(values) : 0;
-            retval = (Double.isNaN(d) || Double.isInfinite(d))
-                    ? (ValueEval) ErrorEval.NUM_ERROR
-                    : new NumberEval(d);
-        }
-        
-        return retval;
-    }
-}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Maxa.java b/src/java/org/apache/poi/hssf/record/formula/functions/Maxa.java
deleted file mode 100644 (file)
index e25db7b..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-
-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.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt; 
- *
- */
-public final class Maxa extends MultiOperandNumericFunction {
-    private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
-        new ValueEvalToNumericXlator((short) (
-                  ValueEvalToNumericXlator.BOOL_IS_PARSED  
-                | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED  
-                | ValueEvalToNumericXlator.STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED  
-                | ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE  
-                | ValueEvalToNumericXlator.BLANK_IS_PARSED
-                | ValueEvalToNumericXlator.REF_BLANK_IS_PARSED
-                ));
-    
-    protected ValueEvalToNumericXlator getXlator() {
-        return DEFAULT_NUM_XLATOR;
-    }
-
-    
-    public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
-        ValueEval retval = null;
-        double[] values = getNumberArray(operands, srcCellRow, srcCellCol);
-        if (values == null) {
-            retval = ErrorEval.VALUE_INVALID;
-        }
-        else {
-            double d = values.length > 0 ? MathX.max(values) : 0;
-            retval = (Double.isNaN(d) || Double.isInfinite(d))
-                    ? (ValueEval) ErrorEval.NUM_ERROR
-                    : new NumberEval(d);
-        }
-        
-        return retval;
-    }
-}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Median.java b/src/java/org/apache/poi/hssf/record/formula/functions/Median.java
deleted file mode 100644 (file)
index 57a18b3..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
-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.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *
- */
-public class Median extends MultiOperandNumericFunction {
-    private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
-        new ValueEvalToNumericXlator((short) (0
-                | ValueEvalToNumericXlator.BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED  
-                | ValueEvalToNumericXlator.STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
-              //| ValueEvalToNumericXlator.EVALUATED_REF_BLANK_IS_PARSED
-              //| ValueEvalToNumericXlator.REF_BLANK_IS_PARSED
-              //| ValueEvalToNumericXlator.BLANK_IS_PARSED
-                ));
-    
-    /**
-     * this is the default impl for the factory method getXlator
-     * of the super class NumericFunction. Subclasses can override this method
-     * if they desire to return a different ValueEvalToNumericXlator instance
-     * than the default.
-     */
-    protected ValueEvalToNumericXlator getXlator() {
-        return DEFAULT_NUM_XLATOR;
-    }
-
-    
-    public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
-        ValueEval retval = null;
-        double[] values = getNumberArray(operands, srcCellRow, srcCellCol);
-        if (values == null) {
-            retval = ErrorEval.VALUE_INVALID;
-        }
-        else {
-            double d = StatsLib.median(values);
-            retval = (Double.isNaN(d) || Double.isInfinite(d))
-                    ? (ValueEval) ErrorEval.NUM_ERROR
-                    : new NumberEval(d);
-        }
-        
-        return retval;
-    }
-}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Mid.java b/src/java/org/apache/poi/hssf/record/formula/functions/Mid.java
deleted file mode 100644 (file)
index b9d679d..0000000
+++ /dev/null
@@ -1,87 +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.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.EvaluationException;
-import org.apache.poi.hssf.record.formula.eval.OperandResolver;
-import org.apache.poi.hssf.record.formula.eval.StringEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-
-/**
- * An implementation of the MID function<br/> MID returns a specific number of
- * characters from a text string, starting at the specified position.<p/>
- * 
- * <b>Syntax<b>:<br/> <b>MID</b>(<b>text</b>, <b>start_num</b>,
- * <b>num_chars</b>)<br/>
- * 
- * @author Manda Wilson &lt; wilson at c bio dot msk cc dot org &gt;
- */
-public class Mid implements Function {
-       /**
-        * Returns a specific number of characters from a text string, starting at
-        * the position you specify, based on the number of characters you specify.
-        * 
-        * @see org.apache.poi.hssf.record.formula.eval.Eval
-        */
-       public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
-               if (args.length != 3) {
-                       return ErrorEval.VALUE_INVALID;
-               }
-
-               String text;
-               int startIx; // zero based
-               int numChars;
-
-               try {
-                       ValueEval evText = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
-                       text = OperandResolver.coerceValueToString(evText);
-                       int startCharNum = evaluateNumberArg(args[1], srcCellRow, srcCellCol);
-                       numChars = evaluateNumberArg(args[2], srcCellRow, srcCellCol);
-                       startIx = startCharNum - 1; // convert to zero-based
-               } catch (EvaluationException e) {
-                       return e.getErrorEval();
-               }
-
-               int len = text.length();
-               if (startIx < 0) {
-                       return ErrorEval.VALUE_INVALID;
-               }
-               if (numChars < 0) {
-                       return ErrorEval.VALUE_INVALID;
-               }
-               if (numChars < 0 || startIx > len) {
-                       return new StringEval("");
-               }
-               int endIx = startIx + numChars;
-               if (endIx > len) {
-                       endIx = len;
-               }
-               String result = text.substring(startIx, endIx);
-               return new StringEval(result);
-
-       }
-
-       private static int evaluateNumberArg(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException {
-               ValueEval ev = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
-               // Note - for start_num arg, blank/zero causes error(#VALUE!),
-               // but for num_chars causes empty string to be returned.
-               return OperandResolver.coerceValueToInt(ev);
-       }
-}
\ No newline at end of file
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Min.java b/src/java/org/apache/poi/hssf/record/formula/functions/Min.java
deleted file mode 100644 (file)
index 8cd8967..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
-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.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt; 
- *
- */
-public class Min extends MultiOperandNumericFunction {
-    private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
-        new ValueEvalToNumericXlator((short) (
-                  ValueEvalToNumericXlator.BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED  
-                | ValueEvalToNumericXlator.STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE  
-                ));
-    
-    protected ValueEvalToNumericXlator getXlator() {
-        return DEFAULT_NUM_XLATOR;
-    }
-
-    
-    public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
-        ValueEval retval = null;
-        double[] values = getNumberArray(operands, srcCellRow, srcCellCol);
-        if (values == null) {
-            retval = ErrorEval.VALUE_INVALID;
-        }
-        else {
-            double d = values.length > 0 ? MathX.min(values) : 0;
-            retval = (Double.isNaN(d) || Double.isInfinite(d))
-                    ? (ValueEval) ErrorEval.NUM_ERROR
-                    : new NumberEval(d);
-        }
-        
-        return retval;
-    }
-}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Mina.java b/src/java/org/apache/poi/hssf/record/formula/functions/Mina.java
deleted file mode 100644 (file)
index 21ba47b..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-
-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.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt; 
- *
- */
-public class Mina extends MultiOperandNumericFunction {
-    private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
-        new ValueEvalToNumericXlator((short) (
-                  ValueEvalToNumericXlator.BOOL_IS_PARSED  
-                | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED  
-                | ValueEvalToNumericXlator.STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED  
-                | ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
-                | ValueEvalToNumericXlator.REF_BLANK_IS_PARSED
-                | ValueEvalToNumericXlator.BLANK_IS_PARSED
-                ));
-    
-    protected ValueEvalToNumericXlator getXlator() {
-        return DEFAULT_NUM_XLATOR;
-    }
-
-    
-    public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
-        ValueEval retval = null;
-        double[] values = getNumberArray(operands, srcCellRow, srcCellCol);
-        if (values == null) {
-            retval = ErrorEval.VALUE_INVALID;
-        }
-        else {
-            double d = values.length > 0 ? MathX.min(values) : 0;
-            retval = (Double.isNaN(d) || Double.isInfinite(d))
-                    ? (ValueEval) ErrorEval.NUM_ERROR
-                    : new NumberEval(d);
-        }
-        
-        return retval;
-    }
-}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/MinaMaxa.java b/src/java/org/apache/poi/hssf/record/formula/functions/MinaMaxa.java
new file mode 100644 (file)
index 0000000..70e5e7f
--- /dev/null
@@ -0,0 +1,40 @@
+/* ====================================================================
+   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;
+
+/**
+ * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
+ * 
+ */
+public abstract class MinaMaxa extends MultiOperandNumericFunction {
+
+       protected MinaMaxa() {
+               super(true, true);
+       }
+
+       public static final Function MAXA = new MinaMaxa() {
+               protected double evaluate(double[] values) {
+                       return values.length > 0 ? MathX.max(values) : 0;
+               }
+       };
+       public static final Function MINA = new MinaMaxa() {
+               protected double evaluate(double[] values) {
+                       return values.length > 0 ? MathX.min(values) : 0;
+               }
+       };
+}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Mod.java b/src/java/org/apache/poi/hssf/record/formula/functions/Mod.java
deleted file mode 100644 (file)
index ebef231..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.functions;
-
-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.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *
- */
-public class Mod extends NumericFunction {
-
-    public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
-        double d0 = 0;
-        double d1 = 0;
-        ValueEval retval = null;
-
-        switch (operands.length) {
-        default:
-            retval = ErrorEval.VALUE_INVALID;
-            break;
-        case 2:
-            ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
-            if (ve instanceof NumericValueEval) {
-                NumericValueEval ne = (NumericValueEval) ve;
-                d0 = ne.getNumberValue();
-            }
-            else if (ve instanceof BlankEval) {
-                // do nothing
-            }
-            else {
-                retval = ErrorEval.NUM_ERROR;
-            }
-
-            if (retval == null) {
-                ve = singleOperandEvaluate(operands[1], srcRow, srcCol);
-                if (ve instanceof NumericValueEval) {
-                    NumericValueEval ne = (NumericValueEval) ve;
-                    d1 = ne.getNumberValue();
-                }
-                else if (ve instanceof BlankEval) {
-                    // do nothing
-                }
-                else {
-                    retval = ErrorEval.NUM_ERROR;
-                }
-            }
-        }
-
-        if (retval == null) {
-            if (d1 == 0) {
-                retval = ErrorEval.DIV_ZERO;
-            }
-            else {
-                double d = MathX.mod(d0, d1);
-                retval = (Double.isNaN(d) || Double.isInfinite(d))
-                        ? (ValueEval) ErrorEval.NUM_ERROR
-                        : new NumberEval(d);
-            }
-        }
-        return retval;
-    }
-
-}
index 724b6e654339ae9621c09452583105480603bce8..c236ccee62cc72ab4fd2cf904ecbc9d71d2281c0 100644 (file)
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
 package org.apache.poi.hssf.record.formula.functions;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+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.EvaluationException;
 import org.apache.poi.hssf.record.formula.eval.NumberEval;
+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.record.formula.eval.ValueEvalToNumericXlator;
 
 /**
  * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *
+ * 
  */
-public class Mode extends MultiOperandNumericFunction {
-    private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
-        new ValueEvalToNumericXlator((short) (0
-              //| ValueEvalToNumericXlator.BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED  
-                | ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
-              //| ValueEvalToNumericXlator.EVALUATED_REF_BLANK_IS_PARSED
-              //| ValueEvalToNumericXlator.REF_BLANK_IS_PARSED
-              //| ValueEvalToNumericXlator.BLANK_IS_PARSED
-                ));
-    
-    /**
-     * this is the default impl for the factory method getXlator
-     * of the super class NumericFunction. Subclasses can override this method
-     * if they desire to return a different ValueEvalToNumericXlator instance
-     * than the default.
-     */
-    protected ValueEvalToNumericXlator getXlator() {
-        return DEFAULT_NUM_XLATOR;
-    }
+public class Mode implements Function {
+
+       /**
+        * if v is zero length or contains no duplicates, return value is
+        * Double.NaN. Else returns the value that occurs most times and if there is
+        * a tie, returns the first such value.
+        * 
+        * @param v
+        */
+       public static double evaluate(double[] v) throws EvaluationException {
+               if (v.length < 2) {
+                       throw new EvaluationException(ErrorEval.NA);
+               }
+
+               // very naive impl, may need to be optimized
+               int[] counts = new int[v.length];
+               Arrays.fill(counts, 1);
+               for (int i = 0, iSize = v.length; i < iSize; i++) {
+                       for (int j = i + 1, jSize = v.length; j < jSize; j++) {
+                               if (v[i] == v[j])
+                                       counts[i]++;
+                       }
+               }
+               double maxv = 0;
+               int maxc = 0;
+               for (int i = 0, iSize = counts.length; i < iSize; i++) {
+                       if (counts[i] > maxc) {
+                               maxv = v[i];
+                               maxc = counts[i];
+                       }
+               }
+               if (maxc > 1) {
+                       return maxv;
+               }
+               throw new EvaluationException(ErrorEval.NA);
+
+       }
+
+       public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
+               double result;
+               try {
+                       List temp = new ArrayList();
+                       for (int i = 0; i < args.length; i++) {
+                               collectValues(args[i], temp);
+                       }
+                       double[] values = new double[temp.size()];
+                       for (int i = 0; i < values.length; i++) {
+                               values[i] = ((Double) temp.get(i)).doubleValue();
+                       }
+                       result = evaluate(values);
+               } catch (EvaluationException e) {
+                       return e.getErrorEval();
+               }
+               return new NumberEval(result);
+       }
+
+       private static void collectValues(Eval arg, List temp) throws EvaluationException {
+               if (arg instanceof AreaEval) {
+                       AreaEval ae = (AreaEval) arg;
+                       int width = ae.getWidth();
+                       int height = ae.getHeight();
+                       for (int rrIx = 0; rrIx < height; rrIx++) {
+                               for (int rcIx = 0; rcIx < width; rcIx++) {
+                                       ValueEval ve1 = ae.getRelativeValue(rrIx, rcIx);
+                                       collectValue(ve1, temp, false);
+                               }
+                       }
+                       return;
+               }
+               if (arg instanceof RefEval) {
+                       RefEval re = (RefEval) arg;
+                       collectValue(re.getInnerValueEval(), temp, true);
+                       return;
+               }
+               collectValue(arg, temp, true);
 
+       }
 
-    
-    public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
-        ValueEval retval = null;
-        double[] values = getNumberArray(operands, srcCellRow, srcCellCol);
-        if (values == null) {
-            retval = ErrorEval.VALUE_INVALID;
-        }
-        else {
-            double d = StatsLib.mode(values);
-            retval = (Double.isNaN(d) || Double.isInfinite(d))
-                    ? (ValueEval) ErrorEval.NUM_ERROR
-                    : new NumberEval(d);
-        }
-        
-        return retval;
-    }
+       private static void collectValue(Eval arg, List temp, boolean mustBeNumber)
+                       throws EvaluationException {
+               if (arg instanceof ErrorEval) {
+                       throw new EvaluationException((ErrorEval) arg);
+               }
+               if (arg == BlankEval.INSTANCE || arg instanceof BoolEval || arg instanceof StringEval) {
+                       if (mustBeNumber) {
+                               throw EvaluationException.invalidValue();
+                       }
+                       return;
+               }
+               if (arg instanceof NumberEval) {
+                       temp.add(new Double(((NumberEval) arg).getNumberValue()));
+                       return;
+               }
+               throw new RuntimeException("Unexpected value type (" + arg.getClass().getName() + ")");
+       }
 }
index a9e98ae313087f229d2b05da82d58b3b64ff84b8..b3feac6a07a27437f321e6ed08207ac48f55cd7d 100644 (file)
@@ -1,30 +1,33 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
 
 package org.apache.poi.hssf.record.formula.functions;
 
 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.NumericValueEval;
-import org.apache.poi.hssf.record.formula.eval.Ref2DEval;
+import org.apache.poi.hssf.record.formula.eval.EvaluationException;
+import org.apache.poi.hssf.record.formula.eval.NumberEval;
+import org.apache.poi.hssf.record.formula.eval.OperandResolver;
 import org.apache.poi.hssf.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.record.formula.eval.ValueEvalToNumericXlator;
 
 /**
  * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
@@ -32,175 +35,166 @@ import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
  * classes that take variable number of operands, and
  * where the order of operands does not matter
  */
-public abstract class MultiOperandNumericFunction extends NumericFunction {
-    static final double[] EMPTY_DOUBLE_ARRAY = { };
-    
-    private static class DoubleList {
-        private double[] _array;
-        private int _count;
-
-        public DoubleList() {
-            _array = new double[8];
-            _count = 0;
-        }
-        
-        public double[] toArray() {
-            if(_count < 1) {
-                return EMPTY_DOUBLE_ARRAY;
-            }
-            double[] result = new double[_count];
-            System.arraycopy(_array, 0, result, 0, _count);
-            return result;
-        }
-
-        public void add(double[] values) {
-            int addLen = values.length;
-            ensureCapacity(_count + addLen);
-            System.arraycopy(values, 0, _array, _count, addLen);
-            _count += addLen;
-        }
-
-        private void ensureCapacity(int reqSize) {
-            if(reqSize > _array.length) {
-                int newSize = reqSize * 3 / 2; // grow with 50% extra
-                double[] newArr = new double[newSize];
-                System.arraycopy(_array, 0, newArr, 0, _count);
-                _array = newArr;
-            }
-        }
-
-        public void add(double value) {
-            ensureCapacity(_count + 1);
-            _array[_count] = value;
-            _count++;
-        }
-    }
-    
-    private static final int DEFAULT_MAX_NUM_OPERANDS = 30;
-
-    protected abstract ValueEvalToNumericXlator getXlator();
-    
-    /**
-     * Maximum number of operands accepted by this function.
-     * Subclasses may override to change default value.
-     */
-    protected int getMaxNumOperands() {
-        return DEFAULT_MAX_NUM_OPERANDS;
-    }
-    
-    /**
-     * Returns a double array that contains values for the numeric cells
-     * from among the list of operands. Blanks and Blank equivalent cells
-     * are ignored. Error operands or cells containing operands of type
-     * that are considered invalid and would result in #VALUE! error in 
-     * excel cause this function to return <code>null</code>.
-     * 
-     * @param operands
-     * @param srcRow
-     * @param srcCol
-     */
-    protected double[] getNumberArray(Eval[] operands, int srcRow, short srcCol) {
-        if (operands.length > getMaxNumOperands()) {
-            return null;
-        }
-        DoubleList retval = new DoubleList();
-        
-        for (int i=0, iSize=operands.length; i<iSize; i++) {
-            double[] temp = getNumberArray(operands[i], srcRow, srcCol);
-            if (temp == null) {
-                return null; // error occurred.
-            }
-            retval.add(temp);
-        }
-        return retval.toArray();
-    }
-    
-    /**
-     * Same as getNumberArray(Eval[], int, short) except that this
-     * takes Eval instead of Eval[].
-     * @param operand
-     * @param srcRow
-     * @param srcCol
-     */
-    protected double[] getNumberArray(Eval operand, int srcRow, short srcCol) {
-        
-        if (operand instanceof AreaEval) {
-            AreaEval ae = (AreaEval) operand;
-            DoubleList retval = new DoubleList();
-            int width = ae.getWidth();
-               int height = ae.getHeight();
-               for (int rrIx=0; rrIx<height; rrIx++) {
-                       for (int rcIx=0; rcIx<width; rcIx++) {
-                               ValueEval ve1 = ae.getRelativeValue(rrIx, rcIx);
-                     /*
-                     * TODO: For an AreaEval, we are constructing a RefEval
-                     * per element.
-                     * For now this is a tempfix solution since this may
-                     * require a more generic fix at the level of
-                     * HSSFFormulaEvaluator where we store an array
-                     * of RefEvals as the "values" array. 
-                     */
-                    RefEval re = new Ref2DEval(null, ve1);
-                    ValueEval ve = singleOperandEvaluate(re, srcRow, srcCol);
-                    
-                    if (ve instanceof NumericValueEval) {
-                        NumericValueEval nve = (NumericValueEval) ve;
-                        retval.add(nve.getNumberValue());
-                    }
-                    else if (ve instanceof BlankEval) {
-                        // note - blanks are ignored, so returned array will be smaller.
-                    } 
-                    else {
-                        return null; // indicate to calling subclass that error occurred
-                    }
-                       }
-            }
-            return retval.toArray();
-        }
-        
-        // for ValueEvals other than AreaEval
-        ValueEval ve = singleOperandEvaluate(operand, srcRow, srcCol);
-        
-        if (ve instanceof NumericValueEval) {
-            NumericValueEval nve = (NumericValueEval) ve;
-            return new double[] { nve.getNumberValue(), };
-        }
-        
-        if (ve instanceof BlankEval) {
-            // ignore blanks
-            return EMPTY_DOUBLE_ARRAY;
-        } 
-        return null;
-    }
-    
-    /**
-     * Ensures that a two dimensional array has all sub-arrays present and the same length
-     * @return <code>false</code> if any sub-array is missing, or is of different length
-     */
-    protected static final boolean areSubArraysConsistent(double[][] values) {
-        
-        if (values == null || values.length < 1) {
-            // TODO this doesn't seem right.  Fix or add comment.
-            return true;
-        }
-        
-        if (values[0] == null) {
-            return false;
-        }
-        int outerMax = values.length;
-        int innerMax = values[0].length;
-        for (int i=1; i<outerMax; i++) { // note - 'i=1' start at second sub-array
-            double[] subArr = values[i];
-            if (subArr == null) {
-                return false;
-            }
-            if (innerMax != subArr.length) {
-                return false;
-            }
-        }
-        return true;
-    }
-    
-   
-    
+public abstract class MultiOperandNumericFunction implements Function {
+
+       private final boolean _isReferenceBoolCounted;
+       private final boolean _isBlankCounted;
+
+       protected MultiOperandNumericFunction(boolean isReferenceBoolCounted, boolean isBlankCounted) {
+               _isReferenceBoolCounted = isReferenceBoolCounted;
+               _isBlankCounted = isBlankCounted;
+       }
+
+
+       static final double[] EMPTY_DOUBLE_ARRAY = { };
+
+       private static class DoubleList {
+               private double[] _array;
+               private int _count;
+
+               public DoubleList() {
+                       _array = new double[8];
+                       _count = 0;
+               }
+
+               public double[] toArray() {
+                       if(_count < 1) {
+                               return EMPTY_DOUBLE_ARRAY;
+                       }
+                       double[] result = new double[_count];
+                       System.arraycopy(_array, 0, result, 0, _count);
+                       return result;
+               }
+
+               private void ensureCapacity(int reqSize) {
+                       if(reqSize > _array.length) {
+                               int newSize = reqSize * 3 / 2; // grow with 50% extra
+                               double[] newArr = new double[newSize];
+                               System.arraycopy(_array, 0, newArr, 0, _count);
+                               _array = newArr;
+                       }
+               }
+
+               public void add(double value) {
+                       ensureCapacity(_count + 1);
+                       _array[_count] = value;
+                       _count++;
+               }
+       }
+
+       private static final int DEFAULT_MAX_NUM_OPERANDS = 30;
+
+       public final Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
+
+               double d;
+               try {
+                       double[] values = getNumberArray(args);
+                       d = evaluate(values);
+               } catch (EvaluationException e) {
+                       return e.getErrorEval();
+               }
+
+               if (Double.isNaN(d) || Double.isInfinite(d))
+                       return ErrorEval.NUM_ERROR;
+
+               return new NumberEval(d);
+       }
+
+       protected abstract double evaluate(double[] values) throws EvaluationException;
+
+
+       /**
+        * Maximum number of operands accepted by this function.
+        * Subclasses may override to change default value.
+        */
+       protected int getMaxNumOperands() {
+               return DEFAULT_MAX_NUM_OPERANDS;
+       }
+
+       /**
+        * Returns a double array that contains values for the numeric cells
+        * from among the list of operands. Blanks and Blank equivalent cells
+        * are ignored. Error operands or cells containing operands of type
+        * that are considered invalid and would result in #VALUE! error in
+        * excel cause this function to return <code>null</code>.
+        *
+        * @return never <code>null</code>
+        */
+       protected final double[] getNumberArray(Eval[] operands) throws EvaluationException {
+               if (operands.length > getMaxNumOperands()) {
+                       throw EvaluationException.invalidValue();
+               }
+               DoubleList retval = new DoubleList();
+
+               for (int i=0, iSize=operands.length; i<iSize; i++) {
+                       collectValues(operands[i], retval);
+               }
+               return retval.toArray();
+       }
+
+       /**
+        * Collects values from a single argument
+        */
+       private void collectValues(Eval operand, DoubleList temp) throws EvaluationException {
+
+               if (operand instanceof AreaEval) {
+                       AreaEval ae = (AreaEval) operand;
+                       int width = ae.getWidth();
+                       int height = ae.getHeight();
+                       for (int rrIx=0; rrIx<height; rrIx++) {
+                               for (int rcIx=0; rcIx<width; rcIx++) {
+                                       ValueEval ve = ae.getRelativeValue(rrIx, rcIx);
+                                       collectValue(ve, true, temp);
+                               }
+                       }
+                       return;
+               }
+               if (operand instanceof RefEval) {
+                       RefEval re = (RefEval) operand;
+                       collectValue(re.getInnerValueEval(), true, temp);
+                       return;
+               }
+               collectValue((ValueEval)operand, false, temp);
+       }
+       private void collectValue(ValueEval ve, boolean isViaReference, DoubleList temp)  throws EvaluationException {
+               if (ve == null) {
+                       throw new IllegalArgumentException("ve must not be null");
+               }
+               if (ve instanceof NumberEval) {
+                       NumberEval ne = (NumberEval) ve;
+                       temp.add(ne.getNumberValue());
+                       return;
+               }
+               if (ve instanceof ErrorEval) {
+                       throw new EvaluationException((ErrorEval) ve);
+               }
+               if (ve instanceof StringEval) {
+                       if (isViaReference) {
+                               // ignore all ref strings
+                               return;
+                       }
+                       String s = ((StringEval) ve).getStringValue();
+                       Double d = OperandResolver.parseDouble(s);
+                       if(d == null) {
+                               throw new EvaluationException(ErrorEval.VALUE_INVALID);
+                       }
+                       temp.add(d.doubleValue());
+                       return;
+               }
+               if (ve instanceof BoolEval) {
+                       if (!isViaReference || _isReferenceBoolCounted) {
+                               BoolEval boolEval = (BoolEval) ve;
+                               temp.add(boolEval.getNumberValue());
+                       }
+                       return;
+               }
+               if (ve == BlankEval.INSTANCE) {
+                       if (_isBlankCounted) {
+                               temp.add(0.0);
+                       }
+                       return;
+               }
+               throw new RuntimeException("Invalid ValueEval type passed for conversion: ("
+                               + ve.getClass() + ")");
+       }
 }
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Nper.java b/src/java/org/apache/poi/hssf/record/formula/functions/Nper.java
deleted file mode 100644 (file)
index 95411ed..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.functions;
-
-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.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-
-public class Nper extends FinanceFunction {
-
-    public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
-        double rate = 0, fv = 0, pmt = 0, pv = 0, d = 0;
-        boolean type = false;
-        ValueEval retval = null;
-        ValueEval ve = null;
-        
-        switch (operands.length) {
-        default:
-            retval = ErrorEval.VALUE_INVALID;
-            break;
-        case 5:
-            ve = singleOperandNumericAsBoolean(operands[4], srcRow, srcCol);
-            if (ve instanceof ErrorEval) { retval = ErrorEval.VALUE_INVALID; break; }
-            type = ((BoolEval) ve).getBooleanValue();
-        case 4:
-            ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
-            if (ve instanceof NumericValueEval) rate = ((NumericValueEval) ve).getNumberValue();
-            else { retval = ErrorEval.VALUE_INVALID; break; }
-            
-            ve = singleOperandEvaluate(operands[1], srcRow, srcCol);
-            if (ve instanceof NumericValueEval) pmt  = ((NumericValueEval) ve).getNumberValue();
-            else { retval = ErrorEval.VALUE_INVALID; break; }
-            
-            ve = singleOperandEvaluate(operands[2], srcRow, srcCol);
-            if (ve instanceof NumericValueEval) pv   = ((NumericValueEval) ve).getNumberValue();
-            else { retval = ErrorEval.VALUE_INVALID; break; }
-            
-            ve = singleOperandEvaluate(operands[3], srcRow, srcCol);
-            if (ve instanceof NumericValueEval) fv = ((NumericValueEval) ve).getNumberValue();
-            else { retval = ErrorEval.VALUE_INVALID; break; }
-        }
-        
-        if (retval == null) {
-            d = FinanceLib.nper(rate, pmt, pv, fv, type);
-            retval = (Double.isNaN(d))
-                    ? (ValueEval) ErrorEval.VALUE_INVALID
-                    : (Double.isInfinite(d)) 
-                        ? (ValueEval) ErrorEval.NUM_ERROR 
-                        : new NumberEval(d);
-        }
-        return retval;
-    }
-
-}
index fd96f1495a9c47978102c43caf1ad250eca49e77..9a702ee6a3467616b7c67d8b92f0e24918627762 100644 (file)
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 14, 2005
- *
- */
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
 package org.apache.poi.hssf.record.formula.functions;
 
-import org.apache.poi.hssf.record.formula.eval.AreaEval;
 import org.apache.poi.hssf.record.formula.eval.ErrorEval;
 import org.apache.poi.hssf.record.formula.eval.Eval;
+import org.apache.poi.hssf.record.formula.eval.EvaluationException;
+import org.apache.poi.hssf.record.formula.eval.NumberEval;
+import org.apache.poi.hssf.record.formula.eval.OperandResolver;
 import org.apache.poi.hssf.record.formula.eval.ValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
 
 /**
  * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *
  */
 public abstract class NumericFunction implements Function {
-    
-    protected static final double E = Math.E;
-    protected static final double PI = Math.PI;
-    
-    private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
-        new ValueEvalToNumericXlator((short) (
-                  ValueEvalToNumericXlator.BOOL_IS_PARSED  
-                | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED  
-                | ValueEvalToNumericXlator.STRING_IS_PARSED  
-                | ValueEvalToNumericXlator.REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE  
-                ));
-    
-    private static final int DEFAULT_MAX_NUM_OPERANDS = 30;
-
-    /**
-     * this is the default impl of the factory(ish) method getXlator.
-     * Subclasses can override this method
-     * if they desire to return a different ValueEvalToNumericXlator instance
-     * than the default.
-     */
-    protected ValueEvalToNumericXlator getXlator() {
-        return DEFAULT_NUM_XLATOR;
-    }
-    
-    protected ValueEval singleOperandEvaluate(Eval eval, int srcRow, short srcCol) {
-        ValueEval retval;
-        if (eval instanceof AreaEval) {
-            AreaEval ae = (AreaEval) eval;
-            if (ae.contains(srcRow, srcCol)) { // circular ref!
-                retval = ErrorEval.CIRCULAR_REF_ERROR;
-            }
-            else if (ae.isRow()) {
-                if (ae.containsColumn(srcCol)) {
-                    ValueEval ve = ae.getValueAt(ae.getFirstRow(), srcCol);
-                    ve = getXlator().attemptXlateToNumeric(ve);
-                    retval = getXlator().attemptXlateToNumeric(ve);
-                }
-                else {
-                    retval = ErrorEval.VALUE_INVALID;
-                }
-            }
-            else if (ae.isColumn()) {
-                if (ae.containsRow(srcRow)) {
-                    ValueEval ve = ae.getValueAt(srcRow, ae.getFirstColumn());
-                    retval = getXlator().attemptXlateToNumeric(ve);
-                }
-                else {
-                    retval = ErrorEval.VALUE_INVALID;
-                }
-            }
-            else {
-                retval = ErrorEval.VALUE_INVALID;
-            }
-        }
-        else {
-            retval = getXlator().attemptXlateToNumeric((ValueEval) eval);
-        }
-        return retval;
-    }
 
+       static final double ZERO = 0.0;
+       static final double TEN = 10.0;
+       static final double LOG_10_TO_BASE_e = Math.log(TEN);
+
+       protected static final double singleOperandEvaluate(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException {
+               ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
+               double result = OperandResolver.coerceValueToDouble(ve);
+               checkValue(result);
+               return result;
+       }
+
+       private static final void checkValue(double result) throws EvaluationException {
+               if (Double.isNaN(result) || Double.isInfinite(result)) {
+                       throw new EvaluationException(ErrorEval.NUM_ERROR);
+               }
+       }
+
+       public final Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
+               double result;
+               try {
+                       result = eval(args, srcCellRow, srcCellCol);
+                       checkValue(result);
+               } catch (EvaluationException e) {
+                       return e.getErrorEval();
+               }
+               return new NumberEval(result);
+       }
+
+       protected abstract double eval(Eval[] args, int srcCellRow, short srcCellCol) throws EvaluationException;
+
+       /* -------------------------------------------------------------------------- */
+       // intermediate sub-classes (one-arg, two-arg and multi-arg
+
+
+       public static abstract class OneArg extends NumericFunction {
+               protected OneArg() {
+                       // no fields to initialise
+               }
+               protected final double eval(Eval[] args, int srcCellRow, short srcCellCol) throws EvaluationException {
+                       if (args.length != 1) {
+                               throw new EvaluationException(ErrorEval.VALUE_INVALID);
+                       }
+                       double d = singleOperandEvaluate(args[0], srcCellRow, srcCellCol);
+                       return evaluate(d);
+               }
+               protected abstract double evaluate(double d) throws EvaluationException;
+       }
+
+       public static abstract class TwoArg extends NumericFunction {
+               protected TwoArg() {
+                       // no fields to initialise
+               }
+               protected final double eval(Eval[] args, int srcCellRow, short srcCellCol) throws EvaluationException {
+                       if (args.length != 2) {
+                               throw new EvaluationException(ErrorEval.VALUE_INVALID);
+                       }
+                       double d0 = singleOperandEvaluate(args[0], srcCellRow, srcCellCol);
+                       double d1 = singleOperandEvaluate(args[1], srcCellRow, srcCellCol);
+                       return evaluate(d0, d1);
+               }
+               protected abstract double evaluate(double d0, double d1) throws EvaluationException;
+       }
+
+       public static abstract class MultiArg extends NumericFunction {
+               private final int _minArgs;
+               private final int _maxArgs;
+               protected MultiArg(int minArgs, int maxArgs) {
+                       _minArgs = minArgs;
+                       _maxArgs = maxArgs;
+               }
+               protected final double eval(Eval[] args, int srcCellRow, short srcCellCol) throws EvaluationException {
+                       int nArgs = args.length;
+                       if (nArgs < _minArgs || nArgs > _maxArgs) {
+                               throw new EvaluationException(ErrorEval.VALUE_INVALID);
+                       }
+                       double[] ds = new double[nArgs];
+                       for(int i=0; i<nArgs; i++) {
+                               ds[i] = singleOperandEvaluate(args[i], srcCellRow, srcCellCol);
+                       }
+                       return evaluate(ds);
+               }
+               protected abstract double evaluate(double[] ds) throws EvaluationException;
+       }
+
+       /* -------------------------------------------------------------------------- */
+
+
+       public static final Function ABS = new OneArg() {
+               protected double evaluate(double d) {
+                       return Math.abs(d);
+               }
+       };
+       public static final Function ACOS = new OneArg() {
+               protected double evaluate(double d) {
+                       return Math.acos(d);
+               }
+       };
+       public static final Function ACOSH = new OneArg() {
+               protected double evaluate(double d) {
+                       return MathX.acosh(d);
+               }
+       };
+       public static final Function ASIN = new OneArg() {
+               protected double evaluate(double d) {
+                       return Math.asin(d);
+               }
+       };
+       public static final Function ASINH = new OneArg() {
+               protected double evaluate(double d) {
+                       return MathX.asinh(d);
+               }
+       };
+       public static final Function ATAN = new OneArg() {
+               protected double evaluate(double d) {
+                       return Math.atan(d);
+               }
+       };
+       public static final Function ATANH = new OneArg() {
+               protected double evaluate(double d) {
+                       return MathX.atanh(d);
+               }
+       };
+       public static final Function COS = new OneArg() {
+               protected double evaluate(double d) {
+                       return Math.cos(d);
+               }
+       };
+       public static final Function COSH = new OneArg() {
+               protected double evaluate(double d) {
+                       return MathX.cosh(d);
+               }
+       };
+       public static final Function DEGREES = new OneArg() {
+               protected double evaluate(double d) {
+                       return Math.toDegrees(d);
+               }
+       };
+       public static final Function DOLLAR = new OneArg() {
+               protected double evaluate(double d) {
+                       return d;
+               }
+       };
+       public static final Function EXP = new OneArg() {
+               protected double evaluate(double d) {
+                       return Math.pow(Math.E, d);
+               }
+       };
+       public static final Function FACT = new OneArg() {
+               protected double evaluate(double d) {
+                       return MathX.factorial((int)d);
+               }
+       };
+       public static final Function INT = new OneArg() {
+               protected double evaluate(double d) {
+                       return Math.round(d-0.5);
+               }
+       };
+       public static final Function LN = new OneArg() {
+               protected double evaluate(double d) {
+                       return Math.log(d);
+               }
+       };
+       public static final Function LOG10 = new OneArg() {
+               protected double evaluate(double d) {
+                       return Math.log(d) / LOG_10_TO_BASE_e;
+               }
+       };
+       public static final Function RADIANS = new OneArg() {
+               protected double evaluate(double d) {
+                       return Math.toRadians(d);
+               }
+       };
+       public static final Function SIGN = new OneArg() {
+               protected double evaluate(double d) {
+                       return MathX.sign(d);
+               }
+       };
+       public static final Function SIN = new OneArg() {
+               protected double evaluate(double d) {
+                       return Math.sin(d);
+               }
+       };
+       public static final Function SINH = new OneArg() {
+               protected double evaluate(double d) {
+                       return MathX.sinh(d);
+               }
+       };
+       public static final Function SQRT = new OneArg() {
+               protected double evaluate(double d) {
+                       return Math.sqrt(d);
+               }
+       };
+
+       public static final Function TAN = new OneArg() {
+               protected double evaluate(double d) {
+                       return Math.tan(d);
+               }
+       };
+       public static final Function TANH = new OneArg() {
+               protected double evaluate(double d) {
+                       return MathX.tanh(d);
+               }
+       };
+
+
+       /* -------------------------------------------------------------------------- */
+
+       public static final Function ATAN2 = new TwoArg() {
+               protected double evaluate(double d0, double d1) throws EvaluationException {
+                       if (d0 == ZERO && d1 == ZERO) {
+                               throw new EvaluationException(ErrorEval.DIV_ZERO);
+                       }
+                       return Math.atan2(d1, d0);
+               }
+       };
+       public static final Function CEILING = new TwoArg() {
+               protected double evaluate(double d0, double d1) {
+                       return MathX.ceiling(d0, d1);
+               }
+       };
+       public static final Function COMBIN = new TwoArg() {
+               protected double evaluate(double d0, double d1) throws EvaluationException {
+                       if (d0 > Integer.MAX_VALUE || d1 > Integer.MAX_VALUE) {
+                               throw new EvaluationException(ErrorEval.NUM_ERROR);
+                       }
+                       return  MathX.nChooseK((int) d0, (int) d1);
+               }
+       };
+       public static final Function FLOOR = new TwoArg() {
+               protected double evaluate(double d0, double d1) throws EvaluationException {
+                       if (d1 == ZERO) {
+                               if (d0 == ZERO) {
+                                       return ZERO;
+                               }
+                               throw new EvaluationException(ErrorEval.DIV_ZERO);
+                       }
+                       return MathX.floor(d0, d1);
+               }
+       };
+       public static final Function MOD = new TwoArg() {
+               protected double evaluate(double d0, double d1) throws EvaluationException {
+                       if (d1 == ZERO) {
+                               throw new EvaluationException(ErrorEval.DIV_ZERO);
+                       }
+                       return MathX.mod(d0, d1);
+               }
+       };
+       public static final Function POWER = new TwoArg() {
+               protected double evaluate(double d0, double d1) {
+                       return Math.pow(d0, d1);
+               }
+       };
+       public static final Function ROUND = new TwoArg() {
+               protected double evaluate(double d0, double d1) {
+                       return MathX.round(d0, (int)d1);
+               }
+       };
+       public static final Function ROUNDDOWN = new TwoArg() {
+               protected double evaluate(double d0, double d1) {
+                       return MathX.roundDown(d0, (int)d1);
+               }
+       };
+       public static final Function ROUNDUP = new TwoArg() {
+               protected double evaluate(double d0, double d1) {
+                       return MathX.roundUp(d0, (int)d1);
+               }
+       };
+
+       /* -------------------------------------------------------------------------- */
+
+       public static final Function LOG = new MultiArg(1,2) {
+               protected double evaluate(double[] ds) {
+
+                       double logE = Math.log(ds[0]);
+                       if (ds.length == 1) {
+                               return logE / LOG_10_TO_BASE_e;
+                       }
+                       double base = ds[1];
+                       if (base == Math.E) {
+                               return logE;
+                       }
+                       return logE / Math.log(base);
+               }
+       };
 }
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/NumericFunctionOneArg.java b/src/java/org/apache/poi/hssf/record/formula/functions/NumericFunctionOneArg.java
deleted file mode 100644 (file)
index 0012013..0000000
+++ /dev/null
@@ -1,172 +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.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.EvaluationException;
-import org.apache.poi.hssf.record.formula.eval.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.OperandResolver;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- * 
- */
-public abstract class NumericFunctionOneArg implements Function {
-
-       public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
-               if (args.length != 1) {
-                       return ErrorEval.VALUE_INVALID;
-               }
-               try {
-                       ValueEval ve = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
-                       double d = OperandResolver.coerceValueToDouble(ve);
-                       if (Double.isNaN(d) || Double.isInfinite(d)) {
-                               return ErrorEval.NUM_ERROR;
-                       }
-                       double result = evaluate(d);
-                       if (Double.isNaN(result) || Double.isInfinite(result)) {
-                               return ErrorEval.NUM_ERROR;
-                       }
-                       return new NumberEval(result);
-               } catch (EvaluationException e) {
-                       return e.getErrorEval();
-               }
-       }
-
-       protected abstract double evaluate(double d);
-
-       public static final Function ABS = new NumericFunctionOneArg() {
-               protected double evaluate(double d) {
-                       return Math.abs(d);
-               }
-       };
-       public static final Function ACOS = new NumericFunctionOneArg() {
-               protected double evaluate(double d) {
-                       return Math.acos(d);
-               }
-       };
-       public static final Function ACOSH = new NumericFunctionOneArg() {
-               protected double evaluate(double d) {
-                       return MathX.acosh(d);
-               }
-       };
-       public static final Function ASIN = new NumericFunctionOneArg() {
-               protected double evaluate(double d) {
-                       return Math.asin(d);
-               }
-       };
-       public static final Function ASINH = new NumericFunctionOneArg() {
-               protected double evaluate(double d) {
-                       return MathX.asinh(d);
-               }
-       };
-       public static final Function ATAN = new NumericFunctionOneArg() {
-               protected double evaluate(double d) {
-                       return Math.atan(d);
-               }
-       };
-       public static final Function ATANH = new NumericFunctionOneArg() {
-               protected double evaluate(double d) {
-                       return MathX.atanh(d);
-               }
-       };
-       public static final Function COS = new NumericFunctionOneArg() {
-               protected double evaluate(double d) {
-                       return Math.cos(d);
-               }
-       };
-       public static final Function COSH = new NumericFunctionOneArg() {
-               protected double evaluate(double d) {
-                       return MathX.cosh(d);
-               }
-       };
-       public static final Function DEGREES = new NumericFunctionOneArg() {
-               protected double evaluate(double d) {
-                       return Math.toDegrees(d);
-               }
-       };
-       public static final Function DOLLAR = new NumericFunctionOneArg() {
-               protected double evaluate(double d) {
-                       return d;
-               }
-       };
-       public static final Function EXP = new NumericFunctionOneArg() {
-               protected double evaluate(double d) {
-                       return Math.pow(Math.E, d);
-               }
-       };
-       public static final Function FACT = new NumericFunctionOneArg() {
-               protected double evaluate(double d) {
-                       return MathX.factorial((int)d);
-               }
-       };
-       public static final Function INT = new NumericFunctionOneArg() {
-               protected double evaluate(double d) {
-                       return Math.round(d-0.5);
-               }
-       };
-       public static final Function LN = new NumericFunctionOneArg() {
-               protected double evaluate(double d) {
-                       return Math.log(d);
-               }
-       };
-    static final double LOG_10_TO_BASE_e = Math.log(10);
-       public static final Function LOG10 = new NumericFunctionOneArg() {
-               protected double evaluate(double d) {
-                       return Math.log(d) / LOG_10_TO_BASE_e;
-               }
-       };
-       public static final Function RADIANS = new NumericFunctionOneArg() {
-               protected double evaluate(double d) {
-                       return Math.toRadians(d);
-               }
-       };
-       public static final Function SIGN = new NumericFunctionOneArg() {
-               protected double evaluate(double d) {
-                       return MathX.sign(d);
-               }
-       };
-       public static final Function SIN = new NumericFunctionOneArg() {
-               protected double evaluate(double d) {
-                       return Math.sin(d);
-               }
-       };
-       public static final Function SINH = new NumericFunctionOneArg() {
-               protected double evaluate(double d) {
-                       return MathX.sinh(d);
-               }
-       };
-       public static final Function SQRT = new NumericFunctionOneArg() {
-               protected double evaluate(double d) {
-                       return Math.sqrt(d);
-               }
-       };
-       
-       public static final Function TAN = new NumericFunctionOneArg() {
-               protected double evaluate(double d) {
-                       return Math.tan(d);
-               }
-       };
-       public static final Function TANH = new NumericFunctionOneArg() {
-               protected double evaluate(double d) {
-                       return MathX.tanh(d);
-               }
-       };
-}
index 4cb8c700d141ee09b2b82b6751c2461c45da646f..74eddac2de51ce9c96e0f97343eda4f6ffdaf1dd 100644 (file)
@@ -22,7 +22,7 @@ package org.apache.poi.hssf.record.formula.functions;
  * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
  *  
  */
-public final class Odd extends NumericFunctionOneArg {
+public final class Odd extends NumericFunction.OneArg {
        private static final long PARITY_MASK = 0xFFFFFFFFFFFFFFFEL;
     
        protected double evaluate(double d) {
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Pmt.java b/src/java/org/apache/poi/hssf/record/formula/functions/Pmt.java
deleted file mode 100644 (file)
index 68a8d43..0000000
+++ /dev/null
@@ -1,91 +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.record.formula.functions;
-
-import org.apache.poi.hssf.record.formula.eval.BoolEval;
-import org.apache.poi.hssf.record.formula.eval.ErrorEval;
-import org.apache.poi.hssf.record.formula.eval.Eval;
-import org.apache.poi.hssf.record.formula.eval.EvaluationException;
-import org.apache.poi.hssf.record.formula.eval.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-
-/**
- * Implementation for the PMT() Excel function.<p/>
- * 
- * <b>Syntax:</b><br/>
- * <b>PMT</b>(<b>rate</b>, <b>nper</b>, <b>pv</b>, fv, type)<p/>
- * 
- * Returns the constant repayment amount required for a loan assuming a constant interest rate.<p/>
- * 
- * <b>rate</b> the loan interest rate.<br/>
- * <b>nper</b> the number of loan repayments.<br/>
- * <b>pv</b> the present value of the future payments (or principle).<br/>
- * <b>fv</b> the future value (default zero) surplus cash at the end of the loan lifetime.<br/>
- * <b>type</b> whether payments are due at the beginning(1) or end(0 - default) of each payment period.<br/>
- * 
- */
-public final class Pmt extends FinanceFunction {
-
-       public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
-               
-               if(args.length < 3 || args.length > 5) {
-                       return ErrorEval.VALUE_INVALID;
-               }
-
-               try {
-                       // evaluate first three (always present) args
-                       double rate = evalArg(args[0], srcRow, srcCol);
-                       double nper = evalArg(args[1], srcRow, srcCol);
-                       double pv  = evalArg(args[2], srcRow, srcCol);
-                       double fv = 0;
-                       boolean arePaymentsAtPeriodBeginning = false;
-
-                       switch (args.length) {
-                               case 5:
-                                       ValueEval ve = singleOperandNumericAsBoolean(args[4], srcRow, srcCol);
-                                       if (ve instanceof ErrorEval) { 
-                                               return ve;
-                                       }
-                                       arePaymentsAtPeriodBeginning = ((BoolEval) ve).getBooleanValue();
-                               case 4:
-                                       fv = evalArg(args[3], srcRow, srcCol);
-                       }
-                       double d = FinanceLib.pmt(rate, nper, pv, fv, arePaymentsAtPeriodBeginning);
-                       if (Double.isNaN(d)) {
-                               return ErrorEval.VALUE_INVALID;
-                       }
-                       if (Double.isInfinite(d)) {
-                               return ErrorEval.NUM_ERROR;
-                       }
-                       return new NumberEval(d);
-               } catch (EvaluationException e) {
-                       return e.getErrorEval();
-               }
-       }
-
-       private double evalArg(Eval arg, int srcRow, short srcCol) throws EvaluationException {
-               ValueEval ve = singleOperandEvaluate(arg, srcRow, srcCol);
-               if(ve instanceof ErrorEval) {
-                       throw new EvaluationException((ErrorEval) ve);
-               }
-               if (ve instanceof NumericValueEval) {
-                       return ((NumericValueEval) ve).getNumberValue();
-               }
-               throw new EvaluationException(ErrorEval.VALUE_INVALID); 
-       }
-}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Power.java b/src/java/org/apache/poi/hssf/record/formula/functions/Power.java
deleted file mode 100644 (file)
index 1327e80..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 6, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.functions;
-
-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.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *  
- */
-public class Power extends NumericFunction {
-   
-    public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
-        double d0 = 0;
-        double d1 = 0;
-        ValueEval retval = null;
-        
-        switch (operands.length) {
-        default:
-            retval = ErrorEval.VALUE_INVALID;
-            break;
-        case 2:
-            ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
-            if (ve instanceof NumericValueEval) {
-                NumericValueEval ne = (NumericValueEval) ve;
-                d0 = ne.getNumberValue();
-            }
-            else if (ve instanceof BlankEval) {
-                // do nothing
-            }
-            else {
-                retval = ErrorEval.NUM_ERROR;
-            }
-            
-            if (retval == null) {
-                ValueEval vev = singleOperandEvaluate(operands[1], srcRow, srcCol);
-                if (vev instanceof NumericValueEval) {
-                    NumericValueEval ne = (NumericValueEval) vev;
-                    d1 = ne.getNumberValue();
-                }
-                else if (vev instanceof BlankEval) {
-                    // do nothing
-                }
-                else {
-                    retval = ErrorEval.NUM_ERROR;
-                }
-            }
-        }
-        
-        if (retval == null) {
-            double d = Math.pow(d0, d1);
-            retval = (Double.isNaN(d) || Double.isNaN(d)) ? (ValueEval) ErrorEval.VALUE_INVALID : new NumberEval(d);
-        }
-        return retval;
-    }
-
-}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Product.java b/src/java/org/apache/poi/hssf/record/formula/functions/Product.java
deleted file mode 100644 (file)
index a88a324..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
-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.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *
- */
-public class Product extends MultiOperandNumericFunction {
-    private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
-        new ValueEvalToNumericXlator((short) (
-                  ValueEvalToNumericXlator.BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED  
-                | ValueEvalToNumericXlator.STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE  
-                ));
-    
-    protected ValueEvalToNumericXlator getXlator() {
-        return DEFAULT_NUM_XLATOR;
-    }
-
-    
-    public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
-        ValueEval retval = null;
-        double[] values = getNumberArray(operands, srcCellRow, srcCellCol);
-        if (values == null) {
-            retval = ErrorEval.VALUE_INVALID;
-        }
-        else {
-            double d = MathX.product(values);
-            retval = (Double.isNaN(d) || Double.isInfinite(d))
-                    ? (ValueEval) ErrorEval.NUM_ERROR
-                    : new NumberEval(d);
-        }
-        
-        return retval;
-    }
-}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Pv.java b/src/java/org/apache/poi/hssf/record/formula/functions/Pv.java
deleted file mode 100644 (file)
index 1d54bee..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.functions;
-
-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.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-
-public class Pv extends FinanceFunction {
-
-    public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
-        double rate = 0, fv = 0, nper = 0, pmt = 0, d = 0;
-        boolean type = false;
-        ValueEval retval = null;
-        ValueEval ve = null;
-        
-        switch (operands.length) {
-        default:
-            retval = ErrorEval.VALUE_INVALID;
-            break;
-        case 5:
-            ve = singleOperandNumericAsBoolean(operands[4], srcRow, srcCol);
-            if (ve instanceof ErrorEval) { retval = ErrorEval.VALUE_INVALID; break; }
-            type = ((BoolEval) ve).getBooleanValue();
-        case 4:
-            ve = singleOperandEvaluate(operands[3], srcRow, srcCol);
-            if (ve instanceof NumericValueEval) fv = ((NumericValueEval) ve).getNumberValue();
-            else { retval = ErrorEval.VALUE_INVALID; break; }
-        case 3:
-            ve = singleOperandEvaluate(operands[1], srcRow, srcCol);
-            if (ve instanceof NumericValueEval) nper  = ((NumericValueEval) ve).getNumberValue();
-            else { retval = ErrorEval.VALUE_INVALID; break; }
-            
-            ve = singleOperandEvaluate(operands[2], srcRow, srcCol);
-            if (ve instanceof NumericValueEval) pmt   = ((NumericValueEval) ve).getNumberValue();
-            else { retval = ErrorEval.VALUE_INVALID; break; }
-
-            ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
-            if (ve instanceof NumericValueEval) rate = ((NumericValueEval) ve).getNumberValue();
-            else { retval = ErrorEval.VALUE_INVALID; break; }
-        }
-        
-        if (retval == null) {
-            d = FinanceLib.pv(rate, nper, pmt, fv, type);
-            retval = (Double.isNaN(d))
-                    ? (ValueEval) ErrorEval.VALUE_INVALID
-                    : (Double.isInfinite(d)) 
-                        ? (ValueEval) ErrorEval.NUM_ERROR 
-                        : new NumberEval(d);
-        }
-        return retval;
-    }
-}
index 95413f08238bb1d4f088b6e82e1dbe493445aad9..1dfc4f8f0571d55f9e9c6d6d9e002ed20ef6c8f1 100644 (file)
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
 package org.apache.poi.hssf.record.formula.functions;
 
 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.NumericValueEval;
+import org.apache.poi.hssf.record.formula.eval.EvaluationException;
 import org.apache.poi.hssf.record.formula.eval.StringEval;
-import org.apache.poi.hssf.record.formula.eval.StringValueEval;
 import org.apache.poi.hssf.record.formula.eval.ValueEval;
 
 /**
- * An implementation of the REPLACE function:
- * Replaces part of a text string based on the number of characters 
- * you specify, with another text string.
+ * An implementation of the Excel REPLACE() function<p/>:
+ * Replaces part of a text string based on the number of characters
+ * you specify, with another text string.<br/>
+ *
+ * <b>Syntax</b>:<br/>
+ * <b>REPLACE</b>(<b>oldText</b>, <b>startNum</b>, <b>numChars</b>, <b>newText</b>)<p/>
+ *
+ * <b>oldText</b>  The text string containing characters to replace<br/>
+ * <b>startNum</b> The position of the first character to replace (1-based)<br/>
+ * <b>numChars</b> The number of characters to replace<br/>
+ * <b>newText</b> The new text value to replace the removed section<br/>
+ *
  * @author Manda Wilson &lt; wilson at c bio dot msk cc dot org &gt;
  */
-public class Replace extends TextFunction {
+public final class Replace extends TextFunction {
+
+       protected ValueEval evaluateFunc(Eval[] args, int srcCellRow, short srcCellCol)
+               throws EvaluationException {
+               if (args.length != 4) {
+                       return ErrorEval.VALUE_INVALID;
+               }
 
-       /**
-        * Replaces part of a text string based on the number of characters 
-        * you specify, with another text string.
-        * 
-        * @see org.apache.poi.hssf.record.formula.eval.Eval
-        */
-    public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {          
-       Eval retval = null;
-        String oldStr = null;
-        String newStr = null;
-        int startNum = 0;
-        int numChars = 0;
-        
-        switch (operands.length) {
-               default:
-                   retval = ErrorEval.VALUE_INVALID;
-               case 4:         
-                       // first operand is text string containing characters to replace
-                   // second operand is position of first character to replace
-                   // third operand is the number of characters in the old string
-                   // you want to replace with new string
-                   // fourth operand is the new string
-                   ValueEval firstveval = singleOperandEvaluate(operands[0], srcCellRow, srcCellCol);
-                   ValueEval secondveval = singleOperandEvaluate(operands[1], srcCellRow, srcCellCol);
-                   ValueEval thirdveval = singleOperandEvaluate(operands[2], srcCellRow, srcCellCol);
-                   ValueEval fourthveval = singleOperandEvaluate(operands[3], srcCellRow, srcCellCol);
-                   if (firstveval instanceof StringValueEval
-                       && secondveval instanceof NumericValueEval
-                       && thirdveval instanceof NumericValueEval
-                       && fourthveval instanceof StringValueEval) {
-                       
-                       StringValueEval oldStrEval = (StringValueEval) firstveval;
-                       oldStr = oldStrEval.getStringValue();
-                       
-                       NumericValueEval startNumEval = (NumericValueEval) secondveval;
-                       // NOTE: it is safe to cast to int here
-                       // because in Excel =REPLACE("task", 2.7, 3, "est") 
-                       // returns test 
-                       // so 2.7 must be truncated to 2
-                       // and =REPLACE("task", 1, 1.9, "") returns ask 
-                       // so 1.9 must be truncated to 1
-                       startNum = (int) startNumEval.getNumberValue();
-                       
-                       NumericValueEval numCharsEval = (NumericValueEval) thirdveval;
-                       numChars = (int) numCharsEval.getNumberValue();
-                                    
-                       StringValueEval newStrEval = (StringValueEval) fourthveval;
-                       newStr = newStrEval.getStringValue();
-                   } else {
-                       retval = ErrorEval.VALUE_INVALID;
-                   }
-           }
-               
-        if (retval == null) {
-                       if (startNum < 1 || numChars < 0) {
-                               retval = ErrorEval.VALUE_INVALID;
-                       } else {
-                               StringBuffer strBuff = new StringBuffer(oldStr);
-                               // remove any characters that should be replaced
-                               if (startNum <= oldStr.length() && numChars != 0) {
-                                       strBuff.delete(startNum - 1, startNum - 1 + numChars);
-                               } 
-                               // now insert (or append) newStr
-                               if (startNum > strBuff.length()) {
-                                       strBuff.append(newStr);
-                               } else {
-                                       strBuff.insert(startNum - 1, newStr);
-                               }
-                               retval = new StringEval(strBuff.toString());
-                       }
-        } 
-               return retval;
-    }
+               String oldStr = evaluateStringArg(args[0], srcCellRow, srcCellCol);
+               int startNum = evaluateIntArg(args[1], srcCellRow, srcCellCol);
+               int numChars = evaluateIntArg(args[2], srcCellRow, srcCellCol);
+               String newStr = evaluateStringArg(args[3], srcCellRow, srcCellCol);
 
+               if (startNum < 1 || numChars < 0) {
+                       return ErrorEval.VALUE_INVALID;
+               }
+               StringBuffer strBuff = new StringBuffer(oldStr);
+               // remove any characters that should be replaced
+               if (startNum <= oldStr.length() && numChars != 0) {
+                       strBuff.delete(startNum - 1, startNum - 1 + numChars);
+               }
+               // now insert (or append) newStr
+               if (startNum > strBuff.length()) {
+                       strBuff.append(newStr);
+               } else {
+                       strBuff.insert(startNum - 1, newStr);
+               }
+               return new StringEval(strBuff.toString());
+       }
 }
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Right.java b/src/java/org/apache/poi/hssf/record/formula/functions/Right.java
deleted file mode 100644 (file)
index 771d045..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.functions;
-
-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.NumberEval;
-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;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *
- */
-public class Right extends TextFunction {
-
-    public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
-        Eval retval = ErrorEval.VALUE_INVALID;
-        int index = 1;
-        switch (operands.length) {
-        default:
-            break;
-        case 2:
-            Eval indexEval = operands[1];
-            index = evaluateAsInteger(indexEval);
-            if (index < 0) {
-                break;
-            }
-        case 1:
-            ValueEval veval = singleOperandEvaluate(operands[0], srcCellRow, srcCellCol);
-            String str = null;
-            if (veval instanceof StringEval) {
-                StringEval stringEval = (StringEval) veval;
-                str = stringEval.getStringValue();
-            }
-            else if (veval instanceof BoolEval) {
-                BoolEval beval = (BoolEval) veval;
-                str = beval.getBooleanValue() ? "TRUE" : "FALSE";
-            }
-            else if (veval instanceof NumberEval) {
-                NumberEval neval = (NumberEval) veval;
-                str = neval.getStringValue();
-            }
-            if (null != str) {
-                int strlen = str.length();
-                str = str.substring(Math.max(0, strlen-index));
-                retval = new StringEval(str);
-            }
-        }
-        return retval;
-    }
-    
-    protected int evaluateAsInteger(Eval eval) {
-        int numval = -1;
-        if (eval instanceof NumberEval) {
-            NumberEval neval = (NumberEval) eval;
-            double d = neval.getNumberValue();
-            numval = (int) d;
-        }
-        else if (eval instanceof StringEval) {
-            StringEval seval = (StringEval) eval;
-            String s = seval.getStringValue();
-            try { 
-                double d = Double.parseDouble(s);
-                numval = (int) d;
-            } 
-            catch (Exception e) {
-            }
-        }
-        else if (eval instanceof BoolEval) {
-            BoolEval beval = (BoolEval) eval;
-            numval = beval.getBooleanValue() ? 1 : 0;
-        }
-        else if (eval instanceof RefEval) {
-            numval = evaluateAsInteger(xlateRefEval((RefEval) eval));
-        }
-        return numval;
-    }
-    
-    protected Eval xlateRefEval(RefEval reval) {
-        Eval retval = reval.getInnerValueEval();
-        
-        if (retval instanceof RefEval) {
-            retval = xlateRefEval((RefEval) retval);
-        }
-        return retval;
-    }
-}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Round.java b/src/java/org/apache/poi/hssf/record/formula/functions/Round.java
deleted file mode 100644 (file)
index db4f1db..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.functions;
-
-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.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-
-public class Round extends NumericFunction {
-
-    public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
-        double d0 = 0;
-        double d1 = 0;
-        ValueEval retval = null;
-
-        switch (operands.length) {
-        default:
-            retval = ErrorEval.VALUE_INVALID;
-            break;
-        case 2:
-            ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
-            if (ve instanceof NumericValueEval) {
-                NumericValueEval ne = (NumericValueEval) ve;
-                d0 = ne.getNumberValue();
-            }
-            else if (ve instanceof BlankEval) {
-                // do nothing
-            }
-            else {
-                retval = ErrorEval.NUM_ERROR;
-            }
-
-            if (retval == null) {
-                ve = singleOperandEvaluate(operands[1], srcRow, srcCol);
-                if (ve instanceof NumericValueEval) {
-                    NumericValueEval ne = (NumericValueEval) ve;
-                    d1 = ne.getNumberValue();
-                }
-                else if (ve instanceof BlankEval) {
-                    // do nothing
-                }
-                else {
-                    retval = ErrorEval.NUM_ERROR;
-                }
-            }
-        }
-
-        if (retval == null) {
-            double d;
-            if (d0 > Integer.MAX_VALUE) {
-                d = (Double.isNaN(d0) || Double.isInfinite(d0))
-                        ? Double.NaN
-                        : 0;
-            }
-            else {
-                d = MathX.round(d0, (int) d1);
-            }
-            retval = (Double.isNaN(d) || Double.isInfinite(d)) ? (ValueEval) ErrorEval.VALUE_INVALID : new NumberEval(d);
-        }
-        return retval;
-    }
-}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Rounddown.java b/src/java/org/apache/poi/hssf/record/formula/functions/Rounddown.java
deleted file mode 100644 (file)
index 1352229..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.functions;
-
-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.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-
-public class Rounddown extends NumericFunction {
-
-    public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
-        double d0 = 0;
-        double d1 = 0;
-        ValueEval retval = null;
-
-        switch (operands.length) {
-        default:
-            retval = ErrorEval.VALUE_INVALID;
-            break;
-        case 2:
-            ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
-            if(ve instanceof ErrorEval) {
-               return ve;
-            }
-            if (ve instanceof NumericValueEval) {
-                NumericValueEval ne = (NumericValueEval) ve;
-                d0 = ne.getNumberValue();
-            }
-            else if (ve instanceof BlankEval) {
-                // do nothing
-            }
-            else {
-                retval = ErrorEval.NUM_ERROR;
-            }
-
-            if (retval == null) {
-                ve = singleOperandEvaluate(operands[1], srcRow, srcCol);
-                if (ve instanceof NumericValueEval) {
-                    NumericValueEval ne = (NumericValueEval) ve;
-                    d1 = ne.getNumberValue();
-                }
-                else if (ve instanceof BlankEval) {
-                    // do nothing
-                }
-                else {
-                    retval = ErrorEval.NUM_ERROR;
-                }
-            }
-        }
-
-        if (retval == null) {
-            double d;
-            if (d0 > Integer.MAX_VALUE) {
-                d = (Double.isInfinite(d0))
-                        ? Double.NaN
-                        : 0;
-            }
-            else {
-                d = MathX.roundDown(d0, (int) d1);
-            }
-            retval = (Double.isNaN(d) || Double.isInfinite(d)) ? (ValueEval) ErrorEval.VALUE_INVALID : new NumberEval(d);
-        }
-        return retval;
-    }
-}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Roundup.java b/src/java/org/apache/poi/hssf/record/formula/functions/Roundup.java
deleted file mode 100644 (file)
index 4dae76d..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.functions;
-
-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.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-
-public class Roundup extends NumericFunction {
-
-    public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
-        double d0 = 0;
-        double d1 = 0;
-        ValueEval retval = null;
-
-        switch (operands.length) {
-        default:
-            retval = ErrorEval.VALUE_INVALID;
-            break;
-        case 2:
-            ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
-            if(ve instanceof ErrorEval) {
-               return ve;
-            }
-            if (ve instanceof NumericValueEval) {
-                NumericValueEval ne = (NumericValueEval) ve;
-                d0 = ne.getNumberValue();
-            }
-            else if (ve instanceof BlankEval) {
-                // do nothing
-            }
-            else {
-                retval = ErrorEval.NUM_ERROR;
-            }
-
-            if (retval == null) {
-                ve = singleOperandEvaluate(operands[1], srcRow, srcCol);
-                if (ve instanceof NumericValueEval) {
-                    NumericValueEval ne = (NumericValueEval) ve;
-                    d1 = ne.getNumberValue();
-                }
-                else if (ve instanceof BlankEval) {
-                    // do nothing
-                }
-                else {
-                    retval = ErrorEval.NUM_ERROR;
-                }
-            }
-        }
-
-        if (retval == null) {
-            double d;
-            if (d0 > Integer.MAX_VALUE) {
-                d = (Double.isNaN(d0))
-                        ? Double.NaN
-                        : 0;
-            }
-            else {
-                d = MathX.roundUp(d0, (int) d1);
-            }
-            retval = (Double.isNaN(d) || Double.isInfinite(d))
-                    ? (ValueEval) ErrorEval.NUM_ERROR
-                    : new NumberEval(d);
-        }
-        return retval;
-    }
-}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Small.java b/src/java/org/apache/poi/hssf/record/formula/functions/Small.java
deleted file mode 100644 (file)
index 9fcdc10..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
-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.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *
- */
-public class Small extends MultiOperandNumericFunction {
-    private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
-        new ValueEvalToNumericXlator((short) (0
-                | ValueEvalToNumericXlator.BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED  
-                | ValueEvalToNumericXlator.STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
-              //| ValueEvalToNumericXlator.EVALUATED_REF_BLANK_IS_PARSED
-              //| ValueEvalToNumericXlator.REF_BLANK_IS_PARSED
-              //| ValueEvalToNumericXlator.BLANK_IS_PARSED
-                ));
-    
-    /**
-     * this is the default impl for the factory method getXlator
-     * of the super class NumericFunction. Subclasses can override this method
-     * if they desire to return a different ValueEvalToNumericXlator instance
-     * than the default.
-     */
-    protected ValueEvalToNumericXlator getXlator() {
-        return DEFAULT_NUM_XLATOR;
-    }
-
-    
-    
-    public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
-        ValueEval retval = null;
-        double[] ops = getNumberArray(operands, srcCellRow, srcCellCol);
-        if (ops == null || ops.length < 2) {
-            retval = ErrorEval.VALUE_INVALID;
-        }
-        else {
-            double[] values = new double[ops.length-1];
-            int k = (int) ops[ops.length-1];
-            System.arraycopy(ops, 0, values, 0, values.length);
-            double d = StatsLib.kthSmallest(values, k);
-            retval = (Double.isNaN(d) || Double.isInfinite(d))
-                    ? (ValueEval) ErrorEval.NUM_ERROR
-                    : new NumberEval(d);
-        }
-        
-        return retval;
-    }
-}
index 8ebccfd9508115083532532ff33dfb695377045c..e78f38caaa0b770ade45ce0daa6857d9377e6405 100644 (file)
@@ -60,36 +60,6 @@ public final class StatsLib {
         return r;
     }
     
-    /**
-     * if v is zero length or contains no duplicates, return value
-     * is Double.NaN. Else returns the value that occurs most times
-     * and if there is a tie, returns the first such value. 
-     * @param v
-     */
-    public static double mode(double[] v) {
-        double r = Double.NaN;
-        
-        // very naive impl, may need to be optimized
-        if (v!=null && v.length > 1) {
-            int[] counts = new int[v.length]; 
-            Arrays.fill(counts, 1);
-            for (int i=0, iSize=v.length; i<iSize; i++) {
-                for (int j=i+1, jSize=v.length; j<jSize; j++) {
-                    if (v[i] == v[j]) counts[i]++;
-                }
-            }
-            double maxv = 0;
-            int maxc = 0;
-            for (int i=0, iSize=counts.length; i<iSize; i++) {
-                if (counts[i] > maxc) {
-                    maxv = v[i];
-                    maxc = counts[i];
-                }
-            }
-            r = (maxc > 1) ? maxv : Double.NaN; // "no-dups" check
-        }
-        return r;
-    }
     
     public static double median(double[] v) {
         double r = Double.NaN;
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Stdev.java b/src/java/org/apache/poi/hssf/record/formula/functions/Stdev.java
deleted file mode 100644 (file)
index 7995e66..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
-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.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *
- */
-public class Stdev extends MultiOperandNumericFunction {
-
-    private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
-        new ValueEvalToNumericXlator((short) (
-                  ValueEvalToNumericXlator.BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED  
-                | ValueEvalToNumericXlator.STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
-                ));
-    
-    /**
-     * this is the default impl for the factory method getXlator
-     * of the super class NumericFunction. Subclasses can override this method
-     * if they desire to return a different ValueEvalToNumericXlator instance
-     * than the default.
-     */
-    protected ValueEvalToNumericXlator getXlator() {
-        return DEFAULT_NUM_XLATOR;
-    }
-
-    
-    
-    public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
-        ValueEval retval = null;
-        double[] values = getNumberArray(operands, srcCellRow, srcCellCol);
-        if (values == null) {
-            retval = ErrorEval.VALUE_INVALID;
-        }
-        else {
-            double d = StatsLib.stdev(values);
-            retval = (Double.isNaN(d) || Double.isInfinite(d))
-                    ? (ValueEval) ErrorEval.NUM_ERROR
-                    : new NumberEval(d);
-        }
-        
-        return retval;
-    }
-}
index 9d2e9ce3618a1a2d570a8c90f670ec268e9e1620..b00d7510e319c77b372ee6f3871e65a779ab7c54 100644 (file)
@@ -1,30 +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.
-*/
-/*
- * Created on May 15, 2005
- *
- */
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
 package org.apache.poi.hssf.record.formula.functions;
 
 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.NumericValueEval;
+import org.apache.poi.hssf.record.formula.eval.EvaluationException;
 import org.apache.poi.hssf.record.formula.eval.StringEval;
-import org.apache.poi.hssf.record.formula.eval.StringValueEval;
 import org.apache.poi.hssf.record.formula.eval.ValueEval;
 
 /**
@@ -32,86 +28,75 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
  * Substitutes text in a text string with new text, some number of times.
  * @author Manda Wilson &lt; wilson at c bio dot msk cc dot org &gt;
  */
-public class Substitute extends TextFunction {
-       private static final int REPLACE_ALL = -1;
-       
-       /**
-        *Substitutes text in a text string with new text, some number of times.
-        * 
-        * @see org.apache.poi.hssf.record.formula.eval.Eval
-        */
-    public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {          
-       Eval retval = null;
-        String oldStr = null;
-        String searchStr = null;
-        String newStr = null;
-        int numToReplace = REPLACE_ALL;
-        
-        switch (operands.length) {
-               default:
-                   retval = ErrorEval.VALUE_INVALID;
-               case 4:
-                       ValueEval fourthveval = singleOperandEvaluate(operands[3], srcCellRow, srcCellCol);
-                       if (fourthveval instanceof NumericValueEval) {
-                               NumericValueEval numToReplaceEval = (NumericValueEval) fourthveval;
-                               // NOTE: it is safe to cast to int here
-                       // because in Excel =SUBSTITUTE("teststr","t","T",1.9) 
-                       // returns Teststr 
-                       // so 1.9 must be truncated to 1
-                               numToReplace = (int) numToReplaceEval.getNumberValue();
-                       } else {
-                               retval = ErrorEval.VALUE_INVALID;
-                       }
-               case 3: 
-                       // first operand is text string containing characters to replace
-                   // second operand is text to find
-                   // third operand is replacement text
-                   ValueEval firstveval = singleOperandEvaluate(operands[0], srcCellRow, srcCellCol);
-                   ValueEval secondveval = singleOperandEvaluate(operands[1], srcCellRow, srcCellCol);
-                   ValueEval thirdveval = singleOperandEvaluate(operands[2], srcCellRow, srcCellCol);
-                   if (firstveval instanceof StringValueEval
-                       && secondveval instanceof StringValueEval
-                       && thirdveval instanceof StringValueEval) {
-                       
-                       StringValueEval oldStrEval = (StringValueEval) firstveval;
-                       oldStr = oldStrEval.getStringValue();
-                       
-                       StringValueEval searchStrEval = (StringValueEval) secondveval;
-                       searchStr = searchStrEval.getStringValue();
-                       
-                       StringValueEval newStrEval = (StringValueEval) thirdveval;
-                       newStr = newStrEval.getStringValue();
-                   } else {
-                       retval = ErrorEval.VALUE_INVALID;
-                   }
-           }
-               
-        if (retval == null) {
-                       if (numToReplace != REPLACE_ALL && numToReplace < 1) {
-                               retval = ErrorEval.VALUE_INVALID;
-                       } else if (searchStr.length() == 0) {
-                               retval = new StringEval(oldStr);
-                       } else {
-                               StringBuffer strBuff = new StringBuffer();
-                               int startIndex = 0;
-                               int nextMatch = -1;
-                               for (int leftToReplace = numToReplace; 
-                                       (leftToReplace > 0 || numToReplace == REPLACE_ALL) 
-                                               && (nextMatch = oldStr.indexOf(searchStr, startIndex)) != -1;
-                                       leftToReplace--) {
-                                       // store everything from end of last match to start of this match
-                                       strBuff.append(oldStr.substring(startIndex, nextMatch));
-                                       strBuff.append(newStr);
-                                       startIndex = nextMatch + searchStr.length();
+public final class Substitute extends TextFunction {
+
+       protected ValueEval evaluateFunc(Eval[] args, int srcCellRow, short srcCellCol)
+                       throws EvaluationException {
+               if (args.length < 3 || args.length > 4) {
+                       return ErrorEval.VALUE_INVALID;
+               }
+
+               String oldStr = evaluateStringArg(args[0], srcCellRow, srcCellCol);
+               String searchStr = evaluateStringArg(args[1], srcCellRow, srcCellCol);
+               String newStr = evaluateStringArg(args[2], srcCellRow, srcCellCol);
+
+               String result;
+               switch (args.length) {
+                       default:
+                               throw new IllegalStateException("Cannot happen");
+                       case 4:
+                               int instanceNumber = evaluateIntArg(args[3], srcCellRow, srcCellCol);
+                               if (instanceNumber < 1) {
+                                       return ErrorEval.VALUE_INVALID;
                                }
+                               result = replaceOneOccurrence(oldStr, searchStr, newStr, instanceNumber);
+                               break;
+                       case 3:
+                               result = replaceAllOccurrences(oldStr, searchStr, newStr);
+               }
+               return new StringEval(result);
+       }
+
+       private static String replaceAllOccurrences(String oldStr, String searchStr, String newStr) {
+               StringBuffer sb = new StringBuffer();
+               int startIndex = 0;
+               int nextMatch = -1;
+               while (true) {
+                       nextMatch = oldStr.indexOf(searchStr, startIndex);
+                       if (nextMatch < 0) {
                                // store everything from end of last match to end of string
-                               if (startIndex < oldStr.length()) {
-                                       strBuff.append(oldStr.substring(startIndex));
-                               }
-                               retval = new StringEval(strBuff.toString());
+                               sb.append(oldStr.substring(startIndex));
+                               return sb.toString();
+                       }
+                       // store everything from end of last match to start of this match
+                       sb.append(oldStr.substring(startIndex, nextMatch));
+                       sb.append(newStr);
+                       startIndex = nextMatch + searchStr.length();
+               }
+       }
+
+       private static String replaceOneOccurrence(String oldStr, String searchStr, String newStr, int instanceNumber) {
+               if (searchStr.length() < 1) {
+                       return oldStr;
+               }
+               int startIndex = 0;
+               int nextMatch = -1;
+               int count=0;
+               while (true) {
+                       nextMatch = oldStr.indexOf(searchStr, startIndex);
+                       if (nextMatch < 0) {
+                               // not enough occurrences found - leave unchanged
+                               return oldStr;
+                       }
+                       count++;
+                       if (count == instanceNumber) {
+                               StringBuffer sb = new StringBuffer(oldStr.length() + newStr.length());
+                               sb.append(oldStr.substring(0, nextMatch));
+                               sb.append(newStr);
+                               sb.append(oldStr.substring(nextMatch + searchStr.length()));
+                               return sb.toString();
                        }
-        } 
-               return retval;
-    }
-    
+                       startIndex = nextMatch + searchStr.length();
+               }
+       }
 }
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Sum.java b/src/java/org/apache/poi/hssf/record/formula/functions/Sum.java
deleted file mode 100644 (file)
index d3dcd9b..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
-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.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt; 
- *
- */
-public class Sum extends MultiOperandNumericFunction {
-    private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
-        new ValueEvalToNumericXlator((short) (
-                  ValueEvalToNumericXlator.BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED  
-                | ValueEvalToNumericXlator.STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE  
-                ));
-    
-    protected ValueEvalToNumericXlator getXlator() {
-        return DEFAULT_NUM_XLATOR;
-    }
-
-    
-    public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
-        ValueEval retval = null;
-        double[] values = getNumberArray(operands, srcCellRow, srcCellCol);
-        if (values == null) {
-            retval = ErrorEval.VALUE_INVALID;
-        }
-        else {
-            double d = values.length > 0 ? MathX.sum(values) : 0;
-            retval = (Double.isNaN(d) || Double.isInfinite(d))
-                    ? (ValueEval) ErrorEval.NUM_ERROR
-                    : new NumberEval(d);
-        }
-        
-        return retval;
-    }
-}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Sumsq.java b/src/java/org/apache/poi/hssf/record/formula/functions/Sumsq.java
deleted file mode 100644 (file)
index b74b416..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
-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.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt; 
- *
- */
-public class Sumsq extends MultiOperandNumericFunction {
-    private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
-        new ValueEvalToNumericXlator((short) (
-                ValueEvalToNumericXlator.BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED  
-                | ValueEvalToNumericXlator.STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED  
-              //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE  
-              //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE  
-              //| ValueEvalToNumericXlator.REF_BLANK_IS_PARSED
-              //| ValueEvalToNumericXlator.BLANK_IS_PARSED
-                ));
-    
-    protected ValueEvalToNumericXlator getXlator() {
-        return DEFAULT_NUM_XLATOR;
-    }
-
-    
-    public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
-        ValueEval retval = null;
-        double[] values = getNumberArray(operands, srcCellRow, srcCellCol);
-        if (values == null) {
-            retval = ErrorEval.VALUE_INVALID;
-        }
-        else {
-            double d = MathX.sumsq(values);
-            retval = (Double.isNaN(d) || Double.isInfinite(d))
-                    ? (ValueEval) ErrorEval.NUM_ERROR
-                    : new NumberEval(d);
-        }
-        
-        return retval;
-    }
-}
index 9da17761275c4f521cfc5b19a0248c607c821425..df7db7ad32892b8e96f92d3674fa3f0db9831bb1 100644 (file)
@@ -1,31 +1,29 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 22, 2005
- *
- */
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
 package org.apache.poi.hssf.record.formula.functions;
 
-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.RefEval;
-import org.apache.poi.hssf.record.formula.eval.StringValueEval;
+import org.apache.poi.hssf.record.formula.eval.EvaluationException;
+import org.apache.poi.hssf.record.formula.eval.NumberEval;
+import org.apache.poi.hssf.record.formula.eval.OperandResolver;
+import org.apache.poi.hssf.record.formula.eval.StringEval;
 import org.apache.poi.hssf.record.formula.eval.ValueEval;
 
 /**
@@ -33,75 +31,164 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
  *
  */
 public abstract class TextFunction implements Function {
-    
-    protected static final String EMPTY_STRING = "";
-    
-    protected ValueEval singleOperandEvaluate(Eval eval, int srcRow, short srcCol) {
-        ValueEval retval;
-        if (eval instanceof AreaEval) {
-            AreaEval ae = (AreaEval) eval;
-            if (ae.contains(srcRow, srcCol)) { // circular ref!
-                retval = ErrorEval.CIRCULAR_REF_ERROR;
-            }
-            else if (ae.isRow()) {
-                if (ae.containsColumn(srcCol)) {
-                    ValueEval ve = ae.getValueAt(ae.getFirstRow(), srcCol);
-                    retval = attemptXlateToText(ve);
-                }
-                else {
-                    retval = ErrorEval.VALUE_INVALID;
-                }
-            }
-            else if (ae.isColumn()) {
-                if (ae.containsRow(srcRow)) {
-                    ValueEval ve = ae.getValueAt(srcRow, ae.getFirstColumn());
-                    retval = attemptXlateToText(ve);
-                }
-                else {
-                    retval = ErrorEval.VALUE_INVALID;
-                }
-            }
-            else {
-                retval = ErrorEval.VALUE_INVALID;
-            }
-        }
-        else {
-            retval = attemptXlateToText((ValueEval) eval);
-        }
-        return retval;
-    }
-
-    
-    /**
-     * converts from Different ValueEval types to StringEval.
-     * Note: AreaEvals are not handled, if arg is an AreaEval,
-     * the returned value is ErrorEval.VALUE_INVALID
-     * @param ve
-     */
-    protected ValueEval attemptXlateToText(ValueEval ve) {
-        ValueEval retval;
-        if (ve instanceof StringValueEval) {
-            retval = ve;
-        }
-        else if (ve instanceof RefEval) {
-            RefEval re = (RefEval) ve;
-            ValueEval ive = re.getInnerValueEval();
-            if (ive instanceof StringValueEval) {
-                retval = ive;
-            }
-            else if (ive instanceof BlankEval) {
-                retval = ive;
-            }
-            else {
-                retval = ErrorEval.VALUE_INVALID;
-            }
-        }
-        else if (ve instanceof BlankEval) {
-            retval = ve;
-        }
-        else {
-            retval = ErrorEval.VALUE_INVALID;
-        }
-        return retval;
-    }
+
+       protected static final String EMPTY_STRING = "";
+
+       protected static final String evaluateStringArg(Eval eval, int srcRow, short srcCol) throws EvaluationException {
+               ValueEval ve = OperandResolver.getSingleValue(eval, srcRow, srcCol);
+               return OperandResolver.coerceValueToString(ve);
+       }
+       protected static final int evaluateIntArg(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException {
+               ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
+               return OperandResolver.coerceValueToInt(ve);
+       }
+
+       public final Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
+               try {
+                       return evaluateFunc(args, srcCellRow, srcCellCol);
+               } catch (EvaluationException e) {
+                       return e.getErrorEval();
+               }
+       }
+
+       protected abstract ValueEval evaluateFunc(Eval[] args, int srcCellRow, short srcCellCol) throws EvaluationException;
+
+       /* ---------------------------------------------------------------------- */
+       
+       private static abstract class SingleArgTextFunc extends TextFunction {
+
+               protected SingleArgTextFunc() {
+                       // no fields to initialise
+               }
+               protected ValueEval evaluateFunc(Eval[] args, int srcCellRow, short srcCellCol)
+                               throws EvaluationException {
+                       if (args.length != 1) {
+                               return ErrorEval.VALUE_INVALID;
+                       }
+                       String arg = evaluateStringArg(args[0], srcCellRow, srcCellCol);
+                       return evaluate(arg);
+               }
+               protected abstract ValueEval evaluate(String arg);
+       }
+
+       public static final Function LEN = new SingleArgTextFunc() {
+               protected ValueEval evaluate(String arg) {
+                       return new NumberEval(arg.length());
+               }
+       };
+       public static final Function LOWER = new SingleArgTextFunc() {
+               protected ValueEval evaluate(String arg) {
+                       return new StringEval(arg.toLowerCase());
+               }
+       };
+       public static final Function UPPER = new SingleArgTextFunc() {
+               protected ValueEval evaluate(String arg) {
+                       return new StringEval(arg.toUpperCase());
+               }
+       };
+       /**
+        * An implementation of the TRIM function:
+        * Removes leading and trailing spaces from value if evaluated operand
+        *  value is string.
+        * @author Manda Wilson &lt; wilson at c bio dot msk cc dot org &gt;
+        */
+       public static final Function TRIM = new SingleArgTextFunc() {
+               protected ValueEval evaluate(String arg) {
+                       return new StringEval(arg.trim());
+               }
+       };
+
+       /**
+        * An implementation of the MID function<br/>
+        * MID returns a specific number of
+        * characters from a text string, starting at the specified position.<p/>
+        * 
+        * <b>Syntax<b>:<br/> <b>MID</b>(<b>text</b>, <b>start_num</b>,
+        * <b>num_chars</b>)<br/>
+        * 
+        * @author Manda Wilson &lt; wilson at c bio dot msk cc dot org &gt;
+        */
+       public static final Function MID = new TextFunction() {
+
+               protected ValueEval evaluateFunc(Eval[] args, int srcCellRow, short srcCellCol)
+                               throws EvaluationException {
+                       if (args.length != 3) {
+                               return ErrorEval.VALUE_INVALID;
+                       }
+
+                       String text = evaluateStringArg(args[0], srcCellRow, srcCellCol);
+                       int startCharNum = evaluateIntArg(args[1], srcCellRow, srcCellCol);
+                       int numChars = evaluateIntArg(args[2], srcCellRow, srcCellCol);
+                       int startIx = startCharNum - 1; // convert to zero-based
+
+                       // Note - for start_num arg, blank/zero causes error(#VALUE!),
+                       // but for num_chars causes empty string to be returned.
+                       if (startIx < 0) {
+                               return ErrorEval.VALUE_INVALID;
+                       }
+                       if (numChars < 0) {
+                               return ErrorEval.VALUE_INVALID;
+                       }
+                       int len = text.length();
+                       if (numChars < 0 || startIx > len) {
+                               return new StringEval("");
+                       }
+                       int endIx = Math.min(startIx + numChars, len);
+                       String result = text.substring(startIx, endIx);
+                       return new StringEval(result);
+               }
+       };
+
+       private static final class LeftRight extends TextFunction {
+
+               private final boolean _isLeft;
+               protected LeftRight(boolean isLeft) {
+                       _isLeft = isLeft;
+               }
+               protected ValueEval evaluateFunc(Eval[] args, int srcCellRow, short srcCellCol)
+                               throws EvaluationException {
+                       if (args.length != 2) {
+                               return ErrorEval.VALUE_INVALID;
+                       }
+                       String arg = evaluateStringArg(args[0], srcCellRow, srcCellCol);
+                       int index = evaluateIntArg(args[1], srcCellRow, srcCellCol);
+
+                       String result;
+                       if (_isLeft) {
+                               result = arg.substring(0, Math.min(arg.length(), index));
+                       } else {
+                               result = arg.substring(Math.max(0, arg.length()-index));
+                       }
+                       return new StringEval(result);
+               }
+       }
+
+       public static final Function LEFT = new LeftRight(true);
+       public static final Function RIGHT = new LeftRight(false);
+
+       public static final Function CONCATENATE = new TextFunction() {
+
+               protected ValueEval evaluateFunc(Eval[] args, int srcCellRow, short srcCellCol)
+                               throws EvaluationException {
+                       StringBuffer sb = new StringBuffer();
+                       for (int i=0, iSize=args.length; i<iSize; i++) {
+                               sb.append(evaluateStringArg(args[i], srcCellRow, srcCellCol));
+                       }
+                       return new StringEval(sb.toString());
+               }
+       };
+
+       public static final Function EXACT = new TextFunction() {
+
+               protected ValueEval evaluateFunc(Eval[] args, int srcCellRow, short srcCellCol)
+                               throws EvaluationException {
+                       if (args.length != 2) {
+                               return ErrorEval.VALUE_INVALID;
+                       }
+
+                       String s0 = evaluateStringArg(args[0], srcCellRow, srcCellCol);
+                       String s1 = evaluateStringArg(args[1], srcCellRow, srcCellCol);
+                       return BoolEval.valueOf(s0.equals(s1));
+               }
+       };
 }
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Trim.java b/src/java/org/apache/poi/hssf/record/formula/functions/Trim.java
deleted file mode 100644 (file)
index 87e29ee..0000000
+++ /dev/null
@@ -1,53 +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.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.EvaluationException;
-import org.apache.poi.hssf.record.formula.eval.OperandResolver;
-import org.apache.poi.hssf.record.formula.eval.StringEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-
-/**
- * An implementation of the TRIM function:
- * Removes leading and trailing spaces from value if evaluated operand
- *  value is string.
- * @author Manda Wilson &lt; wilson at c bio dot msk cc dot org &gt;
- */
-public final class Trim extends TextFunction {
-
-       public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
-               
-               if(args.length != 1) {
-                       return ErrorEval.VALUE_INVALID;
-               }
-               
-               try {
-                       ValueEval veval = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
-
-                       String str = OperandResolver.coerceValueToString(veval);
-                       str = str.trim();
-                       if(str.length() < 1) {
-                               return StringEval.EMPTY_INSTANCE;
-                       }
-                       return new StringEval(str);
-               } catch (EvaluationException e) {
-                       return e.getErrorEval();
-               }
-       }
-}
diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Upper.java b/src/java/org/apache/poi/hssf/record/formula/functions/Upper.java
deleted file mode 100644 (file)
index 5146a66..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-/*
- * Created on May 15, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.functions;
-
-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.StringEval;
-import org.apache.poi.hssf.record.formula.eval.StringValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-
-/**
- * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
- *
- */
-public class Upper extends TextFunction {
-
-
-    public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
-        ValueEval retval = null;
-        String s = null;
-        
-        switch (operands.length) {
-        default:
-            retval = ErrorEval.VALUE_INVALID;
-            break;
-        case 1:
-            ValueEval ve = singleOperandEvaluate(operands[0], srcCellRow, srcCellCol);
-            if (ve instanceof StringValueEval) {
-                StringValueEval sve = (StringValueEval) ve;
-                s = sve.getStringValue();
-            }
-            else if (ve instanceof BlankEval) {}
-            else {
-                retval = ErrorEval.VALUE_INVALID;
-                break;
-            }
-        }
-        
-        if (retval == null) {
-            s = (s == null) ? EMPTY_STRING : s;
-            retval = new StringEval(s.toUpperCase());
-        }
-        
-        return retval;
-    }
-}
index 21fb5707e6cbd6b14842a8abb4119406dc30d4ab..1f88cce2bd132059d506e3eeb5b8ce4ec00145cc 100644 (file)
@@ -23,6 +23,7 @@ import java.util.Calendar;
 import java.util.Date;
 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.Sheet;
@@ -60,8 +61,8 @@ import org.apache.poi.ss.usermodel.RichTextString;
  * Cells can be numeric, formula-based or string-based (text).  The cell type
  * specifies this.  String cells cannot conatin numbers and numeric cells cannot
  * contain strings (at least according to our model).  Client apps should do the
- * conversions themselves.  Formula cells have the formula string, as well as 
- * the formula result, which can be numeric or string. 
+ * conversions themselves.  Formula cells have the formula string, as well as
+ * the formula result, which can be numeric or string.
  * <p>
  * Cells should have their number (0 based) before being added to a row.  Only
  * cells that have values should be added.
@@ -86,14 +87,15 @@ public class HSSFCell implements Cell {
     public final static int CELL_TYPE_BOOLEAN = 4;
     /** Error   Cell type (5) @see #setCellType(int) @see #getCellType() */
     public final static int CELL_TYPE_ERROR   = 5;
-    
+
     public final static short        ENCODING_UNCHANGED          = -1;
     public final static short        ENCODING_COMPRESSED_UNICODE = 0;
     public final static short        ENCODING_UTF_16             = 1;
+
+    private final HSSFWorkbook       book;
+    private final HSSFSheet          sheet;
     private int                      cellType;
     private HSSFRichTextString       stringValue;
-    private HSSFWorkbook             book;
-    private Sheet                    sheet;
     private CellValueRecordInterface record;
     private HSSFComment              comment;
 
@@ -113,7 +115,7 @@ public class HSSFCell implements Cell {
      *
      * @see org.apache.poi.hssf.usermodel.HSSFRow#createCell(short)
      */
-    protected HSSFCell(HSSFWorkbook book, Sheet sheet, int row, short col)
+    protected HSSFCell(HSSFWorkbook book, HSSFSheet sheet, int row, short col)
     {
         checkBounds(col);
         stringValue  = null;
@@ -123,9 +125,12 @@ public class HSSFCell implements Cell {
         // Relying on the fact that by default the cellType is set to 0 which
         // is different to CELL_TYPE_BLANK hence the following method call correctly
         // creates a new blank cell.
-        short xfindex = sheet.getXFIndexForColAt(col);
+        short xfindex = sheet.getSheet().getXFIndexForColAt(col);
         setCellType(CELL_TYPE_BLANK, false, row, col,xfindex);
     }
+    public HSSFSheet getSheet() {
+        return sheet;
+    }
 
     /**
      * Creates new Cell - Should only be called by HSSFRow.  This creates a cell
@@ -140,7 +145,7 @@ public class HSSFCell implements Cell {
      *                Type of cell
      * @see org.apache.poi.hssf.usermodel.HSSFRow#createCell(short,int)
      */
-    protected HSSFCell(HSSFWorkbook book, Sheet sheet, int row, short col,
+    protected HSSFCell(HSSFWorkbook book, HSSFSheet sheet, int row, short col,
                        int type)
     {
         checkBounds(col);
@@ -148,8 +153,8 @@ public class HSSFCell implements Cell {
         stringValue  = null;
         this.book    = book;
         this.sheet   = sheet;
-        
-        short xfindex = sheet.getXFIndexForColAt(col);
+
+        short xfindex = sheet.getSheet().getXFIndexForColAt(col);
         setCellType(type,false,row,col,xfindex);
     }
 
@@ -161,7 +166,7 @@ public class HSSFCell implements Cell {
      * @param sheet - Sheet record of the sheet containing this cell
      * @param cval - the Cell Value Record we wish to represent
      */
-    protected HSSFCell(HSSFWorkbook book, Sheet sheet, CellValueRecordInterface cval) {
+    protected HSSFCell(HSSFWorkbook book, HSSFSheet sheet, CellValueRecordInterface cval) {
         record      = cval;
         cellType    = determineType(cval);
         stringValue = null;
@@ -190,10 +195,10 @@ public class HSSFCell implements Cell {
      * used internally -- given a cell value record, figure out its type
      */
     private static int determineType(CellValueRecordInterface cval) {
-       if (cval instanceof FormulaRecordAggregate) {
-               return HSSFCell.CELL_TYPE_FORMULA;
-       }
-       // all others are plain BIFF records
+        if (cval instanceof FormulaRecordAggregate) {
+            return HSSFCell.CELL_TYPE_FORMULA;
+        }
+        // all others are plain BIFF records
         Record record = ( Record ) cval;
         switch (record.getSid()) {
 
@@ -209,13 +214,13 @@ public class HSSFCell implements Cell {
         }
         throw new RuntimeException("Bad cell value rec (" + cval.getClass().getName() + ")");
     }
-    
+
     /**
      * Returns the Workbook that this Cell is bound to
      * @return
      */
     protected Workbook getBoundWorkbook() {
-       return book.getWorkbook();
+        return book.getWorkbook();
     }
 
     /**
@@ -233,7 +238,7 @@ public class HSSFCell implements Cell {
     {
         record.setColumn(num);
     }
-    
+
     /**
      * Updates the cell record's idea of what
      *  column it belongs in (0 based)
@@ -241,7 +246,7 @@ public class HSSFCell implements Cell {
      */
     protected void updateCellNum(short num)
     {
-       record.setColumn(num);
+        record.setColumn(num);
     }
 
     /**
@@ -295,7 +300,7 @@ public class HSSFCell implements Cell {
                 FormulaRecordAggregate frec;
 
                 if (cellType != this.cellType) {
-                    frec = sheet.createFormula(row, col);
+                    frec = sheet.getSheet().createFormula(row, col);
                 } else {
                     frec = (FormulaRecordAggregate) record;
                     frec.setRow(row);
@@ -421,17 +426,17 @@ public class HSSFCell implements Cell {
                 errRec.setColumn(col);
                 if (setValue)
                 {
-                    errRec.setValue(getErrorCellValue());
+                    errRec.setValue((byte)HSSFErrorConstants.ERROR_VALUE);
                 }
                 errRec.setXFIndex(styleIndex);
                 errRec.setRow(row);
                 record = errRec;
                 break;
         }
-        if (cellType != this.cellType && 
+        if (cellType != this.cellType &&
             this.cellType!=-1 )  // Special Value to indicate an uninitialized Cell
         {
-            sheet.replaceValueRecord(record);
+            sheet.getSheet().replaceValueRecord(record);
         }
         this.cellType = cellType;
     }
@@ -457,21 +462,20 @@ public class HSSFCell implements Cell {
      *        precalculated value, for numerics we'll set its value. For other types we
      *        will change the cell to a numeric cell and set its value.
      */
-    public void setCellValue(double value)
-    {
+    public void setCellValue(double value) {
         int row=record.getRow();
         short col=record.getColumn();
         short styleIndex=record.getXFIndex();
-        if ((cellType != CELL_TYPE_NUMERIC) && (cellType != CELL_TYPE_FORMULA))
-        {
-            setCellType(CELL_TYPE_NUMERIC, false, row, col, styleIndex);
-        }
-        
-        // Save into the appropriate record
-        if(record instanceof FormulaRecordAggregate) {
-               (( FormulaRecordAggregate ) record).getFormulaRecord().setValue(value);
-        } else {
-               (( NumberRecord ) record).setValue(value);
+
+        switch (cellType) {
+            default:
+                setCellType(CELL_TYPE_NUMERIC, false, row, col, styleIndex);
+            case CELL_TYPE_ERROR:
+                (( NumberRecord ) record).setValue(value);
+                break;
+            case CELL_TYPE_FORMULA:
+                ((FormulaRecordAggregate)record).getFormulaRecord().setValue(value);
+                break;
         }
     }
 
@@ -491,7 +495,7 @@ public class HSSFCell implements Cell {
     /**
      * set a date value for the cell. Excel treats dates as numeric so you will need to format the cell as
      * a date.
-     * 
+     *
      * This will set the cell value based on the Calendar's timezone. As Excel
      * does not support timezones this means that both 20:00+03:00 and
      * 20:00-03:00 will be reported as the same value (20:00) even that there
@@ -547,31 +551,20 @@ public class HSSFCell implements Cell {
             return;
         }
         if (cellType == CELL_TYPE_FORMULA) {
-            // Set the 'pre-evaluated result' for the formula 
+            // Set the 'pre-evaluated result' for the formula
             // note - formulas do not preserve text formatting.
             FormulaRecordAggregate fr = (FormulaRecordAggregate) record;
-            
-            // Save the string into a String Record, creating
-            //  one if required
-            StringRecord sr = fr.getStringRecord();
-            if(sr == null) {
-               // Wasn't a string before, need a new one
-               sr = new StringRecord();
-                fr.setStringRecord(sr);
-            }
-            
-            // Save, loosing the formatting
-            sr.setString(hvalue.getString());
+            fr.setCachedStringResult(hvalue.getString());
             // Update our local cache to the un-formatted version
-            stringValue = new HSSFRichTextString(sr.getString());
-            
+            stringValue = new HSSFRichTextString(value.getString());
+
             // All done
             return;
         }
 
         // If we get here, we're not dealing with a formula,
         //  so handle things as a normal rich text cell
-        
+
         if (cellType != CELL_TYPE_STRING) {
             setCellType(CELL_TYPE_STRING, false, row, col, styleIndex);
         }
@@ -599,95 +592,95 @@ public class HSSFCell implements Cell {
         FormulaRecord frec = rec.getFormulaRecord();
         frec.setOptions((short) 2);
         frec.setValue(0);
-        
+
         //only set to default if there is no extended format index already set
         if (rec.getXFIndex() == (short)0) {
-                       rec.setXFIndex((short) 0x0f);
-               }
+            rec.setXFIndex((short) 0x0f);
+        }
         Ptg[] ptgs = FormulaParser.parse(formula, book);
         frec.setParsedExpression(ptgs);
     }
+    /* package */ void setFormulaOnly(Ptg[] ptgs) {
+        if (ptgs == null) {
+            throw new IllegalArgumentException("ptgs must not be null");
+        }
+        ((FormulaRecordAggregate)record).getFormulaRecord().setParsedExpression(ptgs);
+    }
 
     public String getCellFormula() {
         return FormulaParser.toFormulaString(book, ((FormulaRecordAggregate)record).getFormulaRecord().getParsedExpression());
     }
 
+    /**
+     * Used to help format error messages
+     */
+    private static String getCellTypeName(int cellTypeCode) {
+        switch (cellTypeCode) {
+            case CELL_TYPE_BLANK:   return "blank";
+            case CELL_TYPE_STRING:  return "text";
+            case CELL_TYPE_BOOLEAN: return "boolean";
+            case CELL_TYPE_ERROR:   return "error";
+            case CELL_TYPE_NUMERIC: return "numeric";
+            case CELL_TYPE_FORMULA: return "formula";
+        }
+        return "#unknown cell type (" + cellTypeCode + ")#";
+    }
+
+    private static RuntimeException typeMismatch(int expectedTypeCode, int actualTypeCode, boolean isFormulaCell) {
+        String msg = "Cannot get a "
+            + getCellTypeName(expectedTypeCode) + " value from a "
+            + getCellTypeName(actualTypeCode) + " " + (isFormulaCell ? "formula " : "") + "cell";
+        return new IllegalStateException(msg);
+    }
+    private static void checkFormulaCachedValueType(int expectedTypeCode, FormulaRecord fr) {
+        int cachedValueType = fr.getCachedResultType();
+        if (cachedValueType != expectedTypeCode) {
+            throw typeMismatch(expectedTypeCode, cachedValueType, true);
+        }
+    }
 
     /**
-     * Get the value of the cell as a number.  
+     * Get the value of the cell as a number.
      * For strings we throw an exception.
      * For blank cells we return a 0.
      * See {@link HSSFDataFormatter} for turning this
      *  number into a string similar to that which
-     *  Excel would render this number as. 
+     *  Excel would render this number as.
      */
-    public double getNumericCellValue()
-    {
-        if (cellType == CELL_TYPE_BLANK)
-        {
-            return 0;
-        }
-        if (cellType == CELL_TYPE_STRING)
-        {
-            throw new NumberFormatException(
-                "You cannot get a numeric value from a String based cell");
-        }
-        if (cellType == CELL_TYPE_BOOLEAN)
-        {
-            throw new NumberFormatException(
-                "You cannot get a numeric value from a boolean cell");
-        }
-        if (cellType == CELL_TYPE_ERROR)
-        {
-            throw new NumberFormatException(
-                "You cannot get a numeric value from an error cell");
-        }
-        if(cellType == CELL_TYPE_NUMERIC)
-        {
-          return ((NumberRecord)record).getValue();
-        }
-        if(cellType == CELL_TYPE_FORMULA)
-        {
-          return ((FormulaRecordAggregate)record).getFormulaRecord().getValue();
+    public double getNumericCellValue() {
+
+        switch(cellType) {
+            case CELL_TYPE_BLANK:
+                return 0.0;
+            case CELL_TYPE_NUMERIC:
+                return ((NumberRecord)record).getValue();
+            default:
+                throw typeMismatch(CELL_TYPE_NUMERIC, cellType, false);
+            case CELL_TYPE_FORMULA:
+                break;
         }
-        throw new NumberFormatException("Unknown Record Type in Cell:"+cellType);
+        FormulaRecord fr = ((FormulaRecordAggregate)record).getFormulaRecord();
+        checkFormulaCachedValueType(CELL_TYPE_NUMERIC, fr);
+        return fr.getValue();
     }
 
     /**
-     * Get the value of the cell as a date.  
+     * Get the value of the cell as a date.
      * For strings we throw an exception.
      * For blank cells we return a null.
      * See {@link HSSFDataFormatter} for formatting
      *  this date into a string similar to how excel does.
      */
-    public Date getDateCellValue()
-    {
-        if (cellType == CELL_TYPE_BLANK)
-        {
+    public Date getDateCellValue() {
+
+        if (cellType == CELL_TYPE_BLANK) {
             return null;
         }
-        if (cellType == CELL_TYPE_STRING)
-        {
-            throw new NumberFormatException(
-                "You cannot get a date value from a String based cell");
-        }
-        if (cellType == CELL_TYPE_BOOLEAN)
-        {
-            throw new NumberFormatException(
-                "You cannot get a date value from a boolean cell");
-        }
-        if (cellType == CELL_TYPE_ERROR)
-        {
-            throw new NumberFormatException(
-                "You cannot get a date value from an error cell");
-        }
-        double value=this.getNumericCellValue();
+        double value = getNumericCellValue();
         if (book.getWorkbook().isUsing1904DateWindowing()) {
-            return HSSFDateUtil.getJavaDate(value,true);
-        }
-        else {
-            return HSSFDateUtil.getJavaDate(value,false);
+            return HSSFDateUtil.getJavaDate(value, true);
         }
+        return HSSFDateUtil.getJavaDate(value, false);
     }
 
     /**
@@ -708,33 +701,22 @@ public class HSSFCell implements Cell {
      * For blank cells we return an empty string.
      * For formulaCells that are not string Formulas, we return empty String
      */
+    public HSSFRichTextString getRichStringCellValue() {
 
-    public HSSFRichTextString getRichStringCellValue()
-    {
-        if (cellType == CELL_TYPE_BLANK)
-        {
-            return new HSSFRichTextString("");
-        }
-        if (cellType == CELL_TYPE_NUMERIC)
-        {
-            throw new NumberFormatException(
-                "You cannot get a string value from a numeric cell");
-        }
-        if (cellType == CELL_TYPE_BOOLEAN)
-        {
-            throw new NumberFormatException(
-                "You cannot get a string value from a boolean cell");
-        }
-        if (cellType == CELL_TYPE_ERROR)
-        {
-            throw new NumberFormatException(
-                "You cannot get a string value from an error cell");
-        }
-        if (cellType == CELL_TYPE_FORMULA) 
-        {
-            if (stringValue==null) return new HSSFRichTextString("");
+        switch(cellType) {
+            case CELL_TYPE_BLANK:
+                return new HSSFRichTextString("");
+            case CELL_TYPE_STRING:
+                return stringValue;
+            default:
+                throw typeMismatch(CELL_TYPE_STRING, cellType, false);
+            case CELL_TYPE_FORMULA:
+                break;
         }
-        return stringValue;
+        FormulaRecordAggregate fra = ((FormulaRecordAggregate)record);
+        checkFormulaCachedValueType(CELL_TYPE_STRING, fra.getFormulaRecord());
+        String strVal = fra.getStringValue();
+        return new HSSFRichTextString(strVal == null ? "" : strVal);
     }
 
     /**
@@ -745,47 +727,56 @@ public class HSSFCell implements Cell {
      *        will change the cell to a boolean cell and set its value.
      */
 
-    public void setCellValue(boolean value)
-    {
+    public void setCellValue(boolean value) {
         int row=record.getRow();
         short col=record.getColumn();
         short styleIndex=record.getXFIndex();
-        if ((cellType != CELL_TYPE_BOOLEAN ) && ( cellType != CELL_TYPE_FORMULA))
-        {
-            setCellType(CELL_TYPE_BOOLEAN, false, row, col, styleIndex);
+
+        switch (cellType) {
+            default:
+                setCellType(CELL_TYPE_BOOLEAN, false, row, col, styleIndex);
+            case CELL_TYPE_ERROR:
+                (( BoolErrRecord ) record).setValue(value);
+                break;
+            case CELL_TYPE_FORMULA:
+                ((FormulaRecordAggregate)record).getFormulaRecord().setCachedResultBoolean(value);
+                break;
         }
-        (( BoolErrRecord ) record).setValue(value);
     }
 
     /**
      * set a error value for the cell
      *
-     * @param value the error value to set this cell to.  For formulas we'll set the
-     *        precalculated value ??? IS THIS RIGHT??? , for errors we'll set
+     * @param errorCode the error value to set this cell to.  For formulas we'll set the
+     *        precalculated value , for errors we'll set
      *        its value. For other types we will change the cell to an error
      *        cell and set its value.
      */
-
-    public void setCellErrorValue(byte value)
-    {
+    public void setCellErrorValue(byte errorCode) {
         int row=record.getRow();
         short col=record.getColumn();
         short styleIndex=record.getXFIndex();
-        if (cellType != CELL_TYPE_ERROR) {
-            setCellType(CELL_TYPE_ERROR, false, row, col, styleIndex);
+        switch (cellType) {
+            default:
+                setCellType(CELL_TYPE_ERROR, false, row, col, styleIndex);
+            case CELL_TYPE_ERROR:
+                (( BoolErrRecord ) record).setValue(errorCode);
+                break;
+            case CELL_TYPE_FORMULA:
+                ((FormulaRecordAggregate)record).getFormulaRecord().setCachedResultErrorCode(errorCode);
+                break;
         }
-        (( BoolErrRecord ) record).setValue(value);
     }
     /**
      * Chooses a new boolean value for the cell when its type is changing.<p/>
-     * 
-     * Usually the caller is calling setCellType() with the intention of calling 
+     *
+     * Usually the caller is calling setCellType() with the intention of calling
      * setCellValue(boolean) straight afterwards.  This method only exists to give
      * the cell a somewhat reasonable value until the setCellValue() call (if at all).
      * TODO - perhaps a method like setCellTypeAndValue(int, Object) should be introduced to avoid this
      */
     private boolean convertCellValueToBoolean() {
-        
+
         switch (cellType) {
             case CELL_TYPE_BOOLEAN:
                 return (( BoolErrRecord ) record).getBooleanValue();
@@ -796,11 +787,11 @@ public class HSSFCell implements Cell {
 
             // All other cases convert to false
             // These choices are not well justified.
-            case CELL_TYPE_FORMULA:  
+            case CELL_TYPE_FORMULA:
                 // should really evaluate, but HSSFCell can't call HSSFFormulaEvaluator
             case CELL_TYPE_ERROR:
             case CELL_TYPE_BLANK:
-                return false;  
+                return false;
         }
         throw new RuntimeException("Unexpected cell type (" + cellType + ")");
     }
@@ -809,38 +800,39 @@ public class HSSFCell implements Cell {
      * get the value of the cell as a boolean.  For strings, numbers, and errors, we throw an exception.
      * For blank cells we return a false.
      */
+    public boolean getBooleanCellValue() {
 
-    public boolean getBooleanCellValue()
-    {
-        if (cellType == CELL_TYPE_BOOLEAN)
-        {
-            return (( BoolErrRecord ) record).getBooleanValue();
-        }
-        if (cellType == CELL_TYPE_BLANK)
-        {
-            return false;
+        switch(cellType) {
+            case CELL_TYPE_BLANK:
+                return false;
+            case CELL_TYPE_BOOLEAN:
+                return (( BoolErrRecord ) record).getBooleanValue();
+            default:
+                throw typeMismatch(CELL_TYPE_BOOLEAN, cellType, false);
+            case CELL_TYPE_FORMULA:
+                break;
         }
-        throw new NumberFormatException(
-            "You cannot get a boolean value from a non-boolean cell");
+        FormulaRecord fr = ((FormulaRecordAggregate)record).getFormulaRecord();
+        checkFormulaCachedValueType(CELL_TYPE_BOOLEAN, fr);
+        return fr.getCachedBooleanValue();
     }
 
     /**
      * get the value of the cell as an error code.  For strings, numbers, and booleans, we throw an exception.
      * For blank cells we return a 0.
      */
-
-    public byte getErrorCellValue()
-    {
-        if (cellType == CELL_TYPE_ERROR)
-        {
-            return (( BoolErrRecord ) record).getErrorValue();
-        }
-        if (cellType == CELL_TYPE_BLANK)
-        {
-            return ( byte ) 0;
+    public byte getErrorCellValue() {
+        switch(cellType) {
+            case CELL_TYPE_ERROR:
+                return (( BoolErrRecord ) record).getErrorValue();
+            default:
+                throw typeMismatch(CELL_TYPE_ERROR, cellType, false);
+            case CELL_TYPE_FORMULA:
+                break;
         }
-        throw new NumberFormatException(
-            "You cannot get an error value from a non-error cell");
+        FormulaRecord fr = ((FormulaRecordAggregate)record).getFormulaRecord();
+        checkFormulaCachedValueType(CELL_TYPE_ERROR, fr);
+        return (byte) fr.getCachedErrorValue();
     }
 
     /**
@@ -897,7 +889,7 @@ public class HSSFCell implements Cell {
           throw new RuntimeException("You cannot reference columns with an index of less then 0.");
       }
     }
-    
+
     /**
      * Sets this cell as the active cell for the worksheet
      */
@@ -905,45 +897,45 @@ public class HSSFCell implements Cell {
     {
         int row=record.getRow();
         short col=record.getColumn();
-        this.sheet.setActiveCellRow(row);
-        this.sheet.setActiveCellCol(col);
+        this.sheet.getSheet().setActiveCellRow(row);
+        this.sheet.getSheet().setActiveCellCol(col);
     }
-    
+
     /**
      * Returns a string representation of the cell
-     * 
-     * This method returns a simple representation, 
+     *
+     * This method returns a simple representation,
      * anthing more complex should be in user code, with
-     * knowledge of the semantics of the sheet being processed. 
-     * 
-     * Formula cells return the formula string, 
-     * rather than the formula result. 
+     * knowledge of the semantics of the sheet being processed.
+     *
+     * Formula cells return the formula string,
+     * rather than the formula result.
      * Dates are displayed in dd-MMM-yyyy format
      * Errors are displayed as #ERR&lt;errIdx&gt;
      */
     public String toString() {
-       switch (getCellType()) {
-               case CELL_TYPE_BLANK:
-                       return "";
-               case CELL_TYPE_BOOLEAN:
-                       return getBooleanCellValue()?"TRUE":"FALSE";
-               case CELL_TYPE_ERROR:
-                       return ErrorEval.getText((( BoolErrRecord ) record).getErrorValue());
-               case CELL_TYPE_FORMULA:
-                       return getCellFormula();
-               case CELL_TYPE_NUMERIC:
-                       //TODO apply the dataformat for this cell
-                       if (HSSFDateUtil.isCellDateFormatted(this)) {
-                               DateFormat sdf = new SimpleDateFormat("dd-MMM-yyyy");
-                               return sdf.format(getDateCellValue());
-                       } else {
-                               return  getNumericCellValue() + "";
-                       }
-               case CELL_TYPE_STRING:
-                       return getStringCellValue();
-               default:
-                       return "Unknown Cell Type: " + getCellType();
-       }
+        switch (getCellType()) {
+            case CELL_TYPE_BLANK:
+                return "";
+            case CELL_TYPE_BOOLEAN:
+                return getBooleanCellValue()?"TRUE":"FALSE";
+            case CELL_TYPE_ERROR:
+                return ErrorEval.getText((( BoolErrRecord ) record).getErrorValue());
+            case CELL_TYPE_FORMULA:
+                return getCellFormula();
+            case CELL_TYPE_NUMERIC:
+                //TODO apply the dataformat for this cell
+                if (HSSFDateUtil.isCellDateFormatted(this)) {
+                    DateFormat sdf = new SimpleDateFormat("dd-MMM-yyyy");
+                    return sdf.format(getDateCellValue());
+                } else {
+                    return  getNumericCellValue() + "";
+                }
+            case CELL_TYPE_STRING:
+                return getStringCellValue();
+            default:
+                return "Unknown Cell Type: " + getCellType();
+        }
     }
 
     /**
@@ -954,14 +946,14 @@ public class HSSFCell implements Cell {
      * @param comment comment associated with this cell
      */
     public void setCellComment(Comment comment){
-       if(comment == null) {
-               removeCellComment();
-               return;
-       }
-
-        this.comment = (HSSFComment) comment;
-        this.comment.setRow((short)record.getRow());
-        this.comment.setColumn(record.getColumn());
+        if(comment == null) {
+            removeCellComment();
+            return;
+        }
+
+        comment.setRow((short)record.getRow());
+        comment.setColumn(record.getColumn());
+        this.comment = (HSSFComment)comment;
     }
 
     /**
@@ -971,11 +963,11 @@ public class HSSFCell implements Cell {
      */
      public HSSFComment getCellComment(){
         if (comment == null) {
-            comment = findCellComment(sheet, record.getRow(), record.getColumn());
+            comment = findCellComment(sheet.getSheet(), record.getRow(), record.getColumn());
         }
         return comment;
     }
-     
+
     /**
      * Removes the comment for this cell, if
      *  there is one.
@@ -983,40 +975,41 @@ public class HSSFCell implements Cell {
      *  all comments after performing this action!
      */
     public void removeCellComment() {
-       HSSFComment comment = findCellComment(sheet, record.getRow(), record.getColumn());
-               this.comment = null;
-       
-       if(comment == null) {
-               // Nothing to do
-               return;
-       }
-       
-       // Zap the underlying NoteRecord
-       sheet.getRecords().remove(comment.getNoteRecord());
-       
-       // If we have a TextObjectRecord, is should
-       //  be proceeed by:
-       // MSODRAWING with container
-       // OBJ
-       // MSODRAWING with EscherTextboxRecord
-       if(comment.getTextObjectRecord() != null) {
-               TextObjectRecord txo = comment.getTextObjectRecord();
-               int txoAt = sheet.getRecords().indexOf(txo);
-               
-               if(sheet.getRecords().get(txoAt-3) instanceof DrawingRecord &&
-                       sheet.getRecords().get(txoAt-2) instanceof ObjRecord &&
-                       sheet.getRecords().get(txoAt-1) instanceof DrawingRecord) {
-                       // Zap these, in reverse order
-                       sheet.getRecords().remove(txoAt-1);
-                       sheet.getRecords().remove(txoAt-2);
-                       sheet.getRecords().remove(txoAt-3);
-               } else {
-                       throw new IllegalStateException("Found the wrong records before the TextObjectRecord, can't remove comment");
-               }
-               
-               // Now remove the text record
-               sheet.getRecords().remove(txo);
-       }
+        HSSFComment comment = findCellComment(sheet.getSheet(), record.getRow(), record.getColumn());
+        this.comment = null;
+
+        if(comment == null) {
+            // Nothing to do
+            return;
+        }
+
+        // Zap the underlying NoteRecord
+        List sheetRecords = sheet.getSheet().getRecords();
+        sheetRecords.remove(comment.getNoteRecord());
+
+        // If we have a TextObjectRecord, is should
+        //  be proceeed by:
+        // MSODRAWING with container
+        // OBJ
+        // MSODRAWING with EscherTextboxRecord
+        if(comment.getTextObjectRecord() != null) {
+            TextObjectRecord txo = comment.getTextObjectRecord();
+            int txoAt = sheetRecords.indexOf(txo);
+
+            if(sheetRecords.get(txoAt-3) instanceof DrawingRecord &&
+                sheetRecords.get(txoAt-2) instanceof ObjRecord &&
+                sheetRecords.get(txoAt-1) instanceof DrawingRecord) {
+                // Zap these, in reverse order
+                sheetRecords.remove(txoAt-1);
+                sheetRecords.remove(txoAt-2);
+                sheetRecords.remove(txoAt-3);
+            } else {
+                throw new IllegalStateException("Found the wrong records before the TextObjectRecord, can't remove comment");
+            }
+
+            // Now remove the text record
+            sheetRecords.remove(txo);
+        }
     }
 
     /**
@@ -1070,7 +1063,7 @@ public class HSSFCell implements Cell {
      * @return hyperlink associated with this cell or null if not found
      */
     public HSSFHyperlink getHyperlink(){
-        for (Iterator it = sheet.getRecords().iterator(); it.hasNext(); ) {
+        for (Iterator it = sheet.getSheet().getRecords().iterator(); it.hasNext(); ) {
             RecordBase rec = (RecordBase) it.next();
             if (rec instanceof HyperlinkRecord){
                 HyperlinkRecord link = (HyperlinkRecord)rec;
@@ -1108,7 +1101,19 @@ public class HSSFCell implements Cell {
                 break;
         }
 
-        int eofLoc = sheet.findFirstRecordLocBySid( EOFRecord.sid );
-        sheet.getRecords().add( eofLoc, link.record );
+        int eofLoc = sheet.getSheet().findFirstRecordLocBySid( EOFRecord.sid );
+        sheet.getSheet().getRecords().add( eofLoc, link.record );
+    }
+    /**
+     * Only valid for formula cells
+     * @return one of ({@link #CELL_TYPE_NUMERIC}, {@link #CELL_TYPE_STRING},
+     *     {@link #CELL_TYPE_BOOLEAN}, {@link #CELL_TYPE_ERROR}) depending
+     * on the cached value of the formula
+     */
+    public int getCachedFormulaResultType() {
+        if (this.cellType != CELL_TYPE_FORMULA) {
+            throw new IllegalStateException("Only formula cells have cached results");
+        }
+        return ((FormulaRecordAggregate)record).getFormulaRecord().getCachedResultType();
     }
 }
index 4fb31606eaf9c8a51dbf1dd87357d7a959c3ce28..64ec2fd211f45a508b3ae257f43606a3990555a2 100644 (file)
@@ -31,6 +31,9 @@ public class HSSFFormulaEvaluator extends FormulaEvaluator {
     public HSSFFormulaEvaluator(HSSFSheet sheet, HSSFWorkbook workbook) {
        super(sheet, workbook);
     }
+    public HSSFFormulaEvaluator(HSSFWorkbook workbook) {
+       super(workbook);
+    }
 
     /**
      * Returns an underlying FormulaParser, for the specified
index 4e351e6ade282073ec3ae953ab7a4d66b469e4ab..641c07467cc140edebb4226aeb708abde5f1e9da 100644 (file)
@@ -20,7 +20,6 @@ package org.apache.poi.hssf.usermodel;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
 
-import org.apache.poi.hssf.model.Sheet;
 import org.apache.poi.hssf.record.CellValueRecordInterface;
 import org.apache.poi.hssf.record.RowRecord;
 import org.apache.poi.ss.usermodel.Cell;
@@ -55,7 +54,7 @@ public final class HSSFRow implements Comparable, Row {
     /**
      * reference to containing Sheet
      */
-    private Sheet sheet;
+    private HSSFSheet sheet;
 
     /**
      * Creates new HSSFRow from scratch. Only HSSFSheet should do this.
@@ -65,7 +64,7 @@ public final class HSSFRow implements Comparable, Row {
      * @param rowNum the row number of this row (0 based)
      * @see org.apache.poi.hssf.usermodel.HSSFSheet#createRow(int)
      */
-    HSSFRow(HSSFWorkbook book, Sheet sheet, int rowNum)
+    HSSFRow(HSSFWorkbook book, HSSFSheet sheet, int rowNum)
     {
         this.rowNum = rowNum;
         this.book = book;
@@ -84,7 +83,7 @@ public final class HSSFRow implements Comparable, Row {
      * @param record the low level api object this row should represent
      * @see org.apache.poi.hssf.usermodel.HSSFSheet#createRow(int)
      */
-    HSSFRow(HSSFWorkbook book, Sheet sheet, RowRecord record)
+    HSSFRow(HSSFWorkbook book, HSSFSheet sheet, RowRecord record)
     {
         this.book = book;
         this.sheet = sheet;
@@ -139,7 +138,7 @@ public final class HSSFRow implements Comparable, Row {
 
         HSSFCell cell = new HSSFCell(book, sheet, getRowNum(), shortCellNum, type);
         addCell(cell);
-        sheet.addValueRecord(getRowNum(), cell.getCellValueRecord());
+        sheet.getSheet().addValueRecord(getRowNum(), cell.getCellValueRecord());
         return cell;
     }
 
@@ -166,7 +165,7 @@ public final class HSSFRow implements Comparable, Row {
         
         if(alsoRemoveRecords) {
             CellValueRecordInterface cval = cell.getCellValueRecord();
-            sheet.removeValueRecord(getRowNum(), cval);
+            sheet.getSheet().removeValueRecord(getRowNum(), cval);
         }
         
         if (cell.getCellNum()+1 == row.getLastCol()) {
@@ -465,7 +464,7 @@ public final class HSSFRow implements Comparable, Row {
 
         //The low-order 15 bits contain the row height.
         //The 0x8000 bit indicates that the row is standard height (optional) 
-        if ((height & 0x8000) != 0) height = sheet.getDefaultRowHeight();
+        if ((height & 0x8000) != 0) height = sheet.getSheet().getDefaultRowHeight();
         else height &= 0x7FFF;
 
         return height;
index 6670b20414c7651064f38231a5d118544042743b..4d6a1f72539f884e966ae4481d578eca9a3fce65 100644 (file)
@@ -207,7 +207,7 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
      */
     public HSSFRow createRow(int rownum)
     {
-        HSSFRow row = new HSSFRow(workbook, sheet, rownum);
+        HSSFRow row = new HSSFRow(workbook, this, rownum);
 
         addRow(row, true);
         return row;
@@ -222,7 +222,7 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
 
     private HSSFRow createRowFromRecord(RowRecord row)
     {
-        HSSFRow hrow = new HSSFRow(workbook, sheet, row);
+        HSSFRow hrow = new HSSFRow(workbook, this, row);
 
         addRow(hrow, false);
         return hrow;
@@ -390,7 +390,7 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
         
         for(int index=0; index<records.size(); index++) {
            if(records.get(index) instanceof DVRecord) {
-                  dvRecords.add(records.get(index));
+               dvRecords.add(records.get(index));
            }
         }
         return dvRecords;
@@ -1301,9 +1301,7 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
                     // If any references were changed, then
                     //  re-create the formula string
                     if(changed) {
-                        c.setCellFormula(
-                             FormulaParser.toFormulaString(workbook, ptgs)
-                        );
+                        c.setFormulaOnly(ptgs);
                     }
                 }
             }
@@ -1601,15 +1599,33 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
         return patriarch;
     }
 
+    /**
+     * @deprecated (Sep 2008) use {@link #setColumnGroupCollapsed(int, boolean)}
+     */
+    public void setColumnGroupCollapsed(short columnNumber, boolean collapsed) {
+        setColumnGroupCollapsed(columnNumber & 0xFFFF, collapsed);
+    }
+    /**
+     * @deprecated (Sep 2008) use {@link #groupColumn(int, int)}
+     */
+    public void groupColumn(short fromColumn, short toColumn) {
+        groupColumn(fromColumn & 0xFFFF, toColumn & 0xFFFF);
+    }
+    /**
+     * @deprecated (Sep 2008) use {@link #ungroupColumn(int, int)}
+     */
+    public void ungroupColumn(short fromColumn, short toColumn) {
+        ungroupColumn(fromColumn & 0xFFFF, toColumn & 0xFFFF);
+    }
+
     /**
      * Expands or collapses a column group.
      *
      * @param columnNumber      One of the columns in the group.
      * @param collapsed         true = collapse group, false = expand group.
      */
-    public void setColumnGroupCollapsed( short columnNumber, boolean collapsed )
-    {
-        sheet.setColumnGroupCollapsed( columnNumber, collapsed );
+    public void setColumnGroupCollapsed(int columnNumber, boolean collapsed) {
+        sheet.setColumnGroupCollapsed(columnNumber, collapsed);
     }
 
     /**
@@ -1618,14 +1634,12 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
      * @param fromColumn        beginning of the column range.
      * @param toColumn          end of the column range.
      */
-    public void groupColumn(short fromColumn, short toColumn)
-    {
-        sheet.groupColumnRange( fromColumn, toColumn, true );
+    public void groupColumn(int fromColumn, int toColumn) {
+        sheet.groupColumnRange(fromColumn, toColumn, true);
     }
 
-    public void ungroupColumn( short fromColumn, short toColumn )
-    {
-        sheet.groupColumnRange( fromColumn, toColumn, false );
+    public void ungroupColumn(int fromColumn, int toColumn) {
+        sheet.groupColumnRange(fromColumn, toColumn, false);
     }
 
     public void groupRow(int fromRow, int toRow)
index 54962e7f20ecd6dd0bc4f3c0a8ab9cf4f96cb8df..1d24ad39215ccd73f9a29585141ad78899feacce 100644 (file)
@@ -661,6 +661,16 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
         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
diff --git a/src/java/org/apache/poi/hssf/usermodel/LazyAreaEval.java b/src/java/org/apache/poi/hssf/usermodel/LazyAreaEval.java
new file mode 100644 (file)
index 0000000..0a195c8
--- /dev/null
@@ -0,0 +1,29 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hssf.usermodel;
+import org.apache.poi.hssf.record.formula.AreaI;
+
+/**
+ *
+ * @author Josh Micich 
+ */
+final class LazyAreaEval extends org.apache.poi.ss.usermodel.LazyAreaEval {
+       public LazyAreaEval(AreaI ptg, HSSFSheet sheet, HSSFFormulaEvaluator evaluator) {
+               super(ptg, sheet, evaluator);
+       }
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/LazyRefEval.java b/src/java/org/apache/poi/hssf/usermodel/LazyRefEval.java
new file mode 100644 (file)
index 0000000..3b591bb
--- /dev/null
@@ -0,0 +1,33 @@
+/* ====================================================================\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
+import org.apache.poi.hssf.record.formula.Ref3DPtg;\r
+import org.apache.poi.hssf.record.formula.RefPtg;\r
+\r
+/**\r
+*\r
+* @author Josh Micich \r
+*/\r
+final class LazyRefEval extends org.apache.poi.ss.usermodel.LazyRefEval {\r
+       public LazyRefEval(RefPtg ptg, HSSFSheet sheet, HSSFFormulaEvaluator evaluator) {\r
+               super(ptg, sheet, evaluator);\r
+       }\r
+       public LazyRefEval(Ref3DPtg ptg, HSSFSheet sheet, HSSFFormulaEvaluator evaluator) {\r
+               super(ptg, sheet, evaluator);\r
+       }\r
+}\r
index 9cf3aa982f9588ead8639f178fdef3e0dcc1f9e3..30c6d31b1e8bea7348442af57421d1c6929e06d7 100755 (executable)
@@ -20,12 +20,9 @@ package org.apache.poi.ss.usermodel;
 import java.util.ArrayList;
 import java.util.List;
 
-import org.apache.poi.ss.usermodel.Sheet;
-import org.apache.poi.ss.usermodel.Workbook;
-
 /**
  * Instances of this class keep track of multiple dependent cell evaluations due
- * to recursive calls to <tt>HSSFFormulaEvaluator.internalEvaluate()</tt>.
+ * to recursive calls to <tt>FormulaEvaluator.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.
@@ -40,19 +37,19 @@ final class EvaluationCycleDetector {
        private static final class CellEvaluationFrame {
 
                private final Workbook _workbook;
-               private final Sheet _sheet;
+               private final int _sheetIndex;
                private final int _srcRowNum;
                private final int _srcColNum;
 
-               public CellEvaluationFrame(Workbook workbook, Sheet sheet, int srcRowNum, int srcColNum) {
+               public CellEvaluationFrame(Workbook workbook, int sheetIndex, int srcRowNum, int srcColNum) {
                        if (workbook == null) {
                                throw new IllegalArgumentException("workbook must not be null");
                        }
-                       if (sheet == null) {
-                               throw new IllegalArgumentException("sheet must not be null");
+                       if (sheetIndex < 0) {
+                               throw new IllegalArgumentException("sheetIndex must not be negative");
                        }
                        _workbook = workbook;
-                       _sheet = sheet;
+                       _sheetIndex = sheetIndex;
                        _srcRowNum = srcRowNum;
                        _srcColNum = srcColNum;
                }
@@ -62,7 +59,7 @@ final class EvaluationCycleDetector {
                        if (_workbook != other._workbook) {
                                return false;
                        }
-                       if (_sheet != other._sheet) {
+                       if (_sheetIndex != other._sheetIndex) {
                                return false;
                        }
                        if (_srcRowNum != other._srcRowNum) {
@@ -78,7 +75,7 @@ final class EvaluationCycleDetector {
                 * @return human readable string for debug purposes
                 */
                public String formatAsString() {
-                       return "R=" + _srcRowNum + " C=" + _srcColNum + " ShIx=" + _workbook.getSheetIndex(_sheet);
+                       return "R=" + _srcRowNum + " C=" + _srcColNum + " ShIx=" + _sheetIndex;
                }
 
                public String toString() {
@@ -111,8 +108,8 @@ final class EvaluationCycleDetector {
         * @return <code>true</code> if the specified cell has not been visited yet in the current 
         * evaluation. <code>false</code> if the specified cell is already being evaluated.
         */
-       public boolean startEvaluate(Workbook workbook, Sheet sheet, int srcRowNum, int srcColNum) {
-               CellEvaluationFrame cef = new CellEvaluationFrame(workbook, sheet, srcRowNum, srcColNum);
+       public boolean startEvaluate(Workbook workbook, int sheetIndex, int srcRowNum, int srcColNum) {
+               CellEvaluationFrame cef = new CellEvaluationFrame(workbook, sheetIndex, srcRowNum, srcColNum);
                if (_evaluationFrames.contains(cef)) {
                        return false;
                }
@@ -132,7 +129,7 @@ final class EvaluationCycleDetector {
         * required. However, they have been included to assert correct behaviour,
         * and form more meaningful error messages.
         */
-       public void endEvaluate(Workbook workbook, Sheet sheet, int srcRowNum, int srcColNum) {
+       public void endEvaluate(Workbook 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");
@@ -140,7 +137,7 @@ final class EvaluationCycleDetector {
 
                nFrames--;
                CellEvaluationFrame cefExpected = (CellEvaluationFrame) _evaluationFrames.get(nFrames);
-               CellEvaluationFrame cefActual = new CellEvaluationFrame(workbook, sheet, srcRowNum, srcColNum);
+               CellEvaluationFrame cefActual = new CellEvaluationFrame(workbook, sheetIndex, srcRowNum, srcColNum);
                if (!cefActual.equals(cefExpected)) {
                        throw new RuntimeException("Wrong cell specified. "
                                        + "Corresponding startEvaluate() call was for cell {"
index 30e78c33e7fd44b54b0e31f82efc4ed07cf5a251..0f1b7bc47ca66a5058055a8190873b5a9b66cf1a 100644 (file)
@@ -20,9 +20,6 @@ package org.apache.poi.ss.usermodel;
 import java.util.Iterator;
 import java.util.Stack;
 
-import org.apache.poi.ss.util.AreaReference;
-import org.apache.poi.ss.util.CellReference;
-
 import org.apache.poi.hssf.model.FormulaParser;
 import org.apache.poi.hssf.record.NameRecord;
 import org.apache.poi.hssf.record.formula.Area3DPtg;
@@ -49,8 +46,6 @@ 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.LazyAreaEval;
-import org.apache.poi.hssf.record.formula.eval.LazyRefEval;
 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;
@@ -58,6 +53,7 @@ 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/>
@@ -70,29 +66,37 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
  * @author Josh Micich
  */
 public class FormulaEvaluator {
-       /**
-        * used to track the number of evaluations
-        */
+
+    /**
+     * used to track the number of evaluations
+     */
     private static final class Counter {
         public int value;
+        public int depth;
         public Counter() {
             value = 0;
         }
     }
 
-    protected Sheet _sheet;
-    protected Workbook _workbook;
+    protected final Workbook _workbook;
     private final EvaluationCache _cache;
 
     private Counter _evaluationCounter;
 
-
+    /**
+     * @deprecated (Sep 2008) Sheet parameter is ignored
+     */
     public FormulaEvaluator(Sheet sheet, Workbook workbook) {
-        this(sheet, workbook, new EvaluationCache(), new Counter());
+        this(workbook);
+        if (false) {
+            sheet.toString(); // suppress unused parameter compiler warning
+        }
+    }
+    public FormulaEvaluator(Workbook workbook) {
+        this(workbook, new EvaluationCache(), new Counter());
     }
-    
-    private FormulaEvaluator(Sheet sheet, Workbook workbook, EvaluationCache cache, Counter evaluationCounter) {
-        _sheet = sheet;
+
+    private FormulaEvaluator(Workbook workbook, EvaluationCache cache, Counter evaluationCounter) {
         _workbook = workbook;
         _cache = cache;
         _evaluationCounter = evaluationCounter;
@@ -140,7 +144,6 @@ public class FormulaEvaluator {
         _cache.clear();
     }
 
-
     /**
      * If cell contains a formula, the formula is evaluated and returned,
      * else the CellValue simply copies the appropriate cell value from
@@ -150,34 +153,23 @@ public class FormulaEvaluator {
      * @param cell
      */
     public CellValue evaluate(Cell cell) {
-        CellValue retval = null;
-        if (cell != null) {
-            switch (cell.getCellType()) {
-            case Cell.CELL_TYPE_BLANK:
-                retval = new CellValue(Cell.CELL_TYPE_BLANK, _workbook.getCreationHelper());
-                break;
+        if (cell == null) {
+            return null;
+        }
+        
+        switch (cell.getCellType()) {
             case Cell.CELL_TYPE_BOOLEAN:
-                retval = new CellValue(Cell.CELL_TYPE_BOOLEAN, _workbook.getCreationHelper());
-                retval.setBooleanValue(cell.getBooleanCellValue());
-                break;
+                return CellValue.valueOf(cell.getBooleanCellValue());
             case Cell.CELL_TYPE_ERROR:
-                retval = new CellValue(Cell.CELL_TYPE_ERROR, _workbook.getCreationHelper());
-                retval.setErrorValue(cell.getErrorCellValue());
-                break;
+                return CellValue.getError(cell.getErrorCellValue());
             case Cell.CELL_TYPE_FORMULA:
-                retval = getCellValueForEval(internalEvaluate(cell, _sheet), _workbook.getCreationHelper());
-                break;
+                return evaluateFormulaCellValue(cell);
             case Cell.CELL_TYPE_NUMERIC:
-                retval = new CellValue(Cell.CELL_TYPE_NUMERIC, _workbook.getCreationHelper());
-                retval.setNumberValue(cell.getNumericCellValue());
-                break;
+                return new CellValue(cell.getNumericCellValue(), _workbook.getCreationHelper());
             case Cell.CELL_TYPE_STRING:
-                retval = new CellValue(Cell.CELL_TYPE_STRING, _workbook.getCreationHelper());
-                retval.setRichTextStringValue(cell.getRichStringCellValue());
-                break;
-            }
+                return new CellValue(cell.getRichStringCellValue().getString(), _workbook.getCreationHelper());
         }
-        return retval;
+        throw new IllegalStateException("Bad cell type (" + cell.getCellType() + ")");
     }
 
 
@@ -200,32 +192,13 @@ public class FormulaEvaluator {
      * @return The type of the formula result (the cell's type remains as Cell.CELL_TYPE_FORMULA however)
      */
     public int evaluateFormulaCell(Cell cell) {
-        if (cell != null) {
-            switch (cell.getCellType()) {
-            case Cell.CELL_TYPE_FORMULA:
-                CellValue cv = getCellValueForEval(internalEvaluate(cell, _sheet), _workbook.getCreationHelper());
-                switch (cv.getCellType()) {
-                case Cell.CELL_TYPE_BOOLEAN:
-                    cell.setCellValue(cv.getBooleanValue());
-                    break;
-                case Cell.CELL_TYPE_ERROR:
-                    cell.setCellValue(cv.getErrorValue());
-                    break;
-                case Cell.CELL_TYPE_NUMERIC:
-                    cell.setCellValue(cv.getNumberValue());
-                    break;
-                case Cell.CELL_TYPE_STRING:
-                    cell.setCellValue(cv.getRichTextStringValue());
-                    break;
-                case Cell.CELL_TYPE_BLANK:
-                    break;
-                case Cell.CELL_TYPE_FORMULA: // this will never happen, we have already evaluated the formula
-                    break;
-                }
-                return cv.getCellType();
-            }
+        if (cell == null || cell.getCellType() != Cell.CELL_TYPE_FORMULA) {
+            return -1;
         }
-        return -1;
+        CellValue cv = evaluateFormulaCellValue(cell);
+        // cell remains a formula cell, but the cached value is changed
+        setCellValue(cell, cv);
+        return cv.getCellType();
     }
 
     /**
@@ -245,35 +218,56 @@ public class FormulaEvaluator {
      * @param cell
      */
     public Cell evaluateInCell(Cell cell) {
-        if (cell != null) {
-            switch (cell.getCellType()) {
-            case Cell.CELL_TYPE_FORMULA:
-                CellValue cv = getCellValueForEval(internalEvaluate(cell, _sheet), _workbook.getCreationHelper());
-                switch (cv.getCellType()) {
-                case Cell.CELL_TYPE_BOOLEAN:
-                    cell.setCellType(Cell.CELL_TYPE_BOOLEAN);
-                    cell.setCellValue(cv.getBooleanValue());
-                    break;
-                case Cell.CELL_TYPE_ERROR:
-                    cell.setCellErrorValue(cv.getErrorValue());
-                    break;
-                case Cell.CELL_TYPE_NUMERIC:
-                    cell.setCellType(Cell.CELL_TYPE_NUMERIC);
-                    cell.setCellValue(cv.getNumberValue());
-                    break;
-                case Cell.CELL_TYPE_STRING:
-                    cell.setCellType(Cell.CELL_TYPE_STRING);
-                    cell.setCellValue(cv.getRichTextStringValue());
-                    break;
-                case Cell.CELL_TYPE_BLANK:
-                    break;
-                case Cell.CELL_TYPE_FORMULA: // this will never happen, we have already evaluated the formula
-                    break;
-                }
-            }
+        if (cell == null) {
+            return null;
+        }
+        if (cell.getCellType() == Cell.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(Cell cell, CellValue cv) {
+        int cellType = cv.getCellType();
+        switch (cellType) {
+            case Cell.CELL_TYPE_BOOLEAN:
+            case Cell.CELL_TYPE_ERROR:
+            case Cell.CELL_TYPE_NUMERIC:
+            case Cell.CELL_TYPE_STRING:
+                cell.setCellType(cellType);
+                return;
+            case Cell.CELL_TYPE_BLANK:
+                // never happens - blanks eventually get translated to zero
+            case Cell.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(Cell cell, CellValue cv) {
+        int cellType = cv.getCellType();
+        switch (cellType) {
+            case Cell.CELL_TYPE_BOOLEAN:
+                cell.setCellValue(cv.getBooleanValue());
+                break;
+            case Cell.CELL_TYPE_ERROR:
+                cell.setCellErrorValue(cv.getErrorValue());
+                break;
+            case Cell.CELL_TYPE_NUMERIC:
+                cell.setCellValue(cv.getNumberValue());
+                break;
+            case Cell.CELL_TYPE_STRING:
+                cell.setCellValue(cv.getRichTextStringValue());
+                break;
+            case Cell.CELL_TYPE_BLANK:
+                // never happens - blanks eventually get translated to zero
+            case Cell.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
@@ -287,9 +281,9 @@ public class FormulaEvaluator {
      *  cells, and calling evaluateFormulaCell on each one.
      */
     public static void evaluateAllFormulaCells(Workbook wb) {
+        FormulaEvaluator evaluator = new FormulaEvaluator(wb);
         for(int i=0; i<wb.getNumberOfSheets(); i++) {
             Sheet sheet = wb.getSheetAt(i);
-            FormulaEvaluator evaluator = new FormulaEvaluator(sheet, wb);
 
             for (Iterator rit = sheet.rowIterator(); rit.hasNext();) {
                 Row r = (Row)rit.next();
@@ -303,72 +297,61 @@ public class FormulaEvaluator {
         }
     }
 
-
     /**
      * Returns a CellValue wrapper around the supplied ValueEval instance.
      * @param eval
      */
-    private static CellValue getCellValueForEval(ValueEval eval, CreationHelper cHelper) {
-        CellValue retval = null;
-        if (eval != null) {
-            if (eval instanceof NumberEval) {
-                NumberEval ne = (NumberEval) eval;
-                retval = new CellValue(Cell.CELL_TYPE_NUMERIC, cHelper);
-                retval.setNumberValue(ne.getNumberValue());
-            }
-            else if (eval instanceof BoolEval) {
-                BoolEval be = (BoolEval) eval;
-                retval = new CellValue(Cell.CELL_TYPE_BOOLEAN, cHelper);
-                retval.setBooleanValue(be.getBooleanValue());
-            }
-            else if (eval instanceof StringEval) {
-                StringEval ne = (StringEval) eval;
-                retval = new CellValue(Cell.CELL_TYPE_STRING, cHelper);
-                retval.setStringValue(ne.getStringValue());
-            }
-            else if (eval instanceof BlankEval) {
-                retval = new CellValue(Cell.CELL_TYPE_BLANK, cHelper);
-            }
-            else if (eval instanceof ErrorEval) {
-                retval = new CellValue(Cell.CELL_TYPE_ERROR, cHelper);
-                retval.setErrorValue((byte)((ErrorEval)eval).getErrorCode());
-//                retval.setRichTextStringValue(new RichTextString("#An error occurred. check cell.getErrorCode()"));
-            }
-            else {
-                retval = new CellValue(Cell.CELL_TYPE_ERROR, cHelper);
-            }
+    private CellValue evaluateFormulaCellValue(Cell cell) {
+        ValueEval eval = internalEvaluate(cell);
+        if (eval instanceof NumberEval) {
+            NumberEval ne = (NumberEval) eval;
+            return new CellValue(ne.getNumberValue(), _workbook.getCreationHelper());
+        }
+        if (eval instanceof BoolEval) {
+            BoolEval be = (BoolEval) eval;
+            return CellValue.valueOf(be.getBooleanValue());
         }
-        return retval;
+        if (eval instanceof StringEval) {
+            StringEval ne = (StringEval) eval;
+            return new CellValue(ne.getStringValue(), _workbook.getCreationHelper());
+        }
+        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(Cell srcCell, Sheet sheet) {
+    private ValueEval internalEvaluate(Cell srcCell) {
         int srcRowNum = srcCell.getRowIndex();
         int srcColNum = srcCell.getCellNum();
 
         ValueEval result;
 
-        int sheetIndex = _workbook.getSheetIndex(sheet);
+        int sheetIndex = _workbook.getSheetIndex(srcCell.getSheet());
         result = _cache.getValue(sheetIndex, srcRowNum, srcColNum);
         if (result != null) {
-           return result;
+            return result;
         }
         _evaluationCounter.value++;
+        _evaluationCounter.depth++;
 
         EvaluationCycleDetector tracker = EvaluationCycleDetectorManager.getTracker();
 
-        if(!tracker.startEvaluate(_workbook, sheet, srcRowNum, srcColNum)) {
+        if(!tracker.startEvaluate(_workbook, sheetIndex, srcRowNum, srcColNum)) {
             return ErrorEval.CIRCULAR_REF_ERROR;
         }
         try {
-            result = evaluateCell(srcRowNum, (short)srcColNum, srcCell.getCellFormula());
+            result = evaluateCell(sheetIndex, srcRowNum, (short)srcColNum, srcCell.getCellFormula());
         } finally {
-            tracker.endEvaluate(_workbook, sheet, srcRowNum, srcColNum);
+            tracker.endEvaluate(_workbook, sheetIndex, srcRowNum, srcColNum);
             _cache.setValue(sheetIndex, srcRowNum, srcColNum, result);
+            _evaluationCounter.depth--;
         }
         if (isDebugLogEnabled()) {
             String sheetName = _workbook.getSheetName(sheetIndex);
@@ -377,7 +360,7 @@ public class FormulaEvaluator {
         }
         return result;
     }
-    private ValueEval evaluateCell(int srcRowNum, short srcColNum, String cellFormulaText) {
+    private ValueEval evaluateCell(int sheetIndex, int srcRowNum, short srcColNum, String cellFormulaText) {
 
         Ptg[] ptgs = FormulaParser.parse(cellFormulaText, _workbook);
 
@@ -392,8 +375,8 @@ public class FormulaEvaluator {
             }
             if (ptg instanceof MemErrPtg) { continue; }
             if (ptg instanceof MissingArgPtg) {
-               // TODO - might need to push BlankEval or MissingArgEval
-               continue;
+                // TODO - might need to push BlankEval or MissingArgEval
+                continue;
             }
             Eval opResult;
             if (ptg instanceof OperationPtg) {
@@ -411,15 +394,15 @@ public class FormulaEvaluator {
                     Eval p = (Eval) stack.pop();
                     ops[j] = p;
                 }
-                logDebug("invoke " + operation + " (nAgs=" + numops + ")");
-                opResult = invokeOperation(operation, ops, srcRowNum, srcColNum, _workbook, _sheet);
+//                logDebug("invoke " + operation + " (nAgs=" + numops + ")");
+                opResult = invokeOperation(operation, ops, _workbook, sheetIndex, srcRowNum, srcColNum);
             } else {
-                opResult = getEvalForPtg(ptg, _sheet);
+                opResult = getEvalForPtg(ptg, sheetIndex);
             }
             if (opResult == null) {
                 throw new RuntimeException("Evaluation result must not be null");
             }
-            logDebug("push " + opResult);
+//            logDebug("push " + opResult);
             stack.push(opResult);
         }
 
@@ -428,7 +411,7 @@ public class FormulaEvaluator {
             throw new IllegalStateException("evaluation stack not empty");
         }
         value = dereferenceValue(value, srcRowNum, srcColNum);
-        if (value instanceof BlankEval) {
+        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
@@ -464,24 +447,21 @@ public class FormulaEvaluator {
         return evaluationResult;
     }
 
-    private static Eval invokeOperation(OperationEval operation, Eval[] ops, int srcRowNum, short srcColNum,
-            Workbook workbook, Sheet sheet) {
+    private static Eval invokeOperation(OperationEval operation, Eval[] ops, 
+            Workbook workbook, int sheetIndex, int srcRowNum, int srcColNum) {
 
         if(operation instanceof FunctionEval) {
             FunctionEval fe = (FunctionEval) operation;
             if(fe.isFreeRefFunction()) {
-                return fe.getFreeRefFunction().evaluate(ops, srcRowNum, srcColNum, workbook, sheet);
+                return fe.getFreeRefFunction().evaluate(ops, workbook, sheetIndex, srcRowNum, srcColNum);
             }
         }
-        return operation.evaluate(ops, srcRowNum, srcColNum);
+        return operation.evaluate(ops, srcRowNum, (short)srcColNum);
     }
 
     private Sheet getOtherSheet(int externSheetIndex) {
         return _workbook.getSheetAt(_workbook.getSheetIndexFromExternSheetIndex(externSheetIndex));
     }
-    private FormulaEvaluator createEvaluatorForAnotherSheet(Sheet sheet) {
-        return new FormulaEvaluator(sheet, _workbook, _cache, _evaluationCounter);
-    }
 
     /**
      * returns an appropriate Eval impl instance for the Ptg. The Ptg must be
@@ -489,7 +469,7 @@ public class FormulaEvaluator {
      * StringPtg, BoolPtg <br/>special Note: OperationPtg subtypes cannot be
      * passed here!
      */
-    private Eval getEvalForPtg(Ptg ptg, Sheet sheet) {
+    private Eval getEvalForPtg(Ptg ptg, int sheetIndex) {
         if (ptg instanceof NamePtg) {
             // named ranges, macro functions
             NamePtg namePtg = (NamePtg) ptg;
@@ -499,41 +479,23 @@ public class FormulaEvaluator {
                 throw new RuntimeException("Bad name index (" + nameIndex
                         + "). Allowed range is (0.." + (numberOfNames-1) + ")");
             }
-            if(_workbook instanceof org.apache.poi.hssf.usermodel.HSSFWorkbook) { 
-                               org.apache.poi.hssf.usermodel.HSSFWorkbook hssfWb = 
-                                       (org.apache.poi.hssf.usermodel.HSSFWorkbook)_workbook;
-                               NameRecord nameRecord = hssfWb.getNameRecord(nameIndex);
+                       if(_workbook instanceof org.apache.poi.hssf.usermodel.HSSFWorkbook) {
+                               NameRecord nameRecord = ((org.apache.poi.hssf.usermodel.HSSFWorkbook)_workbook).getNameRecord(nameIndex);
                                if (nameRecord.isFunctionName()) {
                                        return new NameEval(nameRecord.getNameText());
                                }
                                if (nameRecord.hasFormula()) {
-                                       return evaluateNameFormula(nameRecord.getNameDefinition(), sheet);
+                                       return evaluateNameFormula(nameRecord.getNameDefinition(), sheetIndex);
                                }
-                               throw new RuntimeException("Don't know how to evalate name '" + nameRecord.getNameText() + "'");
-                       } else {
-                               throw new RuntimeException("Don't know how to evaluate name records for XSSF");
+
+                               throw new RuntimeException("Don't now how to evalate name '" + nameRecord.getNameText() + "'");
                        }
+                       throw new RuntimeException("Don't now how to evalate name for XSSFWorkbook");
         }
         if (ptg instanceof NameXPtg) {
             NameXPtg nameXPtg = (NameXPtg) ptg;
             return new NameXEval(nameXPtg.getSheetRefIndex(), nameXPtg.getNameIndex());
         }
-        if (ptg instanceof RefPtg) {
-            return new LazyRefEval(((RefPtg) ptg), sheet, this);
-        }
-        if (ptg instanceof Ref3DPtg) {
-            Ref3DPtg refPtg = (Ref3DPtg) ptg;
-            Sheet xsheet = getOtherSheet(refPtg.getExternSheetIndex());
-            return new LazyRefEval(refPtg, xsheet, createEvaluatorForAnotherSheet(xsheet));
-        }
-        if (ptg instanceof AreaPtg) {
-            return new LazyAreaEval(((AreaPtg) ptg), sheet, this);
-        }
-        if (ptg instanceof Area3DPtg) {
-            Area3DPtg a3dp = (Area3DPtg) ptg;
-            Sheet xsheet = getOtherSheet(a3dp.getExternSheetIndex());
-            return new LazyAreaEval(a3dp, xsheet, createEvaluatorForAnotherSheet(xsheet));
-        }
 
         if (ptg instanceof IntPtg) {
             return new NumberEval(((IntPtg)ptg).getValue());
@@ -550,17 +512,38 @@ public class FormulaEvaluator {
         if (ptg instanceof ErrPtg) {
             return ErrorEval.valueOf(((ErrPtg) ptg).getErrorCode());
         }
+        Sheet 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;
+            Sheet xsheet = getOtherSheet(refPtg.getExternSheetIndex());
+            return new LazyRefEval(refPtg, xsheet, this);
+        }
+        if (ptg instanceof Area3DPtg) {
+            Area3DPtg a3dp = (Area3DPtg) ptg;
+            Sheet xsheet = getOtherSheet(a3dp.getExternSheetIndex());
+            return new LazyAreaEval(a3dp, xsheet, this);
+        }
+
         if (ptg instanceof UnknownPtg) {
-            // TODO - remove 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, Sheet sheet) {
+    private Eval evaluateNameFormula(Ptg[] ptgs, int sheetIndex) {
         if (ptgs.length > 1) {
             throw new RuntimeException("Complex name formulas not supported yet");
         }
-        return getEvalForPtg(ptgs[0], sheet);
+        return getEvalForPtg(ptgs[0], sheetIndex);
     }
 
     /**
@@ -568,11 +551,8 @@ public class FormulaEvaluator {
      * impl instance and return that. Since the cell could be an external
      * reference, we need the sheet that this belongs to.
      * Non existent cells are treated as empty.
-     * @param cell
-     * @param sheet
-     * @param workbook
      */
-    public ValueEval getEvalForCell(Cell cell, Sheet sheet) {
+    public ValueEval getEvalForCell(Cell cell) {
 
         if (cell == null) {
             return BlankEval.INSTANCE;
@@ -583,7 +563,7 @@ public class FormulaEvaluator {
             case Cell.CELL_TYPE_STRING:
                 return new StringEval(cell.getRichStringCellValue().getString());
             case Cell.CELL_TYPE_FORMULA:
-                return internalEvaluate(cell, sheet);
+                return internalEvaluate(cell);
             case Cell.CELL_TYPE_BOOLEAN:
                 return BoolEval.valueOf(cell.getBooleanCellValue());
             case Cell.CELL_TYPE_BLANK:
@@ -600,93 +580,99 @@ public class FormulaEvaluator {
      * or Number or boolean type.
      * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
      */
-    public static class CellValue {
-        private CreationHelper creationHelper;
-        private int cellType;
-        private RichTextString richTextStringValue;
-        private double numberValue;
-        private boolean booleanValue;
-        private byte errorValue;
-
-        /**
-         * CellType should be one of the types defined in Cell
-         * @param cellType
-         */
-        public CellValue(int cellType, CreationHelper creationHelper) {
-            super();
-            this.creationHelper = creationHelper;
-            this.cellType = cellType;
-        }
+    public static final class CellValue {
+        public static final CellValue TRUE = new CellValue(Cell.CELL_TYPE_BOOLEAN, 0.0, true,  null, 0, null);
+        public static final CellValue FALSE= new CellValue(Cell.CELL_TYPE_BOOLEAN, 0.0, false, null, 0, null);
+        
+        private final int _cellType;
+        private final double _numberValue;
+        private final boolean _booleanValue;
+        private final String _textValue;
+        private final int _errorCode;
+               private CreationHelper _creationHelper;
+
+        private CellValue(int cellType, double numberValue, boolean booleanValue, 
+                String textValue, int errorCode, CreationHelper creationHelper) {
+            _cellType = cellType;
+            _numberValue = numberValue;
+            _booleanValue = booleanValue;
+            _textValue = textValue;
+            _errorCode = errorCode;
+                       _creationHelper = creationHelper;
+        }
+        
+        
+        /* package*/ CellValue(double numberValue, CreationHelper creationHelper) {
+            this(Cell.CELL_TYPE_NUMERIC, numberValue, false, null, 0, creationHelper);
+        }
+        /* package*/ static CellValue valueOf(boolean booleanValue) {
+            return booleanValue ? TRUE : FALSE;
+        }
+        /* package*/ CellValue(String stringValue, CreationHelper creationHelper) {
+            this(Cell.CELL_TYPE_STRING, 0.0, false, stringValue, 0, creationHelper);
+        }
+        /* package*/ static CellValue getError(int errorCode) {
+            return new CellValue(Cell.CELL_TYPE_ERROR, 0.0, false, null, errorCode, null);
+        }
+        
+        
         /**
          * @return Returns the booleanValue.
          */
         public boolean getBooleanValue() {
-            return booleanValue;
-        }
-        /**
-         * @param booleanValue The booleanValue to set.
-         */
-        public void setBooleanValue(boolean booleanValue) {
-            this.booleanValue = booleanValue;
+            return _booleanValue;
         }
         /**
          * @return Returns the numberValue.
          */
         public double getNumberValue() {
-            return numberValue;
-        }
-        /**
-         * @param numberValue The numberValue to set.
-         */
-        public void setNumberValue(double numberValue) {
-            this.numberValue = numberValue;
+            return _numberValue;
         }
         /**
-         * @return Returns the stringValue. This method is deprecated, use
-         * getRichTextStringValue instead
-         * @deprecated
+         * @return Returns the stringValue.
          */
         public String getStringValue() {
-            return richTextStringValue.getString();
-        }
-        /**
-         * @param stringValue The stringValue to set. This method is deprecated, use
-         * getRichTextStringValue instead.
-         * @deprecated
-         */
-        public void setStringValue(String stringValue) {
-            this.richTextStringValue = 
-                  creationHelper.createRichTextString(stringValue);
+            return _textValue;
         }
         /**
          * @return Returns the cellType.
          */
         public int getCellType() {
-            return cellType;
+            return _cellType;
         }
         /**
          * @return Returns the errorValue.
          */
         public byte getErrorValue() {
-            return errorValue;
-        }
-        /**
-         * @param errorValue The errorValue to set.
-         */
-        public void setErrorValue(byte errorValue) {
-            this.errorValue = errorValue;
+            return (byte) _errorCode;
         }
         /**
          * @return Returns the richTextStringValue.
+         * @deprecated (Sep 2008) Text formatting is lost during formula evaluation.  Use {@link #getStringValue()}  
          */
         public RichTextString getRichTextStringValue() {
-            return richTextStringValue;
+            return _creationHelper.createRichTextString(_textValue);
         }
-        /**
-         * @param richTextStringValue The richTextStringValue to set.
-         */
-        public void setRichTextStringValue(RichTextString richTextStringValue) {
-            this.richTextStringValue = richTextStringValue;
+        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 Cell.CELL_TYPE_NUMERIC:
+                    return String.valueOf(_numberValue);
+                case Cell.CELL_TYPE_STRING:
+                    return '"' + _textValue + '"';
+                case Cell.CELL_TYPE_BOOLEAN:
+                    return _booleanValue ? "TRUE" : "FALSE";
+                case Cell.CELL_TYPE_ERROR:
+                    return ErrorEval.getText(_errorCode);
+            }
+            return "<error unexpected cell type " + _cellType + ">";
         }
     }
 }
diff --git a/src/java/org/apache/poi/ss/usermodel/LazyAreaEval.java b/src/java/org/apache/poi/ss/usermodel/LazyAreaEval.java
new file mode 100644 (file)
index 0000000..8d9b192
--- /dev/null
@@ -0,0 +1,79 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.ss.usermodel;
+
+import 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 
+ */
+public class LazyAreaEval extends AreaEvalBase {
+
+       private final Sheet _sheet;
+       private FormulaEvaluator _evaluator;
+
+       public LazyAreaEval(AreaI ptg, Sheet sheet, FormulaEvaluator evaluator) {
+               super(ptg);
+               _sheet = sheet;
+               _evaluator = evaluator;
+       }
+
+       public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) { 
+               
+               int rowIx = (relativeRowIndex + getFirstRow() ) & 0xFFFF;
+               int colIx = (relativeColumnIndex + getFirstColumn() ) & 0x00FF;
+               
+               Row row = _sheet.getRow(rowIx);
+               if (row == null) {
+                       return BlankEval.INSTANCE;
+               }
+               Cell 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/usermodel/LazyRefEval.java b/src/java/org/apache/poi/ss/usermodel/LazyRefEval.java
new file mode 100644 (file)
index 0000000..7fb7681
--- /dev/null
@@ -0,0 +1,85 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.ss.usermodel;
+
+import org.apache.poi.hssf.record.formula.AreaI;
+import org.apache.poi.hssf.record.formula.Ref3DPtg;
+import org.apache.poi.hssf.record.formula.RefPtg;
+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.BlankEval;
+import org.apache.poi.hssf.record.formula.eval.RefEvalBase;
+import org.apache.poi.hssf.record.formula.eval.ValueEval;
+import org.apache.poi.hssf.util.CellReference;
+
+/**
+*
+* @author Josh Micich 
+*/
+public class LazyRefEval extends RefEvalBase {
+
+       private final Sheet _sheet;
+       private final FormulaEvaluator _evaluator;
+
+
+       public LazyRefEval(RefPtg ptg, Sheet sheet, FormulaEvaluator evaluator) {
+               super(ptg.getRow(), ptg.getColumn());
+               _sheet = sheet;
+               _evaluator = evaluator;
+       }
+       public LazyRefEval(Ref3DPtg ptg, Sheet sheet, FormulaEvaluator evaluator) {
+               super(ptg.getRow(), ptg.getColumn());
+               _sheet = sheet;
+               _evaluator = evaluator;
+       }
+
+       public ValueEval getInnerValueEval() {
+               int rowIx = getRow();
+               int colIx = getColumn();
+               
+               Row row = _sheet.getRow(rowIx);
+               if (row == null) {
+                       return BlankEval.INSTANCE;
+               }
+               Cell 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(getRow(), getColumn(),
+                               relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);
+
+               return new LazyAreaEval(area, _sheet, _evaluator);
+       }
+       
+       public String toString() {
+               CellReference cr = new CellReference(getRow(), getColumn());
+               StringBuffer sb = new StringBuffer();
+               sb.append(getClass().getName()).append("[");
+               String sheetName = _evaluator.getSheetName(_sheet);
+               sb.append(sheetName);
+               sb.append('!');
+               sb.append(cr.formatAsString());
+               sb.append("]");
+               return sb.toString();
+       }
+}
index ed6f128822d006eb96d2fd949b70becedd7d6c70..c5ebab30a763f2d12fd63a436de406264dd52c40 100644 (file)
@@ -255,8 +255,10 @@ public class HexDump {
         retVal.append('[');
         for(int x = 0; x < value.length; x++)
         {
+            if (x>0) {
+                retVal.append(", ");
+            }
             retVal.append(toHex(value[x]));
-            retVal.append(", ");
         }
         retVal.append(']');
         return retVal.toString();
index cd11ccc6404147eb446ababf8faf5dafb4223a81..91632e8fc5d69468931c766d41f6dc0998fa886b 100644 (file)
@@ -172,9 +172,12 @@ public class HexRead
         return rval;
     }
 
-    static public byte[] readFromString(String data) throws IOException
-    {
-        return readData(new ByteArrayInputStream( data.getBytes() ), -1);
+    static public byte[] readFromString(String data) {
+        try {
+            return readData(new ByteArrayInputStream( data.getBytes() ), -1);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
     }
 
     static private void readToEOL( InputStream stream ) throws IOException
index 51d9874edcc8530a682c505b3f350f9e8d00fbd7..823b6191c8339628d27e599afc7ce72007e2cce1 100644 (file)
@@ -19,6 +19,7 @@ package org.apache.poi.ss.usermodel;
 
 import org.apache.poi.hssf.usermodel.HSSFCellStyle;
 import org.apache.poi.hssf.usermodel.HSSFRichTextString;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
 
 /**
  * This is a JDK 1.4 compatible interface for HSSFCell.
@@ -85,6 +86,7 @@ public interface Cell {
     void setCellErrorValue(byte value);
 
     HSSFCellStyle getCellStyle();
+    HSSFSheet getSheet();
 
     boolean getBooleanCellValue();
     double getNumericCellValue();
index 0b1d55c56847b707ab4301ab221763e321b6294e..1959f9ad5642d8415df41c24eef002a3dda0bdfa 100644 (file)
@@ -17,4 +17,7 @@
 
 package org.apache.poi.ss.usermodel;
 
-public interface Comment {}
+public interface Comment {
+       public void setRow(short row);
+       public void setColumn(short row);
+}
index 0211a9aaa59ee4589b4ae46f4396aaaa5ca527ec..29e6bdd8f824a7a2417c8e7b09b6af646c0592e8 100644 (file)
@@ -106,6 +106,8 @@ public interface Cell {
 
     int getRowIndex();
 
+    Sheet getSheet();
+
     /**
      * set the cells type (numeric, formula or string)
      * @see #CELL_TYPE_NUMERIC
index 7d4dc7bee68f833d339cce27c482a63e47a2daf5..7db08a34a62af8b08d5a880884dcb01e2f33219e 100644 (file)
@@ -37,7 +37,7 @@ public class TestEscherBSERecord extends TestCase
         assertEquals( (short) 0x0001, r.getOptions() );
         assertEquals( EscherBSERecord.BT_JPEG, r.getBlipTypeWin32() );
         assertEquals( EscherBSERecord.BT_JPEG, r.getBlipTypeMacOS() );
-        assertEquals( "[01, 02, 03, 04, 05, 06, 07, 08, 09, 0A, 0B, 0C, 0D, 0E, 0F, 00]", HexDump.toHex( r.getUid() ) );
+        assertEquals( "[01, 02, 03, 04, 05, 06, 07, 08, 09, 0A, 0B, 0C, 0D, 0E, 0F, 00]", HexDump.toHex( r.getUid() ) );
         assertEquals( (short) 1, r.getTag() );
         assertEquals( 2, r.getRef() );
         assertEquals( 3, r.getOffset() );
@@ -57,7 +57,7 @@ public class TestEscherBSERecord extends TestCase
         assertEquals( 44, bytesWritten );
         assertEquals( "[01, 00, 00, 00, 24, 00, 00, 00, 05, 05, 01, 02, 03, 04, " +
                 "05, 06, 07, 08, 09, 0A, 0B, 0C, 0D, 0E, 0F, 00, 01, 00, 00, 00, " +
-                "00, 00, 02, 00, 00, 00, 03, 00, 00, 00, 04, 05, 06, 07]",
+                "00, 00, 02, 00, 00, 00, 03, 00, 00, 00, 04, 05, 06, 07]",
                 HexDump.toHex( data ) );
 
     }
@@ -90,7 +90,7 @@ public class TestEscherBSERecord extends TestCase
                 "  Options: 0x0001" + nl +
                 "  BlipTypeWin32: 5" + nl +
                 "  BlipTypeMacOS: 5" + nl +
-                "  SUID: [01, 02, 03, 04, 05, 06, 07, 08, 09, 0A, 0B, 0C, 0D, 0E, 0F, 00]" + nl +
+                "  SUID: [01, 02, 03, 04, 05, 06, 07, 08, 09, 0A, 0B, 0C, 0D, 0E, 0F, 00]" + nl +
                 "  Tag: 1" + nl +
                 "  Size: 0" + nl +
                 "  Ref: 2" + nl +
index 3f73ccb1c2613567ba1ea1920e33233cb0ad2027..8db7c4be385cfed545bf9bee1349eee6f708f22d 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Licensed to the Apache Software Foundation (ASF) under one or more
    contributor license agreements.  See the NOTICE file distributed with
@@ -15,7 +14,7 @@
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
-        
+
 package org.apache.poi.ddf;
 
 import junit.framework.TestCase;
@@ -36,8 +35,7 @@ public class TestEscherBlipWMFRecord extends TestCase
         data = HexRead.readFromString(dataStr);
     }
 
-    public void testSerialize() throws Exception
-    {
+    public void testSerialize() {
         EscherBlipWMFRecord r = new EscherBlipWMFRecord();
         r.setBoundaryLeft(1);
         r.setBoundaryHeight(2);
@@ -72,14 +70,13 @@ public class TestEscherBlipWMFRecord extends TestCase
                 "05, 00, 00, 00, " +    // field_9_cacheOfSavedSize
                 "08, " +                // field_10_compressionFlag
                 "07, " +                // field_11_filter
-                "01, 02]",            // field_12_data
+                "01, 02]",            // field_12_data
                 HexDump.toHex(buf));
         assertEquals(60, r.getRecordSize() );
 
     }
 
-    public void testFillFields() throws Exception
-    {
+    public void testFillFields() {
         EscherBlipWMFRecord r = new EscherBlipWMFRecord();
         r.fillFields( data, 0, new DefaultEscherRecordFactory());
 
@@ -92,15 +89,14 @@ public class TestEscherBlipWMFRecord extends TestCase
         assertEquals( 6, r.getCacheOfSize() );
         assertEquals( 7, r.getFilter() );
         assertEquals( 8, r.getCompressionFlag() );
-        assertEquals( "[01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01]", HexDump.toHex(r.getSecondaryUID() ) );
+        assertEquals( "[01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01]", HexDump.toHex(r.getSecondaryUID() ) );
         assertEquals( 10, r.getWidth() );
         assertEquals( 11, r.getHeight() );
         assertEquals( (short)5420, r.getOptions() );
-        assertEquals( "[01, 02]", HexDump.toHex( r.getData() ) );
+        assertEquals( "[01, 02]", HexDump.toHex( r.getData() ) );
     }
 
-    public void testToString() throws Exception
-    {
+    public void testToString() {
         EscherBlipWMFRecord r = new EscherBlipWMFRecord();
         r.fillFields( data, 0, new DefaultEscherRecordFactory() );
 
@@ -109,7 +105,7 @@ public class TestEscherBlipWMFRecord extends TestCase
         assertEquals( "org.apache.poi.ddf.EscherBlipWMFRecord:" + nl +
                 "  RecordId: 0xF018" + nl +
                 "  Options: 0x152C" + nl +
-                "  Secondary UID: [01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01]" + nl +
+                "  Secondary UID: [01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01]" + nl +
                 "  CacheOfSize: 6" + nl +
                 "  BoundaryTop: 3" + nl +
                 "  BoundaryLeft: 1" + nl +
@@ -124,5 +120,5 @@ public class TestEscherBlipWMFRecord extends TestCase
                 "00000000 01 02                                           .." + nl
                 , r.toString() );
     }
-
 }
+
index 545351a3fa774350a5a1d2ecfca50e84ae596e99..9cf14e47b439e501053b3db693dfc14883cade08 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Licensed to the Apache Software Foundation (ASF) under one or more
    contributor license agreements.  See the NOTICE file distributed with
@@ -15,7 +14,7 @@
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
-        
+
 package org.apache.poi.ddf;
 
 import junit.framework.TestCase;
@@ -24,8 +23,7 @@ import org.apache.poi.util.HexRead;
 
 public class TestEscherChildAnchorRecord extends TestCase
 {
-    public void testSerialize() throws Exception
-    {
+    public void testSerialize() {
         EscherChildAnchorRecord r = createRecord();
 
         byte[] data = new byte[8 + 16];
@@ -37,7 +35,7 @@ public class TestEscherChildAnchorRecord extends TestCase
                 "01, 00, 00, 00, " +
                 "02, 00, 00, 00, " +
                 "03, 00, 00, 00, " +
-                "04, 00, 00, 00]", HexDump.toHex( data ) );
+                "04, 00, 00, 00]", HexDump.toHex( data ) );
     }
 
     public void testFillFields() throws Exception
@@ -62,8 +60,7 @@ public class TestEscherChildAnchorRecord extends TestCase
         assertEquals( (short) 0x0001, r.getOptions() );
     }
 
-    public void testToString() throws Exception
-    {
+    public void testToString(){
         String nl = System.getProperty( "line.separator" );
 
         String expected = "org.apache.poi.ddf.EscherChildAnchorRecord:" + nl +
@@ -76,7 +73,7 @@ public class TestEscherChildAnchorRecord extends TestCase
         assertEquals( expected, createRecord().toString() );
     }
 
-    private EscherChildAnchorRecord createRecord()
+    private static EscherChildAnchorRecord createRecord()
     {
         EscherChildAnchorRecord r = new EscherChildAnchorRecord();
         r.setRecordId( EscherChildAnchorRecord.RECORD_ID );
@@ -87,5 +84,5 @@ public class TestEscherChildAnchorRecord extends TestCase
         r.setDy2( 4 );
         return r;
     }
-
 }
+
index c058622dbc1dd12587c2f36be139aeadd4ddacf1..3a1e5c2b60a67ecbc3b31e0182aec5bc8aac4d13 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Licensed to the Apache Software Foundation (ASF) under one or more
    contributor license agreements.  See the NOTICE file distributed with
@@ -15,7 +14,7 @@
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
-        
+
 package org.apache.poi.ddf;
 
 import junit.framework.TestCase;
@@ -24,8 +23,7 @@ import org.apache.poi.util.HexRead;
 
 public class TestEscherClientAnchorRecord extends TestCase
 {
-    public void testSerialize() throws Exception
-    {
+    public void testSerialize() {
         EscherClientAnchorRecord r = createRecord();
 
         byte[] data = new byte[8 + 18 + 2];
@@ -37,11 +35,10 @@ public class TestEscherClientAnchorRecord extends TestCase
                 "4D, 00, 37, 00, 21, 00, 58, 00, " +
                 "0B, 00, 2C, 00, 16, 00, 63, 00, " +
                 "42, 00, " +
-                "FF, DD]", HexDump.toHex( data ) );
+                "FF, DD]", HexDump.toHex( data ) );
     }
 
-    public void testFillFields() throws Exception
-    {
+    public void testFillFields() {
         String hexData = "01 00 " +
                 "10 F0 " +
                 "14 00 00 00 " +
@@ -68,8 +65,7 @@ public class TestEscherClientAnchorRecord extends TestCase
         assertEquals( (byte) 0xDD, r.getRemainingData()[1] );
     }
 
-    public void testToString() throws Exception
-    {
+    public void testToString() {
         String nl = System.getProperty("line.separator");
 
         String expected = "org.apache.poi.ddf.EscherClientAnchorRecord:" + nl +
index 22a11181c6bcf2890e66b1768bc53deea4aff9fa..2f26a37c5a7bfa8fb39d6a2e42dd20fd9842e768 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Licensed to the Apache Software Foundation (ASF) under one or more
    contributor license agreements.  See the NOTICE file distributed with
@@ -15,7 +14,7 @@
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
-        
 package org.apache.poi.ddf;
 
 import junit.framework.TestCase;
@@ -24,8 +23,7 @@ import org.apache.poi.util.HexRead;
 
 public class TestEscherClientDataRecord extends TestCase
 {
-    public void testSerialize() throws Exception
-    {
+    public void testSerialize() {
         EscherClientDataRecord r = createRecord();
 
         byte[] data = new byte[8];
@@ -33,12 +31,11 @@ public class TestEscherClientDataRecord extends TestCase
         assertEquals( 8, bytesWritten );
         assertEquals( "[02, 00, " +
                 "11, F0, " +
-                "00, 00, 00, 00]",
+                "00, 00, 00, 00]",
                 HexDump.toHex( data ) );
     }
 
-    public void testFillFields() throws Exception
-    {
+    public void testFillFields() {
         String hexData = "02 00 " +
                 "11 F0 " +
                 "00 00 00 00 ";
@@ -51,8 +48,7 @@ public class TestEscherClientDataRecord extends TestCase
         assertEquals( "[]", HexDump.toHex(r.getRemainingData()) );
     }
 
-    public void testToString() throws Exception
-    {
+    public void testToString() {
         String nl = System.getProperty("line.separator");
 
         String expected = "org.apache.poi.ddf.EscherClientDataRecord:" + nl +
@@ -63,7 +59,7 @@ public class TestEscherClientDataRecord extends TestCase
         assertEquals( expected, createRecord().toString() );
     }
 
-    private EscherClientDataRecord createRecord()
+    private static EscherClientDataRecord createRecord()
     {
         EscherClientDataRecord r = new EscherClientDataRecord();
         r.setOptions( (short) 0x0002 );
@@ -71,5 +67,4 @@ public class TestEscherClientDataRecord extends TestCase
         r.setRemainingData( new byte[] {} );
         return r;
     }
-
 }
index 0f1fc9c7330e1d4f690cc3160a9c5cd225282c9e..490a3d50ca5ea1a7ebd7f9db75c817126d6238c9 100644 (file)
@@ -30,13 +30,11 @@ public class TestEscherContainerRecord extends TestCase
 {
        private String ESCHER_DATA_PATH;
        
-       protected void setUp() throws Exception {
-               super.setUp();
+       protected void setUp() {
                ESCHER_DATA_PATH = System.getProperty("DDF.testdata.path");
        }
 
-       public void testFillFields() throws Exception
-    {
+       public void testFillFields() {
         EscherRecordFactory f = new DefaultEscherRecordFactory();
         byte[] data = HexRead.readFromString( "0F 02 11 F1 00 00 00 00" );
         EscherRecord r = f.createRecord( data, 0 );
@@ -55,15 +53,14 @@ public class TestEscherContainerRecord extends TestCase
         assertEquals( (short) 0xF222, c.getRecordId() );
     }
 
-    public void testSerialize() throws Exception
-    {
+    public void testSerialize() {
         UnknownEscherRecord r = new UnknownEscherRecord();
         r.setOptions( (short) 0x123F );
         r.setRecordId( (short) 0xF112 );
         byte[] data = new byte[8];
         r.serialize( 0, data, new NullEscherSerializationListener() );
 
-        assertEquals( "[3F, 12, 12, F1, 00, 00, 00, 00]", HexDump.toHex( data ) );
+        assertEquals( "[3F, 12, 12, F1, 00, 00, 00, 00]", HexDump.toHex( data ) );
 
         EscherRecord childRecord = new UnknownEscherRecord();
         childRecord.setOptions( (short) 0x9999 );
@@ -72,12 +69,11 @@ public class TestEscherContainerRecord extends TestCase
         data = new byte[16];
         r.serialize( 0, data, new NullEscherSerializationListener() );
 
-        assertEquals( "[3F, 12, 12, F1, 08, 00, 00, 00, 99, 99, 01, FF, 00, 00, 00, 00]", HexDump.toHex( data ) );
+        assertEquals( "[3F, 12, 12, F1, 08, 00, 00, 00, 99, 99, 01, FF, 00, 00, 00, 00]", HexDump.toHex( data ) );
 
     }
 
-    public void testToString() throws Exception
-    {
+    public void testToString() {
         EscherContainerRecord r = new EscherContainerRecord();
         r.setRecordId( EscherContainerRecord.SP_CONTAINER );
         r.setOptions( (short) 0x000F );
@@ -134,8 +130,7 @@ public class TestEscherContainerRecord extends TestCase
         assertEquals( expected, r.toString() );
     }
 
-    public void testGetRecordSize() throws Exception
-    {
+    public void testGetRecordSize() {
         EscherContainerRecord r = new EscherContainerRecord();
         r.addChildRecord(new EscherRecord()
         {
index c1be281720ee6fc41620b20390567fe0cb136a66..70ddd47330b00d0e466dcee26c9a00fd34ff5c1a 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Licensed to the Apache Software Foundation (ASF) under one or more
    contributor license agreements.  See the NOTICE file distributed with
@@ -15,7 +14,7 @@
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
-        
+
 package org.apache.poi.ddf;
 
 import junit.framework.TestCase;
@@ -24,8 +23,7 @@ import org.apache.poi.util.HexRead;
 
 public class TestEscherDgRecord extends TestCase
 {
-    public void testSerialize() throws Exception
-    {
+    public void testSerialize() {
         EscherDgRecord r = createRecord();
 
         byte[] data = new byte[16];
@@ -35,12 +33,11 @@ public class TestEscherDgRecord extends TestCase
                 "08, F0, " +
                 "08, 00, 00, 00, " +
                 "02, 00, 00, 00, " +     // num shapes in drawing
-                "01, 04, 00, 00]",     // The last MSOSPID given to an SP in this DG
+                "01, 04, 00, 00]",     // The last MSOSPID given to an SP in this DG
                 HexDump.toHex( data ) );
     }
 
-    public void testFillFields() throws Exception
-    {
+    public void testFillFields() {
         String hexData = "10 00 " +
                 "08 F0 " +
                 "08 00 00 00 " +
@@ -55,8 +52,7 @@ public class TestEscherDgRecord extends TestCase
         assertEquals( 1025, r.getLastMSOSPID() );
     }
 
-    public void testToString() throws Exception
-    {
+    public void testToString() {
         String nl = System.getProperty("line.separator");
 
         String expected = "org.apache.poi.ddf.EscherDgRecord:" + nl +
@@ -67,7 +63,7 @@ public class TestEscherDgRecord extends TestCase
         assertEquals( expected, createRecord().toString() );
     }
 
-    private EscherDgRecord createRecord()
+    private static EscherDgRecord createRecord()
     {
         EscherDgRecord r = new EscherDgRecord();
         r.setOptions( (short) 0x0010 );
@@ -76,5 +72,4 @@ public class TestEscherDgRecord extends TestCase
         r.setLastMSOSPID(1025);
         return r;
     }
-
 }
index afecbf3c99db70fdbe9d4f2de2f550a0a828192c..89680669597237963c5b4bed28e44c6be417b581 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Licensed to the Apache Software Foundation (ASF) under one or more
    contributor license agreements.  See the NOTICE file distributed with
@@ -15,7 +14,7 @@
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
-        
+
 package org.apache.poi.ddf;
 
 import junit.framework.TestCase;
@@ -24,8 +23,7 @@ import org.apache.poi.util.HexRead;
 
 public class TestEscherDggRecord extends TestCase
 {
-    public void testSerialize() throws Exception
-    {
+    public void testSerialize() {
         EscherDggRecord r = createRecord();
 
         byte[] data = new byte[32];
@@ -38,12 +36,11 @@ public class TestEscherDggRecord extends TestCase
                 "02, 00, 00, 00, " +
                 "02, 00, 00, 00, " +
                 "01, 00, 00, 00, " +
-                "01, 00, 00, 00, 02, 00, 00, 00]",
+                "01, 00, 00, 00, 02, 00, 00, 00]",
                 HexDump.toHex( data ) );
     }
 
-    public void testFillFields() throws Exception
-    {
+    public void testFillFields() {
         String hexData = "00 00 " +
                 "06 F0 " +
                 "18 00 00 00 " +
@@ -66,8 +63,7 @@ public class TestEscherDggRecord extends TestCase
         assertEquals( 0x02, r.getFileIdClusters()[0].getNumShapeIdsUsed());
     }
 
-    public void testToString() throws Exception
-    {
+    public void testToString() {
         String nl = System.getProperty("line.separator");
 
         String expected = "org.apache.poi.ddf.EscherDggRecord:" + nl +
@@ -82,7 +78,7 @@ public class TestEscherDggRecord extends TestCase
         assertEquals( expected, createRecord().toString() );
     }
 
-    private EscherDggRecord createRecord()
+    private static EscherDggRecord createRecord()
     {
         EscherDggRecord r = new EscherDggRecord();
         r.setOptions( (short) 0x0000 );
@@ -96,12 +92,9 @@ public class TestEscherDggRecord extends TestCase
         return r;
     }
 
-    public void testGetRecordSize() throws Exception
-    {
+    public void testGetRecordSize() {
         EscherDggRecord r = new EscherDggRecord();
         r.setFileIdClusters(new EscherDggRecord.FileIdCluster[] { new EscherDggRecord.FileIdCluster(0,0) } );
         assertEquals(32,r.getRecordSize());
-
     }
-
 }
index fe996f636bcd8bc43f8698537e26cca9b9021168..a96ba84968b93a27565d322bb7e7f3567a4d2d0f 100644 (file)
@@ -24,20 +24,16 @@ import org.apache.poi.util.HexDump;
 
 import java.io.IOException;
 import java.util.Arrays;
-import java.util.List;
-import java.util.Iterator;
 
 public class TestEscherOptRecord extends TestCase
 {
 
-    public void testFillFields() throws Exception
-    {
+    public void testFillFields() {
         checkFillFieldsSimple();
         checkFillFieldsComplex();
     }
 
-    private void checkFillFieldsComplex() throws IOException
-    {
+    private void checkFillFieldsComplex() {
         String dataStr = "33 00 " +
                 "0B F0 " +
                 "14 00 00 00 " +
@@ -60,9 +56,7 @@ public class TestEscherOptRecord extends TestCase
 
     }
 
-    private void checkFillFieldsSimple()
-            throws IOException
-    {
+    private void checkFillFieldsSimple() {
         String dataStr = "33 00 " + // options
                         "0B F0 " + // recordid
                         "12 00 00 00 " + // remaining bytes
@@ -83,8 +77,7 @@ public class TestEscherOptRecord extends TestCase
         assertEquals( prop3, r.getEscherProperty( 2 ) );
     }
 
-    public void testSerialize() throws Exception
-    {
+    public void testSerialize() {
         checkSerializeSimple();
         checkSerializeComplex();
     }
@@ -111,7 +104,7 @@ public class TestEscherOptRecord extends TestCase
                 "BF, 00, 01, 00, 00, 00, " +
                 "01, 80, 02, 00, 00, 00, " +
                 "BF, 00, 01, 00, 00, 00, " +
-                "01, 02]";
+                "01, 02]";
         assertEquals( dataStr, HexDump.toHex(data) );
 
     }
@@ -135,13 +128,12 @@ public class TestEscherOptRecord extends TestCase
                 "12, 00, 00, 00, " +
                 "BF, 00, 01, 00, 00, 00, " +
                 "81, 01, 09, 00, 00, 08, " +
-                "C0, 01, 40, 00, 00, 08]";
+                "C0, 01, 40, 00, 00, 08]";
         assertEquals( dataStr, HexDump.toHex(data) );
         assertEquals( 26, bytesWritten );
     }
 
-    public void testToString() throws Exception
-    {
+    public void testToString() {
         String nl = System.getProperty("line.separator");
         EscherOptRecord r = new EscherOptRecord();
         r.setOptions((short)0x000F);
@@ -162,8 +154,8 @@ public class TestEscherOptRecord extends TestCase
      * Test serialisation of a particually complex example 
      * This test is currently broken!
      */
-    public void testComplexSerialise() throws Exception {
-       byte[] data = new byte[] {
+    public void testComplexSerialise() {
+       byte[] data = {
                0x53, 0x01, 0x0B, 0xF0-256, 0x9C-256, 0x01, 0x00, 0x00, 
                // Simple data follows
                0x42, 0x01,     0x49, 0x00, 0x00, 0x00,          // SP @ 8
@@ -263,7 +255,7 @@ public class TestEscherOptRecord extends TestCase
      *
      * See Bug 41946 for details.
      */
-    public void test41946() throws IOException {
+    public void test41946() {
         String dataStr1 =
                 "03 08 0B F0 00 03 00 00 81 00 30 65 01 00 82 00 98 B2 00 00 83 00 30 65 01 " +
                 "00 84 00 98 B2 00 00 85 00 00 00 00 00 87 00 01 00 00 00 88 00 00 00 00 00 " +
@@ -315,7 +307,7 @@ public class TestEscherOptRecord extends TestCase
      * Test that EscherOptRecord can properly read/write array properties
      * with empty complex part.
      */
-    public void testEmptyArrayProperty() throws IOException {
+    public void testEmptyArrayProperty() {
         EscherOptRecord r = new EscherOptRecord();
         EscherArrayProperty p = new EscherArrayProperty((short)(EscherProperties.FILL__SHADECOLORS + 0x8000), new byte[0] );
         assertEquals(0, p.getNumberOfElementsInArray());
index f79b27c637dcdc80ad0130691b96bc9b97271339..aef2c72add4753c702e7dd8897685a319beba018 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Licensed to the Apache Software Foundation (ASF) under one or more
    contributor license agreements.  See the NOTICE file distributed with
@@ -15,7 +14,7 @@
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
-        
+
 package org.apache.poi.ddf;
 
 import junit.framework.TestCase;
@@ -30,8 +29,7 @@ import org.apache.poi.util.HexDump;
  */
 public class TestEscherPropertyFactory extends TestCase
 {
-    public void testCreateProperties() throws Exception
-    {
+    public void testCreateProperties() {
         String dataStr = "41 C1 " +     // propid, complex ind
                 "03 00 00 00 " +         // size of complex property
                 "01 00 " +              // propid, complex ind
@@ -46,15 +44,11 @@ public class TestEscherPropertyFactory extends TestCase
         List props = f.createProperties( data, 0, (short)3 );
         EscherComplexProperty p1 = (EscherComplexProperty) props.get( 0 );
         assertEquals( (short)0xC141, p1.getId() );
-        assertEquals( "[01, 02, 03]", HexDump.toHex( p1.getComplexData() ) );
+        assertEquals( "[01, 02, 03]", HexDump.toHex( p1.getComplexData() ) );
 
         EscherComplexProperty p3 = (EscherComplexProperty) props.get( 2 );
         assertEquals( (short)0xC141, p3.getId() );
-        assertEquals( "[01, 02, 03, ]", HexDump.toHex( p3.getComplexData() ) );
-
-
+        assertEquals( "[01, 02, 03]", HexDump.toHex( p3.getComplexData() ) );
     }
-
-
-
 }
+
index 4521727e0a28acb9ff0868e178afb34df26f722d..b4ab900ea9e0213a09ae6c309ac8a8ab252aefac 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Licensed to the Apache Software Foundation (ASF) under one or more
    contributor license agreements.  See the NOTICE file distributed with
@@ -15,7 +14,7 @@
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
-        
+
 package org.apache.poi.ddf;
 
 import junit.framework.TestCase;
@@ -24,8 +23,7 @@ import org.apache.poi.util.HexRead;
 
 public class TestEscherSpRecord extends TestCase
 {
-    public void testSerialize() throws Exception
-    {
+    public void testSerialize() {
         EscherSpRecord r = createRecord();
 
         byte[] data = new byte[16];
@@ -35,12 +33,11 @@ public class TestEscherSpRecord extends TestCase
                 "0A, F0, " +
                 "08, 00, 00, 00, " +
                 "00, 04, 00, 00, " +
-                "05, 00, 00, 00]",
+                "05, 00, 00, 00]",
                 HexDump.toHex( data ) );
     }
 
-    public void testFillFields() throws Exception
-    {
+    public void testFillFields() {
         String hexData = "02 00 " +
                 "0A F0 " +
                 "08 00 00 00 " +
@@ -55,8 +52,7 @@ public class TestEscherSpRecord extends TestCase
         assertEquals( 0x05, r.getFlags() );
     }
 
-    public void testToString() throws Exception
-    {
+    public void testToString() {
         String nl = System.getProperty("line.separator");
 
         String expected = "org.apache.poi.ddf.EscherSpRecord:" + nl +
@@ -67,7 +63,7 @@ public class TestEscherSpRecord extends TestCase
         assertEquals( expected, createRecord().toString() );
     }
 
-    private EscherSpRecord createRecord()
+    private static EscherSpRecord createRecord()
     {
         EscherSpRecord r = new EscherSpRecord();
         r.setOptions( (short) 0x0002 );
index f640183097cf2321ca49f3c3d6230351180ba1c5..9feca0abbcdc98b0e0bd0e0c2453429b8472a788 100644 (file)
@@ -1,4 +1,3 @@
-
 /* ====================================================================
    Licensed to the Apache Software Foundation (ASF) under one or more
    contributor license agreements.  See the NOTICE file distributed with
@@ -15,7 +14,7 @@
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
-        
+
 package org.apache.poi.ddf;
 
 import junit.framework.TestCase;
@@ -24,8 +23,7 @@ import org.apache.poi.util.HexRead;
 
 public class TestEscherSpgrRecord extends TestCase
 {
-    public void testSerialize() throws Exception
-    {
+    public void testSerialize() {
         EscherSpgrRecord r = createRecord();
 
         byte[] data = new byte[24];
@@ -37,12 +35,11 @@ public class TestEscherSpgrRecord extends TestCase
                 "01, 00, 00, 00, " +     // x
                 "02, 00, 00, 00, " +     // y
                 "03, 00, 00, 00, " +     // width
-                "04, 00, 00, 00]",     // height
+                "04, 00, 00, 00]",     // height
                 HexDump.toHex( data ) );
     }
 
-    public void testFillFields() throws Exception
-    {
+    public void testFillFields() {
         String hexData = "10 00 " +
                 "09 F0 " +
                 "10 00 00 00 " +
@@ -61,8 +58,7 @@ public class TestEscherSpgrRecord extends TestCase
         assertEquals( 4, r.getRectY2() );
     }
 
-    public void testToString() throws Exception
-    {
+    public void testToString() {
         String nl = System.getProperty("line.separator");
 
         String expected = "org.apache.poi.ddf.EscherSpgrRecord:" + nl +
@@ -72,11 +68,10 @@ public class TestEscherSpgrRecord extends TestCase
                 "  RectY: 2" + nl +
                 "  RectWidth: 3" + nl +
                 "  RectHeight: 4" + nl;
-                ;
         assertEquals( expected, createRecord().toString() );
     }
 
-    private EscherSpgrRecord createRecord()
+    private static EscherSpgrRecord createRecord()
     {
         EscherSpgrRecord r = new EscherSpgrRecord();
         r.setOptions( (short) 0x0010 );
@@ -87,5 +82,4 @@ public class TestEscherSpgrRecord extends TestCase
         r.setRectY2(4);
         return r;
     }
-
 }
index 1163575eb830fbad0d17979f082b22dd600032f6..d9a9080713f790775d9263305a8edd2266260307 100644 (file)
@@ -24,8 +24,7 @@ import org.apache.poi.util.HexRead;
 
 public class TestEscherSplitMenuColorsRecord extends TestCase
 {
-    public void testSerialize() throws Exception
-    {
+    public void testSerialize() {
         EscherSplitMenuColorsRecord r = createRecord();
 
         byte[] data = new byte[24];
@@ -37,12 +36,11 @@ public class TestEscherSplitMenuColorsRecord extends TestCase
                 "02, 04, 00, 00, " +
                 "02, 00, 00, 00, " +
                 "02, 00, 00, 00, " +
-                "01, 00, 00, 00]",
+                "01, 00, 00, 00]",
                 HexDump.toHex( data ) );
     }
 
-    public void testFillFields() throws Exception
-    {
+    public void testFillFields() {
         String hexData = "40 00 " +
                 "1E F1 " +
                 "10 00 00 00 " +
@@ -61,8 +59,7 @@ public class TestEscherSplitMenuColorsRecord extends TestCase
         assertEquals( 0x01, r.getColor4() );
     }
 
-    public void testToString() throws Exception
-    {
+    public void testToString() {
         String nl = System.getProperty("line.separator");
 
         String expected = "org.apache.poi.ddf.EscherSplitMenuColorsRecord:" + nl +
@@ -76,7 +73,7 @@ public class TestEscherSplitMenuColorsRecord extends TestCase
         assertEquals( expected, createRecord().toString() );
     }
 
-    private EscherSplitMenuColorsRecord createRecord()
+    private static EscherSplitMenuColorsRecord createRecord()
     {
         EscherSplitMenuColorsRecord r = new EscherSplitMenuColorsRecord();
         r.setOptions( (short) 0x0040 );
@@ -87,5 +84,4 @@ public class TestEscherSplitMenuColorsRecord extends TestCase
         r.setColor4( 0x1 );
         return r;
     }
-
 }
index 9845a58891f5ef6a443aab0d4306198335411957..d8c36e1a5cbe3d7322dcbf28efb9554b5c1dae07 100644 (file)
@@ -24,8 +24,7 @@ import org.apache.poi.util.HexDump;
 
 public class TestUnknownEscherRecord extends TestCase
 {
-    public void testFillFields() throws Exception
-    {
+    public void testFillFields() {
         String testData =
                 "0F 02 " + // options
                 "11 F1 " + // record id
@@ -82,15 +81,14 @@ public class TestUnknownEscherRecord extends TestCase
 
     }
 
-    public void testSerialize() throws Exception
-    {
+    public void testSerialize() {
         UnknownEscherRecord r = new UnknownEscherRecord();
         r.setOptions( (short) 0x1234 );
         r.setRecordId( (short) 0xF112 );
         byte[] data = new byte[8];
         r.serialize( 0, data, new NullEscherSerializationListener() );
 
-        assertEquals( "[34, 12, 12, F1, 00, 00, 00, 00]", HexDump.toHex( data ) );
+        assertEquals( "[34, 12, 12, F1, 00, 00, 00, 00]", HexDump.toHex( data ) );
 
         EscherRecord childRecord = new UnknownEscherRecord();
         childRecord.setOptions( (short) 0x9999 );
@@ -100,11 +98,10 @@ public class TestUnknownEscherRecord extends TestCase
         data = new byte[16];
         r.serialize( 0, data, new NullEscherSerializationListener() );
 
-        assertEquals( "[3F, 12, 12, F1, 08, 00, 00, 00, 99, 99, 01, FF, 00, 00, 00, 00]", HexDump.toHex( data ) );
+        assertEquals( "[3F, 12, 12, F1, 08, 00, 00, 00, 99, 99, 01, FF, 00, 00, 00, 00]", HexDump.toHex( data ) );
     }
 
-    public void testToString() throws Exception
-    {
+    public void testToString() {
         UnknownEscherRecord r = new UnknownEscherRecord();
         r.setOptions( (short) 0x1234 );
         r.setRecordId( (short) 0xF112 );
@@ -119,6 +116,4 @@ public class TestUnknownEscherRecord extends TestCase
                 "  numchildren: 0" + nl
                 , r.toString() );
     }
-
-
 }
index e4c8e42dcbc4679e72ef4d458fe7686fc19333dd..f806c792f6c9b4db58c02f6a63f5bb004779663e 100644 (file)
Binary files a/src/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls and b/src/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls differ
diff --git a/src/testcases/org/apache/poi/hssf/data/IndexFunctionTestCaseData.xls b/src/testcases/org/apache/poi/hssf/data/IndexFunctionTestCaseData.xls
new file mode 100644 (file)
index 0000000..9dcde61
Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/IndexFunctionTestCaseData.xls differ
index 17aa9fd7108c2f6cf471f36780a549725365e6c4..f24b6926e83fde686ab619f9fde6fcc93b84532c 100644 (file)
Binary files a/src/testcases/org/apache/poi/hssf/data/testRVA.xls and b/src/testcases/org/apache/poi/hssf/data/testRVA.xls differ
index c9fea7f1e44407b03f4a73c9360ea4333b9336bc..e1ffc2538d6e9d55f5899b808d01a046f3174c58 100644 (file)
@@ -22,10 +22,12 @@ 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;
 import org.apache.poi.hssf.record.formula.AreaI;
 import org.apache.poi.hssf.record.formula.AreaPtg;
+import org.apache.poi.hssf.record.formula.ArrayPtg;
 import org.apache.poi.hssf.record.formula.AttrPtg;
 import org.apache.poi.hssf.record.formula.BoolPtg;
 import org.apache.poi.hssf.record.formula.ConcatPtg;
@@ -48,6 +50,7 @@ import org.apache.poi.hssf.record.formula.SubtractPtg;
 import org.apache.poi.hssf.record.formula.UnaryMinusPtg;
 import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
 import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
 import org.apache.poi.hssf.usermodel.HSSFName;
 import org.apache.poi.hssf.usermodel.HSSFRow;
 import org.apache.poi.hssf.usermodel.HSSFSheet;
@@ -862,4 +865,18 @@ public final class TestFormulaParser extends TestCase {
                assertEquals(65535, aptg.getLastRow());
                
        }
+       public void testParseArray()  {
+               Ptg[] ptgs;
+               ptgs = parseFormula("mode({1,2,2,#REF!;FALSE,3,3,2})");
+               assertEquals(2, ptgs.length);
+               Ptg ptg0 = ptgs[0];
+               assertEquals(ArrayPtg.class, ptg0.getClass());
+               assertEquals("{1.0,2.0,2.0,#REF!;FALSE,3.0,3.0,2.0}", ptg0.toFormulaString(null));
+               
+               ArrayPtg aptg = (ArrayPtg) ptg0;
+               Object[][] values = aptg.getTokenArrayValues();
+               assertEquals(ErrorConstant.valueOf(HSSFErrorConstants.ERROR_REF), values[0][3]);
+               assertEquals(Boolean.FALSE, values[1][0]);
+               
+       }
 }
\ No newline at end of file
index d89ffee3eb8cf42c8d07706591158c53fd0aa0d6..bd40e3d05a5cbdc7f0e4f3eaf7acaa6da1f0abac 100644 (file)
@@ -85,7 +85,7 @@ public final class TestFormulaParserEval extends TestCase {
                sheet.createRow(32768).createCell(0).setCellValue(31);
                sheet.createRow(32769).createCell(0).setCellValue(11);
                
-               HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb);
+               HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
                CellValue result;
                try {
                        result = fe.evaluate(cell);
index ca74c6e2dbaf279d28a2db2aba8f947597273e01..1ec7c92ae9c608111d054d0bc9bda06f2fa0a7e7 100644 (file)
@@ -61,8 +61,10 @@ public final class TestRVA extends TestCase {
                        try {
                                confirmCell(cell, formula, wb);
                        } catch (AssertionFailedError e) {
+                               System.out.flush();
                                System.err.println("Problem with row[" + rowIx + "] formula '" + formula + "'");
                                System.err.println(e.getMessage());
+                               System.err.flush();
                                countFailures++;
                        } catch (RuntimeException e) {
                                System.err.println("Problem with row[" + rowIx + "] formula '" + formula + "'");
@@ -104,8 +106,8 @@ public final class TestRVA extends TestCase {
                        if (excelPtg.getClass() != poiPtg.getClass()) {
                                hasMismatch = true;
                                sb.append("  mismatch token type[" + i + "] " + getShortClassName(excelPtg) + " "
-                                               + getOperandClassName(excelPtg) + " - " + getShortClassName(poiPtg) + " "
-                                               + getOperandClassName(poiPtg));
+                                               + excelPtg.getRVAType() + " - " + getShortClassName(poiPtg) + " "
+                                               + poiPtg.getRVAType());
                                sb.append(NEW_LINE);
                                continue;
                        }
@@ -113,16 +115,16 @@ public final class TestRVA extends TestCase {
                                continue;
                        }
                        sb.append("  token[" + i + "] " + excelPtg.toString() + " "
-                                       + getOperandClassName(excelPtg));
+                                       + excelPtg.getRVAType());
 
                        if (excelPtg.getPtgClass() != poiPtg.getPtgClass()) {
                                hasMismatch = true;
-                               sb.append(" - was " + getOperandClassName(poiPtg));
+                               sb.append(" - was " + poiPtg.getRVAType());
                        }
                        sb.append(NEW_LINE);
                }
                if (false) { // set 'true' to see trace of RVA values
-                       System.out.println(formula);
+                       System.out.println(formulaCell.getRowIndex() + " " + formula);
                        System.out.println(sb.toString());
                }
                if (hasMismatch) {
@@ -135,14 +137,4 @@ public final class TestRVA extends TestCase {
                int pos = cn.lastIndexOf('.');
                return cn.substring(pos + 1);
        }
-
-       private static String getOperandClassName(Ptg ptg) {
-               byte ptgClass = ptg.getPtgClass();
-               switch (ptgClass) {
-                       case Ptg.CLASS_REF:   return "R";
-                       case Ptg.CLASS_VALUE: return "V";
-                       case Ptg.CLASS_ARRAY: return "A";
-               }
-               throw new RuntimeException("Unknown operand class (" + ptgClass + ")");
-       }
 }
index ca6a10f45e8ddb9835f8de24b01948e71ce4cb13..3bc79299b5a8967d47f14105b1a5d8bab197fe15 100644 (file)
@@ -214,7 +214,9 @@ public final class TestSheet extends TestCase {
         records.add(new DimensionsRecord());
         records.add(new RowRecord(0));
         records.add(new RowRecord(1));
-        records.add(new FormulaRecord());
+        FormulaRecord formulaRecord = new FormulaRecord();
+        formulaRecord.setCachedResultTypeString();
+               records.add(formulaRecord);
         records.add(new StringRecord());
         records.add(new RowRecord(2));
         records.add(createWindow2Record());
@@ -357,7 +359,7 @@ public final class TestSheet extends TestCase {
         xfindex = sheet.getXFIndexForColAt((short) 1);
         assertEquals(DEFAULT_IDX, xfindex);
 
-        ColumnInfoRecord nci = ColumnInfoRecordsAggregate.createColInfo();
+        ColumnInfoRecord nci = new ColumnInfoRecord();
         sheet._columnInfos.insertColumn(nci);
 
         // single column ColumnInfoRecord
@@ -567,7 +569,7 @@ public final class TestSheet extends TestCase {
 
         sheet.setMargin(HSSFSheet.LeftMargin, 0.3);
         try {
-            row.createCell((short) 0);
+            row.createCell(0);
         } catch (IllegalStateException e) {
             if (e.getMessage().equals("Cannot create value records before row records exist")) {
                 throw new AssertionFailedError("Identified bug 45717");
@@ -576,4 +578,3 @@ public final class TestSheet extends TestCase {
         }
     }
 }
-
index 0962b9148aaf3feee84298a62cbda598b8d007c4..477908dc7295deb685a37410f9c961f4dac5dc2f 100644 (file)
@@ -20,7 +20,6 @@ package org.apache.poi.hssf.model;
 import junit.framework.TestCase;
 
 import org.apache.poi.hssf.record.ColumnInfoRecord;
-import org.apache.poi.hssf.record.aggregates.ColumnInfoRecordsAggregate;
 
 /**
  * @author Tony Poppleton
@@ -29,7 +28,7 @@ public final class TestSheetAdditional extends TestCase {
        
        public void testGetCellWidth() {
                Sheet sheet = Sheet.createSheet();
-               ColumnInfoRecord nci = ColumnInfoRecordsAggregate.createColInfo();
+               ColumnInfoRecord nci = new ColumnInfoRecord();
 
                // Prepare test model
                nci.setFirstColumn((short)5);
@@ -55,5 +54,4 @@ public final class TestSheetAdditional extends TestCase {
                assertEquals((short)100,sheet.getColumnWidth((short)9));
                assertEquals((short)100,sheet.getColumnWidth((short)10));
        }
-
 }
index d248baf3043148482d47d8ea036bbb7659817f51..17d6475e8b08c26f2aa02c2fb7f4515f08734c22 100644 (file)
@@ -22,14 +22,11 @@ import org.apache.poi.ddf.EscherContainerRecord;
 import org.apache.poi.ddf.EscherSpRecord;
 import org.apache.poi.util.HexDump;
 
-public class TestDrawingGroupRecord extends TestCase
-{
-    static final int MAX_RECORD_SIZE = 8228;
+public final class TestDrawingGroupRecord extends TestCase {
+    private static final int MAX_RECORD_SIZE = 8228;
     private static final int MAX_DATA_SIZE = MAX_RECORD_SIZE - 4;
 
-    public void testGetRecordSize()
-            throws Exception
-    {
+    public void testGetRecordSize() {
         DrawingGroupRecord r = new DrawingGroupRecord();
         assertEquals(4, r.getRecordSize());
 
@@ -48,7 +45,7 @@ public class TestDrawingGroupRecord extends TestCase
 
         byte[] data = new byte[28];
         int size = r.serialize(0, data);
-        assertEquals("[EB, 00, 18, 00, 0F, 00, 00, F0, 10, 00, 00, 00, 11, 11, 0A, F0, 08, 00, 00, 00, FF, FF, FF, FF, FF, FF, FF, FF]", HexDump.toHex(data));
+        assertEquals("[EB, 00, 18, 00, 0F, 00, 00, F0, 10, 00, 00, 00, 11, 11, 0A, F0, 08, 00, 00, 00, FF, FF, FF, FF, FF, FF, FF, FF]", HexDump.toHex(data));
         assertEquals(28, size);
 
         assertEquals(24, dggContainer.getRecordSize());
@@ -65,8 +62,7 @@ public class TestDrawingGroupRecord extends TestCase
         assertEquals( MAX_RECORD_SIZE * 2 + 5, r.getRecordSize() );
     }
 
-    public void testSerialize() throws Exception
-    {
+    public void testSerialize() {
         // Check under max record size
         DrawingGroupRecord r = new DrawingGroupRecord();
         byte[] rawData = new byte[100];
@@ -76,7 +72,7 @@ public class TestDrawingGroupRecord extends TestCase
         byte[] buffer = new byte[r.getRecordSize()];
         int size = r.serialize( 0, buffer );
         assertEquals( 104, size );
-        assertEquals("[EB, 00, 64, 00, 64, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, C8]", HexDump.toHex(buffer));
+        assertEquals("[EB, 00, 64, 00, 64, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, C8]", HexDump.toHex(buffer));
 
         // check at max record size
         rawData = new byte[MAX_DATA_SIZE];
@@ -92,8 +88,8 @@ public class TestDrawingGroupRecord extends TestCase
         buffer = new byte[r.getRecordSize()];
         size = r.serialize( 0, buffer );
         assertEquals( MAX_RECORD_SIZE + 5, size );
-        assertEquals( "[EB, 00, 20, 20]", HexDump.toHex(cut(buffer, 0, 4) ));
-        assertEquals( "[00, EB, 00, 01, 00, FF]", HexDump.toHex(cut(buffer, MAX_RECORD_SIZE - 1, MAX_RECORD_SIZE + 5) ));
+        assertEquals( "[EB, 00, 20, 20]", HexDump.toHex(cut(buffer, 0, 4) ));
+        assertEquals( "[00, EB, 00, 01, 00, FF]", HexDump.toHex(cut(buffer, MAX_RECORD_SIZE - 1, MAX_RECORD_SIZE + 5) ));
 
         // check continue record
         rawData = new byte[MAX_DATA_SIZE * 2 + 1];
@@ -103,9 +99,9 @@ public class TestDrawingGroupRecord extends TestCase
         size = r.serialize( 0, buffer );
         assertEquals( MAX_RECORD_SIZE * 2 + 5, size );
         assertEquals( MAX_RECORD_SIZE * 2 + 5, r.getRecordSize() );
-        assertEquals( "[EB, 00, 20, 20]", HexDump.toHex(cut(buffer, 0, 4) ));
-        assertEquals( "[EB, 00, 20, 20]", HexDump.toHex(cut(buffer, MAX_RECORD_SIZE, MAX_RECORD_SIZE + 4) ));
-        assertEquals( "[3C, 00, 01, 00, FF]", HexDump.toHex(cut(buffer, MAX_RECORD_SIZE * 2, MAX_RECORD_SIZE * 2 + 5) ));
+        assertEquals( "[EB, 00, 20, 20]", HexDump.toHex(cut(buffer, 0, 4) ));
+        assertEquals( "[EB, 00, 20, 20]", HexDump.toHex(cut(buffer, MAX_RECORD_SIZE, MAX_RECORD_SIZE + 4) ));
+        assertEquals( "[3C, 00, 01, 00, FF]", HexDump.toHex(cut(buffer, MAX_RECORD_SIZE * 2, MAX_RECORD_SIZE * 2 + 5) ));
 
         // check continue record
         rawData = new byte[664532];
@@ -116,7 +112,7 @@ public class TestDrawingGroupRecord extends TestCase
         assertEquals( 664856, r.getRecordSize() );
     }
 
-    private byte[] cut( byte[] data, int fromInclusive, int toExclusive )
+    private static byte[] cut( byte[] data, int fromInclusive, int toExclusive )
     {
         int length = toExclusive - fromInclusive;
         byte[] result = new byte[length];
@@ -124,8 +120,7 @@ public class TestDrawingGroupRecord extends TestCase
         return result;
     }
 
-    public void testGrossSizeFromDataSize() throws Exception
-    {
+    public void testGrossSizeFromDataSize() {
         for (int i = 0; i < MAX_RECORD_SIZE * 4; i += 11)
         {
             //System.out.print( "data size = " + i + ", gross size = " + DrawingGroupRecord.grossSizeFromDataSize( i ) );
@@ -139,6 +134,4 @@ public class TestDrawingGroupRecord extends TestCase
         assertEquals( MAX_RECORD_SIZE * 2, DrawingGroupRecord.grossSizeFromDataSize( MAX_DATA_SIZE * 2 ) );
         assertEquals( MAX_RECORD_SIZE * 2 + 5, DrawingGroupRecord.grossSizeFromDataSize( MAX_DATA_SIZE * 2 + 1 ) );
     }
-
-
 }
index 21be7ad1d0562a382afcca6e4f1838b08e71e93c..593290b337649c900fde68cb6ba899ef8dfb6e43 100644 (file)
@@ -133,7 +133,7 @@ public class TestEscherAggregate extends TestCase
         byte[] data = new byte[112];
         int bytesWritten = aggregate.serialize( 0, data );
         assertEquals( 112, bytesWritten );
-        assertEquals( "[EC, 00, 40, 00, 0F, 00, 00, 00, 58, 00, 00, 00, 0F, 00, 04, F0, 10, 00, 00, 00, 00, 00, 0A, F0, 08, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0F, 00, 04, F0, 18, 00, 00, 00, 00, 00, 0A, F0, 08, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 11, F0, 00, 00, 00, 00, 5D, 00, 00, 00, EC, 00, 20, 00, 0F, 00, 04, F0, 18, 00, 00, 00, 00, 00, 0A, F0, 08, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 11, F0, 00, 00, 00, 00, 5D, 00, 00, 00]",
+        assertEquals( "[EC, 00, 40, 00, 0F, 00, 00, 00, 58, 00, 00, 00, 0F, 00, 04, F0, 10, 00, 00, 00, 00, 00, 0A, F0, 08, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0F, 00, 04, F0, 18, 00, 00, 00, 00, 00, 0A, F0, 08, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 11, F0, 00, 00, 00, 00, 5D, 00, 00, 00, EC, 00, 20, 00, 0F, 00, 04, F0, 18, 00, 00, 00, 00, 00, 0A, F0, 08, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 11, F0, 00, 00, 00, 00, 5D, 00, 00, 00]",
                 HexDump.toHex( data ) );
     }
 
index 0d96d737cf08cd0f02c5f02a4ec246718177c8ce..4696539f2385057b0569c13cf3b2d3efcb8f5b89 100644 (file)
@@ -26,12 +26,14 @@ import org.apache.poi.hssf.record.formula.FuncVarPtg;
 import org.apache.poi.hssf.record.formula.IntPtg;
 import org.apache.poi.hssf.record.formula.Ptg;
 import org.apache.poi.hssf.record.formula.RefPtg;
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
 
 /**
  * Tests the serialization and deserialization of the FormulaRecord
- * class works correctly.  
+ * class works correctly.
  *
- * @author Andrew C. Oliver 
+ * @author Andrew C. Oliver
  */
 public final class TestFormulaRecord extends TestCase {
 
@@ -40,52 +42,66 @@ public final class TestFormulaRecord extends TestCase {
                record.setColumn((short)0);
                record.setRow(1);
                record.setXFIndex((short)4);
-               
+
                assertEquals(record.getColumn(),0);
                assertEquals(record.getRow(), 1);
                assertEquals(record.getXFIndex(),4);
        }
-       
+
        /**
         * Make sure a NAN value is preserved
-        * This formula record is a representation of =1/0 at row 0, column 0 
+        * This formula record is a representation of =1/0 at row 0, column 0
         */
        public void testCheckNanPreserve() {
-               byte[] formulaByte = new byte[29];
-
-               formulaByte[4] = (byte)0x0F;
-               formulaByte[6] = (byte)0x02;
-               formulaByte[8] = (byte)0x07;
-               formulaByte[12] = (byte)0xFF;
-               formulaByte[13] = (byte)0xFF;
-               formulaByte[18] = (byte)0xE0;
-               formulaByte[19] = (byte)0xFC;
-               formulaByte[20] = (byte)0x07;
-               formulaByte[22] = (byte)0x1E;
-               formulaByte[23] = (byte)0x01;
-               formulaByte[25] = (byte)0x1E;
-               formulaByte[28] = (byte)0x06;
-               
+               byte[] formulaByte = {
+                       0, 0, 0, 0,
+                       0x0F, 0x00,
+
+                       // 8 bytes cached number is a 'special value' in this case
+                       0x02, // special cached value type 'error'
+                       0x00,
+                       HSSFErrorConstants.ERROR_DIV_0,
+                       0x00,
+                       0x00,
+                       0x00,
+                       (byte)0xFF,
+                       (byte)0xFF,
+
+                       0x00,
+                       0x00,
+                       0x00,
+                       0x00,
+
+                       (byte)0xE0, //18
+                       (byte)0xFC,
+                       // Ptgs
+                       0x07, 0x00, // encoded length
+                       0x1E, 0x01, 0x00, // IntPtg(1)
+                       0x1E, 0x00,     0x00, // IntPtg(0)
+                       0x06, // DividePtg
+
+               };
+
                FormulaRecord record = new FormulaRecord(new TestcaseRecordInputStream(FormulaRecord.sid, (short)29, formulaByte));
                assertEquals("Row", 0, record.getRow());
-               assertEquals("Column", 0, record.getColumn());          
-               assertTrue("Value is not NaN", Double.isNaN(record.getValue()));
-               
+               assertEquals("Column", 0, record.getColumn());
+               assertEquals(HSSFCell.CELL_TYPE_ERROR, record.getCachedResultType());
+
                byte[] output = record.serialize();
                assertEquals("Output size", 33, output.length); //includes sid+recordlength
-               
+
                for (int i = 5; i < 13;i++) {
                        assertEquals("FormulaByte NaN doesn't match", formulaByte[i], output[i+4]);
                }
        }
-       
+
        /**
         * Tests to see if the shared formula cells properly reserialize the expPtg
         *
         */
        public void testExpFormula() {
                byte[] formulaByte = new byte[27];
-               
+
                formulaByte[4] =(byte)0x0F;
                formulaByte[14]=(byte)0x08;
                formulaByte[18]=(byte)0xE0;
@@ -99,13 +115,13 @@ public final class TestFormulaRecord extends TestCase {
                assertEquals("Output size", 31, output.length); //includes sid+recordlength
                assertEquals("Offset 22", 1, output[26]);
        }
-       
+
        public void testWithConcat() {
                // =CHOOSE(2,A2,A3,A4)
                byte[] data = {
                                6, 0, 68, 0,
                                1, 0, 1, 0, 15, 0, 0, 0, 0, 0, 0, 0, 57,
-                               64, 0, 0, 12, 0, 12, -4, 46, 0, 
+                               64, 0, 0, 12, 0, 12, -4, 46, 0,
                                30, 2, 0,       // Int - 2
                                25, 4, 3, 0, // Attr
                                        8, 0, 17, 0, 26, 0, // jumpTable
@@ -115,14 +131,14 @@ public final class TestFormulaRecord extends TestCase {
                                36, 2, 0, 0, -64, // Ref - A3
                                25,     8, 12, 0, // Attr
                                36, 3, 0, 0, -64, // Ref - A4
-                               25, 8, 3, 0,  // Attr 
+                               25, 8, 3, 0,  // Attr
                                66, 4, 100, 0 // CHOOSE
                };
                RecordInputStream inp = new RecordInputStream( new ByteArrayInputStream(data));
                inp.nextRecord();
-               
+
                FormulaRecord fr = new FormulaRecord(inp);
-               
+
                Ptg[] ptgs = fr.getParsedExpression();
                assertEquals(9, ptgs.length);
                assertEquals(IntPtg.class,         ptgs[0].getClass());
@@ -134,7 +150,7 @@ public final class TestFormulaRecord extends TestCase {
                assertEquals(RefPtg.class, ptgs[6].getClass());
                assertEquals(AttrPtg.class,       ptgs[7].getClass());
                assertEquals(FuncVarPtg.class,   ptgs[8].getClass());
-               
+
                FuncVarPtg choose = (FuncVarPtg)ptgs[8];
                assertEquals("CHOOSE", choose.getName());
        }
index 2988290616d29cbda560e9d2fec9a902af0d520b..6c981cab5e74f3d4a7a0ca162ec55b49b897e19b 100644 (file)
 
 package org.apache.poi.hssf.record.aggregates;
 
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;
+
 import org.apache.poi.hssf.record.ColumnInfoRecord;
+import org.apache.poi.hssf.record.Record;
 import org.apache.poi.hssf.record.RecordBase;
+import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor;
 
 /**
  * @author Glen Stampoultzis
@@ -28,11 +35,11 @@ public final class TestColumnInfoRecordsAggregate extends TestCase {
 
        public void testGetRecordSize() {
                ColumnInfoRecordsAggregate agg = new ColumnInfoRecordsAggregate();
-               agg.insertColumn(createColumn(1, 3));
-               agg.insertColumn(createColumn(4, 7));
-               agg.insertColumn(createColumn(8, 8));
+               agg.insertColumn(createColInfo(1, 3));
+               agg.insertColumn(createColInfo(4, 7));
+               agg.insertColumn(createColInfo(8, 8));
                agg.groupColumnRange((short) 2, (short) 5, true);
-               assertEquals(6, agg.getNumColumns());
+               assertEquals(4, agg.getNumColumns());
 
                confirmSerializedSize(agg);
 
@@ -48,10 +55,91 @@ public final class TestColumnInfoRecordsAggregate extends TestCase {
                assertEquals(estimatedSize, serializedSize);
        }
 
-       private static ColumnInfoRecord createColumn(int firstCol, int lastCol) {
+       private static ColumnInfoRecord createColInfo(int firstCol, int lastCol) {
                ColumnInfoRecord columnInfoRecord = new ColumnInfoRecord();
                columnInfoRecord.setFirstColumn((short) firstCol);
                columnInfoRecord.setLastColumn((short) lastCol);
                return columnInfoRecord;
        }
-}
\ No newline at end of file
+
+       private static final class CIRCollector implements RecordVisitor {
+
+               private List _list;
+               public CIRCollector() {
+                       _list = new ArrayList();
+               }
+               public void visitRecord(Record r) {
+                       _list.add(r);
+               }
+               public static ColumnInfoRecord[] getRecords(ColumnInfoRecordsAggregate agg) {
+                       CIRCollector circ = new CIRCollector();
+                       agg.visitContainedRecords(circ);
+                       List list = circ._list;
+                       ColumnInfoRecord[] result = new ColumnInfoRecord[list.size()];
+                       list.toArray(result);
+                       return result;
+               }
+       }
+
+       public void testGroupColumns_bug45639() {
+               ColumnInfoRecordsAggregate agg = new ColumnInfoRecordsAggregate();
+               agg.groupColumnRange( 7, 9, true);
+               agg.groupColumnRange( 4, 12, true);
+               try {
+                       agg.groupColumnRange( 1, 15, true);
+               } catch (ArrayIndexOutOfBoundsException e) {
+                       throw new AssertionFailedError("Identified bug 45639");
+               }
+               ColumnInfoRecord[] cirs = CIRCollector.getRecords(agg);
+               assertEquals(5, cirs.length);
+               confirmCIR(cirs, 0,  1,  3, 1, false, false);
+               confirmCIR(cirs, 1,  4,  6, 2, false, false);
+               confirmCIR(cirs, 2,  7,  9, 3, false, false);
+               confirmCIR(cirs, 3, 10, 12, 2, false, false);
+               confirmCIR(cirs, 4, 13, 15, 1, false, false);
+       }
+
+       /**
+        * Check that an inner group remains hidden
+        */
+       public void testHiddenAfterExpanding() {
+               ColumnInfoRecordsAggregate agg = new ColumnInfoRecordsAggregate();
+               agg.groupColumnRange(1, 15, true);
+               agg.groupColumnRange(4, 12, true);
+
+               ColumnInfoRecord[] cirs;
+
+               // collapse both inner and outer groups
+               agg.collapseColumn(6);
+               agg.collapseColumn(3);
+
+               cirs = CIRCollector.getRecords(agg);
+               assertEquals(5, cirs.length);
+               confirmCIR(cirs, 0,  1,  3, 1, true, false);
+               confirmCIR(cirs, 1,  4, 12, 2, true, false);
+               confirmCIR(cirs, 2, 13, 13, 1, true, true);
+               confirmCIR(cirs, 3, 14, 15, 1, true, false);
+               confirmCIR(cirs, 4, 16, 16, 0, false, true);
+
+               // just expand the inner group
+               agg.expandColumn(6);
+
+               cirs = CIRCollector.getRecords(agg);
+               assertEquals(4, cirs.length);
+               if (!cirs[1].getHidden()) {
+                       throw new AssertionFailedError("Inner group should still be hidden");
+               }
+               confirmCIR(cirs, 0,  1,  3, 1, true, false);
+               confirmCIR(cirs, 1,  4, 12, 2, true, false);
+               confirmCIR(cirs, 2, 13, 15, 1, true, false);
+               confirmCIR(cirs, 3, 16, 16, 0, false, true);
+       }
+       private static void confirmCIR(ColumnInfoRecord[] cirs, int ix, int startColIx, int endColIx, int level, boolean isHidden, boolean isCollapsed) {
+               ColumnInfoRecord cir = cirs[ix];
+               assertEquals("startColIx", startColIx, cir.getFirstColumn());
+               assertEquals("endColIx", endColIx, cir.getLastColumn());
+               assertEquals("level", level, cir.getOutlineLevel());
+               assertEquals("hidden", isHidden, cir.getHidden());
+               assertEquals("collapsed", isCollapsed, cir.getCollapsed());
+       }
+}
index 9b9535602724607da8c8664aef80794a2f5a66a6..978f400fcd0a87f6e69f56b1dd77adad43ff7290 100644 (file)
@@ -30,6 +30,7 @@ public final class TestFormulaRecordAggregate extends TestCase {
     
     public void testBasic() throws Exception {
         FormulaRecord f = new FormulaRecord();
+        f.setCachedResultTypeString();
         StringRecord s = new StringRecord();
         s.setString("abc");
         FormulaRecordAggregate fagg = new FormulaRecordAggregate(f, s, SharedValueManager.EMPTY);
index 9e5a34004505182aa806bad4f2cd0e7d704b917b..2455c87ab9c35d3fc9fd090503f166d7598d2bca 100644 (file)
@@ -35,9 +35,8 @@ import junit.framework.TestCase;
 public final class TestArrayPtg extends TestCase {
 
        private static final byte[] ENCODED_PTG_DATA = {
-               0x40, 0x00,
-               0x08, 0x00,
-               0, 0, 0, 0, 0, 0, 0, 0, 
+               0x40,
+               0, 0, 0, 0, 0, 0, 0, 
        };
        private static final byte[] ENCODED_CONSTANT_DATA = {
                2,    // 3 columns
@@ -60,15 +59,15 @@ public final class TestArrayPtg extends TestCase {
                ptg.readTokenValues(new TestcaseRecordInputStream(0, ENCODED_CONSTANT_DATA));
                assertEquals(3, ptg.getColumnCount());
                assertEquals(2, ptg.getRowCount());
-               Object[] values = ptg.getTokenArrayValues();
-               assertEquals(6, values.length);
+               Object[][] values = ptg.getTokenArrayValues();
+               assertEquals(2, values.length);
                
                
-               assertEquals(Boolean.TRUE, values[0]);
-               assertEquals(new UnicodeString("ABCD"), values[1]);
-               assertEquals(new Double(0), values[3]);
-               assertEquals(Boolean.FALSE, values[4]);
-               assertEquals(new UnicodeString("FG"), values[5]);
+               assertEquals(Boolean.TRUE, values[0][0]);
+               assertEquals(new UnicodeString("ABCD"), values[0][1]);
+               assertEquals(new Double(0), values[1][0]);
+               assertEquals(Boolean.FALSE, values[1][1]);
+               assertEquals(new UnicodeString("FG"), values[1][2]);
                
                byte[] outBuf = new byte[ENCODED_CONSTANT_DATA.length];
                ptg.writeTokenValueBytes(outBuf, 0);
@@ -89,10 +88,10 @@ public final class TestArrayPtg extends TestCase {
                assertEquals(2, ptg.getRowCount());
                
                assertEquals(0, ptg.getValueIndex(0, 0));
-               assertEquals(2, ptg.getValueIndex(1, 0));
-               assertEquals(4, ptg.getValueIndex(2, 0));
-               assertEquals(1, ptg.getValueIndex(0, 1));
-               assertEquals(3, ptg.getValueIndex(1, 1));
+               assertEquals(1, ptg.getValueIndex(1, 0));
+               assertEquals(2, ptg.getValueIndex(2, 0));
+               assertEquals(3, ptg.getValueIndex(0, 1));
+               assertEquals(4, ptg.getValueIndex(1, 1));
                assertEquals(5, ptg.getValueIndex(2, 1));
        }
        
@@ -110,7 +109,7 @@ public final class TestArrayPtg extends TestCase {
                if (formula.equals("SUM({1.0,6.0,11.0;2.0,7.0,12.0;3.0,8.0,13.0;4.0,9.0,14.0;5.0,10.0,15.0})")) {
                        throw new AssertionFailedError("Identified bug 42564 b");
                }
-               assertEquals("SUM({1.0,2.0,3.0;4.0,5.0,6.0;7.0,8.0,9.0;10.0,11.0,12.0;13.0,14.0,15.0})", formula);
+               assertEquals("SUM({1.0,2.0,3.0,4.0,5.0;6.0,7.0,8.0,9.0,10.0;11.0,12.0,13.0,14.0,15.0})", formula);
        }
 
        public void testToFormulaString() {
@@ -127,7 +126,7 @@ public final class TestArrayPtg extends TestCase {
                        }
                        throw e;
                }
-               assertEquals("{TRUE,\"ABCD\";\"E\",0.0;FALSE,\"FG\"}", actualFormula);
+               assertEquals("{TRUE,\"ABCD\",\"E\";0.0,FALSE,\"FG\"}", actualFormula);
        }
        
        /**
@@ -150,6 +149,7 @@ public final class TestArrayPtg extends TestCase {
                RecordInputStream in = new TestcaseRecordInputStream(ArrayPtg.sid, fullData);
                
                Ptg[] ptgs = Ptg.readTokens(ENCODED_PTG_DATA.length, in);
+               assertEquals(1, ptgs.length);
                ArrayPtg aPtg = (ArrayPtg) ptgs[0];
                assertEquals(operandClass, aPtg.getPtgClass());
        }
index c1087c1fc476000d875b26654da693425549ed67..7e71889d8b5e08e465a3138067fb1dc898e0969e 100755 (executable)
@@ -74,7 +74,7 @@ public final class TestExternalFunctionFormulas extends TestCase {
        public void testEvaluate() {
                HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("externalFunctionExample.xls");
                HSSFSheet sheet = wb.getSheetAt(0);
-               HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb);
+               HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
                confirmCellEval(sheet, 0, 0, fe, "YEARFRAC(B1,C1)", 29.0/90.0);
                confirmCellEval(sheet, 1, 0, fe, "YEARFRAC(B2,C2)", 0.0);
                confirmCellEval(sheet, 2, 0, fe, "YEARFRAC(B3,C3,D3)", 0.0);
index 2cad8e3620aa3e6056ad960102e6c06d401400f8..37d1bb314bd9b8d0b38d2d68c34ba1df526c9b04 100644 (file)
@@ -56,7 +56,7 @@ public final class TestYearFracCalculatorFromSpreadsheet extends TestCase {
                
                HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("yearfracExamples.xls");
                HSSFSheet sheet = wb.getSheetAt(0);
-               HSSFFormulaEvaluator formulaEvaluator = new HSSFFormulaEvaluator(sheet, wb);
+               HSSFFormulaEvaluator formulaEvaluator = new HSSFFormulaEvaluator(wb);
                int nSuccess = 0;
                int nFailures = 0;
                int nUnexpectedErrors = 0;
index 07f13a0445bf2601580e7daae58c3ae817d2bf0e..74691b27dde7b8498dfcdf7c57b4c305f6a0368a 100755 (executable)
@@ -35,9 +35,9 @@ public final class TestCircularReferences extends TestCase {
        /**
         * Translates StackOverflowError into AssertionFailedError
         */
-       private static CellValue evaluateWithCycles(HSSFWorkbook wb, HSSFSheet sheet, HSSFCell testCell)
+       private static CellValue evaluateWithCycles(HSSFWorkbook wb, HSSFCell testCell)
                        throws AssertionFailedError {
-               HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb);
+               HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(wb);
                try {
                        return evaluator.evaluate(testCell);
                } catch (StackOverflowError e) {
@@ -75,7 +75,7 @@ public final class TestCircularReferences extends TestCase {
                // arguments before invoking operators, POI must handle such potential cycles gracefully.
                
 
-               CellValue cellValue = evaluateWithCycles(wb, sheet, testCell);
+               CellValue cellValue = evaluateWithCycles(wb, testCell);
                
                assertTrue(cellValue.getCellType() == HSSFCell.CELL_TYPE_NUMERIC);
                assertEquals(2, cellValue.getNumberValue(), 0);
@@ -93,7 +93,7 @@ public final class TestCircularReferences extends TestCase {
                HSSFCell testCell = row.createCell(0);
                testCell.setCellFormula("A1");
 
-               CellValue cellValue = evaluateWithCycles(wb, sheet, testCell);
+               CellValue cellValue = evaluateWithCycles(wb, testCell);
                
                confirmCycleErrorCode(cellValue);
        }
@@ -113,7 +113,7 @@ public final class TestCircularReferences extends TestCase {
                HSSFCell testCell = row.createCell(3);
                testCell.setCellFormula("A1");
 
-               CellValue cellValue = evaluateWithCycles(wb, sheet, testCell);
+               CellValue cellValue = evaluateWithCycles(wb, testCell);
                
                confirmCycleErrorCode(cellValue);
        }
index e1bed55c516a247d6e11a777cc6fed525c05bc43..95408a4aaf57fcbb90065395ca44998fcdc03f6f 100755 (executable)
@@ -65,7 +65,7 @@ public final class TestExternalFunction extends TestCase {
                String actualFormula=cell.getCellFormula();
                assertEquals("myFunc()", actualFormula);
                
-               HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb);
+               HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
                CellValue evalResult = fe.evaluate(cell);
                
                // Check the return value from ExternalFunction.evaluate()
index da110a020c78c16547af3c2c909a9c481154e824..257bb8f71124435405a4fa978aa80adefcd895fb 100755 (executable)
@@ -66,7 +66,7 @@ public final class TestFormulaBugs extends TestCase {
                                .getCellFormula());
 
                // We might as well evaluate the formula
-               HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb);
+               HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
                CellValue cv = fe.evaluate(cell);
 
                assertEquals(HSSFCell.CELL_TYPE_NUMERIC, cv.getCellType());
@@ -111,7 +111,7 @@ public final class TestFormulaBugs extends TestCase {
                }
                
                // use POI's evaluator as an extra sanity check
-               HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb);
+               HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
                CellValue cv;
                cv = fe.evaluate(cell);
                assertEquals(HSSFCell.CELL_TYPE_NUMERIC, cv.getCellType());
@@ -162,7 +162,7 @@ public final class TestFormulaBugs extends TestCase {
 
                double expectedResult = (4.0 * 8.0 + 5.0 * 9.0) / 10.0;
 
-               HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet1, wb);
+               HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
                CellValue cv = fe.evaluate(cell);
 
                assertEquals(HSSFCell.CELL_TYPE_NUMERIC, cv.getCellType());
index ffe4bffc0add18651df19692d473700352345d31..74b436cdb729086b8a796258ab08c810124a2184 100644 (file)
@@ -113,15 +113,6 @@ public final class TestFormulasFromSpreadsheet extends TestCase {
                        throw new AssertionFailedError(msg + " - actual value was null");
                }
                
-               if (expected.getCellType() == Cell.CELL_TYPE_STRING) {
-                       String value = expected.getRichStringCellValue().getString();
-                       if (value.startsWith("#")) {
-                               // TODO - this code never called
-                               expected.setCellType(Cell.CELL_TYPE_ERROR);
-                               // expected.setCellErrorValue(...?);
-                       }
-               }
-               
                switch (expected.getCellType()) {
                        case Cell.CELL_TYPE_BLANK:
                                assertEquals(msg, Cell.CELL_TYPE_BLANK, actual.getCellType());
@@ -132,32 +123,27 @@ public final class TestFormulasFromSpreadsheet extends TestCase {
                                break;
                        case Cell.CELL_TYPE_ERROR:
                                assertEquals(msg, Cell.CELL_TYPE_ERROR, actual.getCellType());
-                               if(false) { // TODO: fix ~45 functions which are currently returning incorrect error values
-                                       assertEquals(msg, expected.getErrorCellValue(), actual.getErrorValue());
-                               }
+                               assertEquals(msg, ErrorEval.getText(expected.getErrorCellValue()), ErrorEval.getText(actual.getErrorValue()));
                                break;
                        case Cell.CELL_TYPE_FORMULA: // will never be used, since we will call method after formula evaluation
                                throw new AssertionFailedError("Cannot expect formula as result of formula evaluation: " + msg);
                        case Cell.CELL_TYPE_NUMERIC:
                                assertEquals(msg, Cell.CELL_TYPE_NUMERIC, actual.getCellType());
                                TestMathX.assertEquals(msg, expected.getNumericCellValue(), actual.getNumberValue(), TestMathX.POS_ZERO, TestMathX.DIFF_TOLERANCE_FACTOR);
-//                             double delta = Math.abs(expected.getNumericCellValue()-actual.getNumberValue());
-//                             double pctExpected = Math.abs(0.00001*expected.getNumericCellValue());
-//                             assertTrue(msg, delta <= pctExpected);
                                break;
                        case Cell.CELL_TYPE_STRING:
                                assertEquals(msg, Cell.CELL_TYPE_STRING, actual.getCellType());
-                               assertEquals(msg, expected.getRichStringCellValue().getString(), actual.getRichTextStringValue().getString());
+                               assertEquals(msg, expected.getRichStringCellValue().getString(), actual.getStringValue());
                                break;
                }
        }
 
 
-       protected void setUp() throws Exception {
+       protected void setUp() {
                if (workbook == null) {
                        workbook = HSSFTestDataSamples.openSampleWorkbook(SS.FILENAME);
                        sheet = workbook.getSheetAt( 0 );
-                 }
+               }
                _functionFailureCount = 0;
                _functionSuccessCount = 0;
                _evaluationFailureCount = 0;
@@ -192,8 +178,7 @@ public final class TestFormulasFromSpreadsheet extends TestCase {
         * Typically pass <code>null</code> to test all functions
         */
        private void processFunctionGroup(int startRowIndex, String testFocusFunctionName) {
-               FormulaEvaluator evaluator = new FormulaEvaluator(sheet, workbook);
+               FormulaEvaluator evaluator = new FormulaEvaluator(workbook);
 
                int rowIndex = startRowIndex;
                while (true) {
@@ -263,7 +248,7 @@ public final class TestFormulasFromSpreadsheet extends TestCase {
                                result = Result.SOME_EVALUATIONS_FAILED;
                        }
                }
-               return result;
+               return result;
        }
 
        /**
index ef2340d4a4fe56b153400ac3eac3fe6239a53259..bac2e688624c3183cb794a2f4edd2549bbad7000 100755 (executable)
@@ -67,7 +67,7 @@ public final class TestPercentEval extends TestCase {
                cell.setCellFormula("B1%");
                row.createCell(1).setCellValue(50.0);
                
-               HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb);
+               HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
                CellValue cv;
                try {
                        cv = fe.evaluate(cell);
index 2ec7ad005a2e1df3f43ffc3303de243c7735140e..4ae90d067b8fa6288bf6cd52a0218da19547f371 100755 (executable)
@@ -34,6 +34,7 @@ public final class AllIndividualFunctionEvaluationTests {
                result.addTestSuite(TestDate.class);
                result.addTestSuite(TestFinanceLib.class);
                result.addTestSuite(TestIndex.class);
+               result.addTestSuite(TestIndexFunctionFromSpreadsheet.class);
                result.addTestSuite(TestIsBlank.class);
                result.addTestSuite(TestLen.class);
                result.addTestSuite(TestLookupFunctionsFromSpreadsheet.class);
index 4f0e5fff2cf93d95cb199630ec4e333286969d04..727f6b76454d8d84bd29987b83846ed8890ed0fb 100755 (executable)
@@ -27,14 +27,13 @@ import org.apache.poi.hssf.record.formula.eval.NumberEval;
 import org.apache.poi.hssf.record.formula.eval.ValueEval;
 /**
  * Tests for Excel function AVERAGE()
- * 
+ *
  * @author Josh Micich
  */
 public final class TestAverage extends TestCase {
 
-       
        private static Eval invokeAverage(Eval[] args) {
-               return new Average().evaluate(args, -1, (short)-1);
+               return AggregateFunction.AVERAGE.evaluate(args, -1, (short)-1);
        }
 
        private void confirmAverage(Eval[] args, double expected) {
@@ -48,56 +47,56 @@ public final class TestAverage extends TestCase {
                assertEquals(ErrorEval.class, result.getClass());
                assertEquals(expectedError.getErrorCode(), ((ErrorEval)result).getErrorCode());
        }
-       
+
        public void testBasic() {
-               
+
                ValueEval[] values = {
-                               new NumberEval(1),      
-                               new NumberEval(2),      
-                               new NumberEval(3),      
-                               new NumberEval(4),      
+                               new NumberEval(1),
+                               new NumberEval(2),
+                               new NumberEval(3),
+                               new NumberEval(4),
                };
-               
+
                confirmAverage(values, 2.5);
-               
+
                values = new ValueEval[] {
-                               new NumberEval(1),      
+                               new NumberEval(1),
                                new NumberEval(2),
                                BlankEval.INSTANCE,
-                               new NumberEval(3),      
+                               new NumberEval(3),
                                BlankEval.INSTANCE,
-                               new NumberEval(4),      
+                               new NumberEval(4),
                                BlankEval.INSTANCE,
                };
-               
+
                confirmAverage(values, 2.5);
        }
-       
+
        /**
         * Valid cases where values are not pure numbers
         */
        public void testUnusualArgs() {
                ValueEval[] values = {
-                               new NumberEval(1),      
-                               new NumberEval(2),      
-                               BoolEval.TRUE,  
-                               BoolEval.FALSE, 
+                               new NumberEval(1),
+                               new NumberEval(2),
+                               BoolEval.TRUE,
+                               BoolEval.FALSE,
                };
-               
+
                confirmAverage(values, 1.0);
-               
+
        }
 
        // currently disabled because MultiOperandNumericFunction.getNumberArray(Eval[], int, short)
        // does not handle error values properly yet
        public void XtestErrors() {
                ValueEval[] values = {
-                               new NumberEval(1),      
-                               ErrorEval.NAME_INVALID, 
-                               new NumberEval(3),      
-                               ErrorEval.DIV_ZERO,     
+                               new NumberEval(1),
+                               ErrorEval.NAME_INVALID,
+                               new NumberEval(3),
+                               ErrorEval.DIV_ZERO,
                };
                confirmAverage(values, ErrorEval.NAME_INVALID);
-                       
+
        }
 }
index 41d703e2cfb2f7ff6c910aff4ae39dfd6b29e29b..8e5a8f32a4a147d58a0576705d43c86a237a870f 100755 (executable)
@@ -21,13 +21,11 @@ import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;
 
 import org.apache.poi.hssf.HSSFTestDataSamples;
-import org.apache.poi.hssf.record.formula.RefPtg;
 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.Eval;
 import org.apache.poi.hssf.record.formula.eval.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.Ref2DEval;
 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.CountUtils.I_MatchPredicate;
@@ -270,7 +268,7 @@ public final class TestCountFuncs extends TestCase {
                int failureCount = 0;
                HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook(FILE_NAME);
                HSSFSheet sheet = wb.getSheetAt(0);
-               HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb);
+               HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
                int maxRow = sheet.getLastRowNum();
                for (int rowIx=START_ROW_IX; rowIx<maxRow; rowIx++) {
                        HSSFRow row = sheet.getRow(rowIx);
index 68bc43154ac17a1c1266b8413ab1cf212e0fadf6..0c08ef5a543111abc26568d8a8d2c43f3cbdcc04 100644 (file)
@@ -37,7 +37,7 @@ public final class TestDate extends TestCase {
         HSSFSheet sheet = wb.createSheet("new sheet");
         cell11 = sheet.createRow(0).createCell(0);
         cell11.setCellType(HSSFCell.CELL_TYPE_FORMULA);
-        evaluator = new HSSFFormulaEvaluator(sheet, wb);
+        evaluator = new HSSFFormulaEvaluator(wb);
     }
 
     /**
index 80c154596eab77b30eaa8d3940f854b3ff09bc38..95355733d245ee5f3b019dfb8b3097f15c7f7274 100755 (executable)
@@ -48,11 +48,11 @@ public final class TestIndex extends TestCase {
                double[] values = TEST_VALUES0;
                confirmAreaEval("C1:D6", values, 4, 1, 7);
                confirmAreaEval("C1:D6", values, 6, 2, 12);
-               confirmAreaEval("C1:D6", values, 3, -1, 5);
+               confirmAreaEval("C1:D6", values, 3, 1, 5);
                
                // now treat same data as 3 columns, 4 rows
                confirmAreaEval("C10:E13", values, 2, 2, 5); 
-               confirmAreaEval("C10:E13", values, 4, -1, 10);
+               confirmAreaEval("C10:E13", values, 4, 1, 10);
        }
        
        /**
diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestIndexFunctionFromSpreadsheet.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestIndexFunctionFromSpreadsheet.java
new file mode 100644 (file)
index 0000000..f28a646
--- /dev/null
@@ -0,0 +1,250 @@
+/* ====================================================================
+   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 java.io.PrintStream;
+
+import junit.framework.Assert;
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
+import org.apache.poi.hssf.HSSFTestDataSamples;
+import org.apache.poi.hssf.record.formula.eval.ErrorEval;
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.hssf.util.CellReference;
+import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
+
+/**
+ * Tests INDEX() as loaded from a test data spreadsheet.<p/>
+ *
+ * @author Josh Micich
+ */
+public final class TestIndexFunctionFromSpreadsheet extends TestCase {
+
+       private static final class Result {
+               public static final int SOME_EVALUATIONS_FAILED = -1;
+               public static final int ALL_EVALUATIONS_SUCCEEDED = +1;
+               public static final int NO_EVALUATIONS_FOUND = 0;
+       }
+
+       /**
+        * This class defines constants for navigating around the test data spreadsheet used for these tests.
+        */
+       private static final class SS {
+
+               /** Name of the test spreadsheet (found in the standard test data folder) */
+               public final static String FILENAME = "IndexFunctionTestCaseData.xls";
+
+               public static final int COLUMN_INDEX_EVALUATION = 2; // Column 'C'
+               public static final int COLUMN_INDEX_EXPECTED_RESULT = 3; // Column 'D'
+
+       }
+
+       // Note - multiple failures are aggregated before ending.
+       // If one or more functions fail, a single AssertionFailedError is thrown at the end
+       private int _evaluationFailureCount;
+       private int _evaluationSuccessCount;
+
+
+
+       private static void confirmExpectedResult(String msg, HSSFCell expected, HSSFFormulaEvaluator.CellValue actual) {
+               if (expected == null) {
+                       throw new AssertionFailedError(msg + " - Bad setup data expected value is null");
+               }
+               if(actual == null) {
+                       throw new AssertionFailedError(msg + " - actual value was null");
+               }
+               if(expected.getCellType() == HSSFCell.CELL_TYPE_ERROR) {
+                       confirmErrorResult(msg, expected.getErrorCellValue(), actual);
+                       return;
+               }
+               if(actual.getCellType() == HSSFCell.CELL_TYPE_ERROR) {
+                       throw unexpectedError(msg, expected, actual.getErrorValue());
+               }
+               if(actual.getCellType() != expected.getCellType()) {
+                       throw wrongTypeError(msg, expected, actual);
+               }
+
+
+               switch (expected.getCellType()) {
+                       case HSSFCell.CELL_TYPE_BOOLEAN:
+                               assertEquals(msg, expected.getBooleanCellValue(), actual.getBooleanValue());
+                               break;
+                       case HSSFCell.CELL_TYPE_FORMULA: // will never be used, since we will call method after formula evaluation
+                               throw new AssertionFailedError("Cannot expect formula as result of formula evaluation: " + msg);
+                       case HSSFCell.CELL_TYPE_NUMERIC:
+                               assertEquals(expected.getNumericCellValue(), actual.getNumberValue(), 0.0);
+                               break;
+                       case HSSFCell.CELL_TYPE_STRING:
+                               assertEquals(msg, expected.getRichStringCellValue().getString(), actual.getStringValue());
+                               break;
+               }
+       }
+
+
+       private static AssertionFailedError wrongTypeError(String msgPrefix, HSSFCell expectedCell, CellValue actualValue) {
+               return new AssertionFailedError(msgPrefix + " Result type mismatch. Evaluated result was "
+                               + actualValue.formatAsString()
+                               + " but the expected result was "
+                               + formatValue(expectedCell)
+                               );
+       }
+       private static AssertionFailedError unexpectedError(String msgPrefix, HSSFCell expected, int actualErrorCode) {
+               return new AssertionFailedError(msgPrefix + " Error code ("
+                               + ErrorEval.getText(actualErrorCode)
+                               + ") was evaluated, but the expected result was "
+                               + formatValue(expected)
+                               );
+       }
+
+
+       private static void confirmErrorResult(String msgPrefix, int expectedErrorCode, CellValue actual) {
+               if(actual.getCellType() != HSSFCell.CELL_TYPE_ERROR) {
+                       throw new AssertionFailedError(msgPrefix + " Expected cell error ("
+                                       + ErrorEval.getText(expectedErrorCode) + ") but actual value was "
+                                       + actual.formatAsString());
+               }
+               if(expectedErrorCode != actual.getErrorValue()) {
+                       throw new AssertionFailedError(msgPrefix + " Expected cell error code ("
+                                       + ErrorEval.getText(expectedErrorCode)
+                                       + ") but actual error code was ("
+                                       + ErrorEval.getText(actual.getErrorValue())
+                                       + ")");
+               }
+       }
+
+
+       private static String formatValue(HSSFCell expecedCell) {
+               switch (expecedCell.getCellType()) {
+                       case HSSFCell.CELL_TYPE_BLANK: return "<blank>";
+                       case HSSFCell.CELL_TYPE_BOOLEAN: return String.valueOf(expecedCell.getBooleanCellValue());
+                       case HSSFCell.CELL_TYPE_NUMERIC: return String.valueOf(expecedCell.getNumericCellValue());
+                       case HSSFCell.CELL_TYPE_STRING: return expecedCell.getRichStringCellValue().getString();
+               }
+               throw new RuntimeException("Unexpected cell type of expected value (" + expecedCell.getCellType() + ")");
+       }
+
+
+       protected void setUp() {
+               _evaluationFailureCount = 0;
+               _evaluationSuccessCount = 0;
+       }
+
+       public void testFunctionsFromTestSpreadsheet() {
+               HSSFWorkbook workbook = HSSFTestDataSamples.openSampleWorkbook(SS.FILENAME);
+
+               processTestSheet(workbook, workbook.getSheetName(0));
+
+               // confirm results
+               String successMsg = "There were "
+                               + _evaluationSuccessCount + " function(s) without error";
+               if(_evaluationFailureCount > 0) {
+                       String msg = _evaluationFailureCount + " evaluation(s) failed.  " + successMsg;
+                       throw new AssertionFailedError(msg);
+               }
+               if(false) { // normally no output for successful tests
+                       System.out.println(getClass().getName() + ": " + successMsg);
+               }
+       }
+
+       private void processTestSheet(HSSFWorkbook workbook, String sheetName) {
+               HSSFSheet sheet = workbook.getSheetAt(0);
+               HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(workbook);
+               int maxRows = sheet.getLastRowNum()+1;
+               int result = Result.NO_EVALUATIONS_FOUND; // so far
+
+               for(int rowIndex=0; rowIndex<maxRows; rowIndex++) {
+                       HSSFRow r = sheet.getRow(rowIndex);
+                       if(r == null) {
+                               continue;
+                       }
+                       HSSFCell c = r.getCell(SS.COLUMN_INDEX_EVALUATION);
+                       if (c == null || c.getCellType() != HSSFCell.CELL_TYPE_FORMULA) {
+                               continue;
+                       }
+                       CellValue actualValue = evaluator.evaluate(c);
+                       HSSFCell expectedValueCell = r.getCell(SS.COLUMN_INDEX_EXPECTED_RESULT);
+
+                       String msgPrefix = formatTestCaseDetails(sheetName, r.getRowNum(), c);
+                       try {
+                               confirmExpectedResult(msgPrefix, expectedValueCell, actualValue);
+                               _evaluationSuccessCount ++;
+                               if(result != Result.SOME_EVALUATIONS_FAILED) {
+                                       result = Result.ALL_EVALUATIONS_SUCCEEDED;
+                               }
+                       } catch (RuntimeException e) {
+                               _evaluationFailureCount ++;
+                               printShortStackTrace(System.err, e);
+                               result = Result.SOME_EVALUATIONS_FAILED;
+                       } catch (AssertionFailedError e) {
+                               _evaluationFailureCount ++;
+                               printShortStackTrace(System.err, e);
+                               result = Result.SOME_EVALUATIONS_FAILED;
+                       }
+
+               }
+       }
+
+
+       private static String formatTestCaseDetails(String sheetName, int rowNum, HSSFCell c) {
+
+               StringBuffer sb = new StringBuffer();
+               CellReference cr = new CellReference(sheetName, rowNum, c.getCellNum(), false, false);
+               sb.append(cr.formatAsString());
+               sb.append(" {=").append(c.getCellFormula()).append("}");
+               return sb.toString();
+       }
+
+       /**
+        * Useful to keep output concise when expecting many failures to be reported by this test case
+        */
+       private static void printShortStackTrace(PrintStream ps, Throwable e) {
+               StackTraceElement[] stes = e.getStackTrace();
+
+               int startIx = 0;
+               // skip any top frames inside junit.framework.Assert
+               while(startIx<stes.length) {
+                       if(!stes[startIx].getClassName().equals(Assert.class.getName())) {
+                               break;
+                       }
+                       startIx++;
+               }
+               // skip bottom frames (part of junit framework)
+               int endIx = startIx+1;
+               while(endIx < stes.length) {
+                       if(stes[endIx].getClassName().equals(TestCase.class.getName())) {
+                               break;
+                       }
+                       endIx++;
+               }
+               if(startIx >= endIx) {
+                       // something went wrong. just print the whole stack trace
+                       e.printStackTrace(ps);
+               }
+               endIx -= 4; // skip 4 frames of reflection invocation
+               ps.println(e.toString());
+               for(int i=startIx; i<endIx; i++) {
+                       ps.println("\tat " + stes[i].toString());
+               }
+       }
+}
+
index 62cc32e95706130044b6afbbf6b775ae1ebfaae4..50f92dfecf837a34c779a7ae155963cea51623db 100755 (executable)
@@ -44,7 +44,7 @@ public final class TestIsBlank extends TestCase {
          
         cell.setCellFormula("isblank(Sheet2!A1:A1)");
         
-        HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet1, wb);
+        HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
         CellValue result = fe.evaluate(cell);
         assertEquals(HSSFCell.CELL_TYPE_BOOLEAN, result.getCellType());
         assertEquals(true, result.getBooleanValue());
index a96fb4e2b09c730489f5e610d4ad749991847d66..459d2fb555b029c066b8be08cf69f536ae0f20c7 100755 (executable)
@@ -35,7 +35,7 @@ public final class TestLen extends TestCase {
        
        private static Eval invokeLen(Eval text) {
                Eval[] args = new Eval[] { text, };
-               return new Len().evaluate(args, -1, (short)-1);
+               return TextFunction.LEN.evaluate(args, -1, (short)-1);
        }
 
        private void confirmLen(Eval text, int expected) {
index fe0bab0a0d358315bb1c105a79c3f90565812156..49d67a8d4e36a8e9c531c210db0d919ef5ff4d38 100644 (file)
@@ -37,33 +37,33 @@ import org.apache.poi.hssf.util.CellReference;
  * Tests lookup functions (VLOOKUP, HLOOKUP, LOOKUP, MATCH) as loaded from a test data spreadsheet.<p/>
  * These tests have been separated from the common function and operator tests because the lookup
  * functions have more complex test cases and test data setup.
- * 
+ *
  * Tests for bug fixes and specific/tricky behaviour can be found in the corresponding test class
  * (<tt>TestXxxx</tt>) of the target (<tt>Xxxx</tt>) implementor, where execution can be observed
  *  more easily.
- * 
+ *
  * @author Josh Micich
  */
 public final class TestLookupFunctionsFromSpreadsheet extends TestCase {
-       
+
        private static final class Result {
                public static final int SOME_EVALUATIONS_FAILED = -1;
                public static final int ALL_EVALUATIONS_SUCCEEDED = +1;
                public static final int NO_EVALUATIONS_FOUND = 0;
        }
 
-       /** 
+       /**
         * This class defines constants for navigating around the test data spreadsheet used for these tests.
         */
        private static final class SS {
-               
+
                /** Name of the test spreadsheet (found in the standard test data folder) */
                public final static String FILENAME = "LookupFunctionsTestCaseData.xls";
-               
+
                /** Name of the first sheet in the spreadsheet (contains comments) */
                public final static String README_SHEET_NAME = "Read Me";
-               
-               
+
+
                /** Row (zero-based) in each sheet where the evaluation cases start.   */
                public static final int START_TEST_CASES_ROW_INDEX = 4; // Row '5'
                /**  Index of the column that contains the function names */
@@ -71,15 +71,15 @@ public final class TestLookupFunctionsFromSpreadsheet extends TestCase {
                public static final int COLUMN_INDEX_EVALUATION = 1; // Column 'B'
                public static final int COLUMN_INDEX_EXPECTED_RESULT = 2; // Column 'C'
                public static final int COLUMN_ROW_COMMENT = 3; // Column 'D'
-       
+
                /** Used to indicate when there are no more test cases on the current sheet   */
                public static final String TEST_CASES_END_MARKER = "<end>";
                /** Used to indicate that the test on the current row should be ignored */
                public static final String SKIP_CURRENT_TEST_CASE_MARKER = "<skip>";
-       
+
        }
 
-       // Note - multiple failures are aggregated before ending.  
+       // Note - multiple failures are aggregated before ending.
        // If one or more functions fail, a single AssertionFailedError is thrown at the end
        private int _sheetFailureCount;
        private int _sheetSuccessCount;
@@ -105,19 +105,19 @@ public final class TestLookupFunctionsFromSpreadsheet extends TestCase {
                if(actual.getCellType() != expected.getCellType()) {
                        throw wrongTypeError(msg, expected, actual);
                }
-               
-               
+
+
                switch (expected.getCellType()) {
                        case HSSFCell.CELL_TYPE_BOOLEAN:
                                assertEquals(msg, expected.getBooleanCellValue(), actual.getBooleanValue());
                                break;
                        case HSSFCell.CELL_TYPE_FORMULA: // will never be used, since we will call method after formula evaluation
-                               throw new AssertionFailedError("Cannot expect formula as result of formula evaluation: " + msg);
+                               throw new IllegalStateException("Cannot expect formula as result of formula evaluation: " + msg);
                        case HSSFCell.CELL_TYPE_NUMERIC:
                                assertEquals(expected.getNumericCellValue(), actual.getNumberValue(), 0.0);
                                break;
                        case HSSFCell.CELL_TYPE_STRING:
-                               assertEquals(msg, expected.getRichStringCellValue().getString(), actual.getRichTextStringValue().getString());
+                               assertEquals(msg, expected.getRichStringCellValue().getString(), actual.getStringValue());
                                break;
                }
        }
@@ -125,14 +125,14 @@ public final class TestLookupFunctionsFromSpreadsheet extends TestCase {
 
        private static AssertionFailedError wrongTypeError(String msgPrefix, HSSFCell expectedCell, CellValue actualValue) {
                return new AssertionFailedError(msgPrefix + " Result type mismatch. Evaluated result was "
-                               + formatValue(actualValue) 
+                               + actualValue.formatAsString()
                                + " but the expected result was "
                                + formatValue(expectedCell)
                                );
        }
        private static AssertionFailedError unexpectedError(String msgPrefix, HSSFCell expected, int actualErrorCode) {
                return new AssertionFailedError(msgPrefix + " Error code ("
-                               + ErrorEval.getText(actualErrorCode) 
+                               + ErrorEval.getText(actualErrorCode)
                                + ") was evaluated, but the expected result was "
                                + formatValue(expected)
                                );
@@ -141,15 +141,15 @@ public final class TestLookupFunctionsFromSpreadsheet extends TestCase {
 
        private static void confirmErrorResult(String msgPrefix, int expectedErrorCode, CellValue actual) {
                if(actual.getCellType() != HSSFCell.CELL_TYPE_ERROR) {
-                       throw new AssertionFailedError(msgPrefix + " Expected cell error (" 
+                       throw new AssertionFailedError(msgPrefix + " Expected cell error ("
                                        + ErrorEval.getText(expectedErrorCode) + ") but actual value was "
-                                       + formatValue(actual));
+                                       + actual.formatAsString());
                }
                if(expectedErrorCode != actual.getErrorValue()) {
-                       throw new AssertionFailedError(msgPrefix + " Expected cell error code (" 
-                                       + ErrorEval.getText(expectedErrorCode) 
+                       throw new AssertionFailedError(msgPrefix + " Expected cell error code ("
+                                       + ErrorEval.getText(expectedErrorCode)
                                        + ") but actual error code was ("
-                                       + ErrorEval.getText(actual.getErrorValue()) 
+                                       + ErrorEval.getText(actual.getErrorValue())
                                        + ")");
                }
        }
@@ -164,57 +164,48 @@ public final class TestLookupFunctionsFromSpreadsheet extends TestCase {
                }
                throw new RuntimeException("Unexpected cell type of expected value (" + expecedCell.getCellType() + ")");
        }
-       private static String formatValue(CellValue actual) {
-               switch (actual.getCellType()) {
-                       case HSSFCell.CELL_TYPE_BLANK: return "<blank>";
-                       case HSSFCell.CELL_TYPE_BOOLEAN: return String.valueOf(actual.getBooleanValue());
-                       case HSSFCell.CELL_TYPE_NUMERIC: return String.valueOf(actual.getNumberValue());
-                       case HSSFCell.CELL_TYPE_STRING: return actual.getRichTextStringValue().getString();
-               }
-               throw new RuntimeException("Unexpected cell type of evaluated value (" + actual.getCellType() + ")");
-       }
 
 
-       protected void setUp() throws Exception {
+       protected void setUp() {
                _sheetFailureCount = 0;
                _sheetSuccessCount = 0;
                _evaluationFailureCount = 0;
                _evaluationSuccessCount = 0;
        }
-       
+
        public void testFunctionsFromTestSpreadsheet() {
                HSSFWorkbook workbook = HSSFTestDataSamples.openSampleWorkbook(SS.FILENAME);
-       
+
                confirmReadMeSheet(workbook);
                int nSheets = workbook.getNumberOfSheets();
                for(int i=1; i< nSheets; i++) {
                        int sheetResult = processTestSheet(workbook, i, workbook.getSheetName(i));
                        switch(sheetResult) {
-                               case Result.ALL_EVALUATIONS_SUCCEEDED: _sheetSuccessCount ++; break; 
-                               case Result.SOME_EVALUATIONS_FAILED: _sheetFailureCount ++; break; 
+                               case Result.ALL_EVALUATIONS_SUCCEEDED: _sheetSuccessCount ++; break;
+                               case Result.SOME_EVALUATIONS_FAILED: _sheetFailureCount ++; break;
                        }
                }
-               
+
                // confirm results
-               String successMsg = "There were " 
+               String successMsg = "There were "
                                + _sheetSuccessCount + " successful sheets(s) and "
                                + _evaluationSuccessCount + " function(s) without error";
-               if(_sheetFailureCount > 0) {
+               if(_sheetFailureCount > 0) {
                        String msg = _sheetFailureCount + " sheets(s) failed with "
                        + _evaluationFailureCount + " evaluation(s).  " + successMsg;
                        throw new AssertionFailedError(msg);
                }
-               if(false) { // normally no output for successful tests
-                       System.out.println(getClass().getName() + ": " + successMsg);
-               }
+               if(false) { // normally no output for successful tests
+                       System.out.println(getClass().getName() + ": " + successMsg);
+               }
        }
 
        private int processTestSheet(HSSFWorkbook workbook, int sheetIndex, String sheetName) {
                HSSFSheet sheet = workbook.getSheetAt(sheetIndex);
-               HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, workbook);
+               HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(workbook);
                int maxRows = sheet.getLastRowNum()+1;
                int result = Result.NO_EVALUATIONS_FOUND; // so far
-               
+
                String currentGroupComment = null;
                for(int rowIndex=SS.START_TEST_CASES_ROW_INDEX; rowIndex<maxRows; rowIndex++) {
                        HSSFRow r = sheet.getRow(rowIndex);
@@ -240,7 +231,7 @@ public final class TestLookupFunctionsFromSpreadsheet extends TestCase {
                        CellValue actualValue = evaluator.evaluate(c);
                        HSSFCell expectedValueCell = r.getCell(SS.COLUMN_INDEX_EXPECTED_RESULT);
                        String rowComment = getRowCommentColumnValue(r);
-                       
+
                        String msgPrefix = formatTestCaseDetails(sheetName, r.getRowNum(), c, currentGroupComment, rowComment);
                        try {
                                confirmExpectedResult(msgPrefix, expectedValueCell, actualValue);
@@ -257,22 +248,22 @@ public final class TestLookupFunctionsFromSpreadsheet extends TestCase {
                                printShortStackTrace(System.err, e);
                                result = Result.SOME_EVALUATIONS_FAILED;
                        }
-                       
+
                }
-               throw new RuntimeException("Missing end marker '" + SS.TEST_CASES_END_MARKER 
+               throw new RuntimeException("Missing end marker '" + SS.TEST_CASES_END_MARKER
                                + "' on sheet '" + sheetName + "'");
-               
+
        }
 
 
        private static String formatTestCaseDetails(String sheetName, int rowNum, HSSFCell c, String currentGroupComment,
                        String rowComment) {
-               
+
                StringBuffer sb = new StringBuffer();
                CellReference cr = new CellReference(sheetName, rowNum, c.getCellNum(), false, false);
                sb.append(cr.formatAsString());
                sb.append(" {=").append(c.getCellFormula()).append("}");
-               
+
                if(currentGroupComment != null) {
                        sb.append(" '");
                        sb.append(currentGroupComment);
@@ -288,13 +279,13 @@ public final class TestLookupFunctionsFromSpreadsheet extends TestCase {
                                sb.append("' ");
                        }
                }
-               
+
                return sb.toString();
        }
 
        /**
-        * Asserts that the 'read me' comment page exists, and has this class' name in one of the 
-        * cells.  This back-link is to make it easy to find this class if a reader encounters the 
+        * Asserts that the 'read me' comment page exists, and has this class' name in one of the
+        * cells.  This back-link is to make it easy to find this class if a reader encounters the
         * spreadsheet first.
         */
        private void confirmReadMeSheet(HSSFWorkbook workbook) {
@@ -313,7 +304,7 @@ public final class TestLookupFunctionsFromSpreadsheet extends TestCase {
         */
        private static void printShortStackTrace(PrintStream ps, Throwable e) {
                StackTraceElement[] stes = e.getStackTrace();
-               
+
                int startIx = 0;
                // skip any top frames inside junit.framework.Assert
                while(startIx<stes.length) {
@@ -339,17 +330,17 @@ public final class TestLookupFunctionsFromSpreadsheet extends TestCase {
                for(int i=startIx; i<endIx; i++) {
                        ps.println("\tat " + stes[i].toString());
                }
-               
+
        }
 
        private static String getRowCommentColumnValue(HSSFRow r) {
                return getCellTextValue(r, SS.COLUMN_ROW_COMMENT, "row comment");
        }
-       
+
        private static String getMarkerColumnValue(HSSFRow r) {
                return getCellTextValue(r, SS.COLUMN_INDEX_MARKER, "marker");
        }
-       
+
        /**
         * @return <code>null</code> if cell is missing, empty or blank
         */
@@ -367,7 +358,7 @@ public final class TestLookupFunctionsFromSpreadsheet extends TestCase {
                if(cell.getCellType() == HSSFCell.CELL_TYPE_STRING) {
                        return cell.getRichStringCellValue().getString();
                }
-               
+
                throw new RuntimeException("Bad cell type for '" + columnName + "' column: ("
                                + cell.getCellType() + ") row (" + (r.getRowNum() +1) + ")");
        }
index 76cd1056edef3d26bb866b4a78b9bd9e1d25764c..81e78e737c4f4a4914b31fb929386149b638e82b 100755 (executable)
@@ -38,7 +38,7 @@ public final class TestMid extends TestCase {
 
        private static Eval invokeMid(Eval text, Eval startPos, Eval numChars) {
                Eval[] args = new Eval[] { text, startPos, numChars, };
-               return new Mid().evaluate(args, -1, (short)-1);
+               return TextFunction.MID.evaluate(args, -1, (short)-1);
        }
 
        private void confirmMid(Eval text, Eval startPos, Eval numChars, String expected) {
index 2fecef7046d3a38b5ff48afb1da33a883df96eb7..9a4cded9af68cfbac3b6ae2a793864724487e52e 100644 (file)
@@ -36,7 +36,7 @@ public final class TestPmt extends TestCase {
                assertEquals(expected, ne.getNumberValue(), 0.00005);
        }
        private static Eval invoke(Eval[] args) {
-               return new Pmt().evaluate(args, -1, (short)-1);
+               return FinanceFunction.PMT.evaluate(args, -1, (short)-1);
        }
        /**
         * Invocation when not expecting an error result
index a6ce345aeb1fe7e32347a9079283183cfae16d9f..9dbbb438e0f2a01c71ce3afa4290338b08ab5c88 100755 (executable)
 
 package org.apache.poi.hssf.record.formula.functions;
 
+import junit.framework.TestCase;
+
 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.NumberEval;
 import org.apache.poi.hssf.record.formula.eval.StringEval;
 
-import junit.framework.TestCase;
-
 /**
  * Test cases for ROUND(), ROUNDUP(), ROUNDDOWN()
  * 
  * @author Josh Micich
  */
 public final class TestRoundFuncs extends TestCase {
+       private static final NumericFunction F = null;
        public void testRounddownWithStringArg() {
                
                Eval strArg = new StringEval("abc");
                Eval[] args = { strArg, new NumberEval(2), };
-               Eval result = new Rounddown().evaluate(args, -1, (short)-1);
+               Eval result = F.ROUNDDOWN.evaluate(args, -1, (short)-1);
                assertEquals(ErrorEval.VALUE_INVALID, result);
        }
        
@@ -42,7 +43,7 @@ public final class TestRoundFuncs extends TestCase {
                
                Eval strArg = new StringEval("abc");
                Eval[] args = { strArg, new NumberEval(2), };
-               Eval result = new Roundup().evaluate(args, -1, (short)-1);
+               Eval result = F.ROUNDUP.evaluate(args, -1, (short)-1);
                assertEquals(ErrorEval.VALUE_INVALID, result);
        }
        
index 237366baf0c64d420dea0d865a257cc13ff7b777..3562a67789c9c41634235323603ac4f538953dde 100644 (file)
  */
 package org.apache.poi.hssf.record.formula.functions;
 
+import junit.framework.AssertionFailedError;
+
+import org.apache.poi.hssf.record.formula.eval.ErrorEval;
+import org.apache.poi.hssf.record.formula.eval.EvaluationException;
+
 
 /**
  * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
@@ -181,49 +186,53 @@ public class TestStatsLib extends AbstractNumericTestCase {
     }
 
     public void testMode() {
-        double[] v = null;
+        double[] v;
         double d, x = 0;
         
         v = new double[] {1,2,3,4,5,6,7,8,9,10};
-        d = StatsLib.mode(v);
-        x = Double.NaN;
-        assertEquals("mode ", x, d);
+        confirmMode(v, null);
         
         v = new double[] {1,1,1,1,1,1,1,1,1,1};
-        d = StatsLib.mode(v);
-        x = 1;
-        assertEquals("mode ", x, d);
+        confirmMode(v, 1.0);
         
         v = new double[] {0,0,0,0,0,0,0,0,0,0};
-        d = StatsLib.mode(v);
-        x = 0;
-        assertEquals("mode ", x, d);
+        confirmMode(v, 0.0);
         
         v = new double[] {1,2,1,2,1,2,1,2,1,2};
-        d = StatsLib.mode(v);
-        x = 1;
-        assertEquals("mode ", x, d);
+        confirmMode(v, 1.0);
         
         v = new double[] {123.12,33.3333,2d/3d,5.37828,0.999};
-        d = StatsLib.mode(v);
-        x = Double.NaN;
-        assertEquals("mode ", x, d);
+        confirmMode(v, null);
         
         v = new double[] {-1,-2,-3,-4,-5,-6,-7,-8,-9,-10};
-        d = StatsLib.mode(v);
-        x = Double.NaN;
-        assertEquals("mode ", x, d);
+        confirmMode(v, null);
         
         v = new double[] {1,2,3,4,1,1,1,1,0,0,0,0,0};
-        d = StatsLib.mode(v);
-        x = 1;
-        assertEquals("mode ", x, d);
+        confirmMode(v, 1.0);
         
         v = new double[] {0,1,2,3,4,1,1,1,0,0,0,0,1};
-        d = StatsLib.mode(v);
-        x = 0;
-        assertEquals("mode ", x, d);
+        confirmMode(v, 0.0);
+    }
+    private static void confirmMode(double[] v, double expectedResult) {
+       confirmMode(v, new Double(expectedResult));
+    }
+    private static void confirmMode(double[] v, Double expectedResult) {
+       double actual;
+               try {
+                       actual = Mode.evaluate(v);
+                       if (expectedResult == null) {
+                               throw new AssertionFailedError("Expected N/A exception was not thrown");
+                       }
+               } catch (EvaluationException e) {
+                       if (expectedResult == null) {
+                               assertEquals(ErrorEval.NA, e.getErrorEval());
+                               return;
+                       }
+                       throw new RuntimeException(e);
+               }
+       assertEquals("mode", expectedResult.doubleValue(), actual);
     }
+    
 
     public void testStddev() {
         double[] v = null;
index 076ac1fc7e4357e983febd80ac62f3c434e6e260..1c65c9ca6d95fcb6b4c740771035190699cf01c1 100755 (executable)
@@ -35,7 +35,7 @@ public final class TestTrim extends TestCase {
        
        private static Eval invokeTrim(Eval text) {
                Eval[] args = new Eval[] { text, };
-               return new Trim().evaluate(args, -1, (short)-1);
+               return TextFunction.TRIM.evaluate(args, -1, (short)-1);
        }
 
        private void confirmTrim(Eval text, String expected) {
index c128c773c55ad73a189b3bb1dc3c1d01c4b85eb2..a43764e8521e5c446b1deafe0ae37b6cbabb6f85 100644 (file)
@@ -43,10 +43,9 @@ public final class TestBug42464 extends TestCase {
        }
        
        private static void process(HSSFWorkbook wb) {
+               HSSFFormulaEvaluator eval =     new HSSFFormulaEvaluator(wb);
                for(int i=0; i<wb.getNumberOfSheets(); i++) {
                        HSSFSheet s = wb.getSheetAt(i);
-                       HSSFFormulaEvaluator eval =
-                               new HSSFFormulaEvaluator(s, wb);
                        
                        Iterator it = s.rowIterator();
                        while(it.hasNext()) {
index 1989d7d4e7be6b3d15e0269d8bc40a7dc9c31f13..3dbf5907ff3325d272c00314e086b4c6c97e7e2a 100644 (file)
@@ -39,7 +39,7 @@ public final class TestBug43093 extends TestCase {
        }
 
        public void testBug43093() {
-               HSSFWorkbook     xlw    = new HSSFWorkbook();
+               HSSFWorkbook xlw = new HSSFWorkbook();
 
                addNewSheetWithCellsA1toD4(xlw, 1);
                addNewSheetWithCellsA1toD4(xlw, 2);
@@ -51,7 +51,7 @@ public final class TestBug43093 extends TestCase {
                HSSFCell  s2E4 = s2r3.createCell(4);
                s2E4.setCellFormula("SUM(s3!B2:C3)");
 
-               HSSFFormulaEvaluator eva = new HSSFFormulaEvaluator(s2, xlw);
+               HSSFFormulaEvaluator eva = new HSSFFormulaEvaluator(xlw);
                double d = eva.evaluate(s2E4).getNumberValue();
 
                // internalEvaluate(...) Area3DEval.: 311+312+321+322 expected
index 200e62884abcab9810cdeb825998c3b1ba3aaa0e..8954656c6ed3ab75d6cc9364efde8ecc8f6b6c81 100644 (file)
@@ -963,7 +963,7 @@ public final class TestBugs extends TestCase {
         writeOutAndReadBack(wb);
         assertTrue("no errors writing sample xls", true);
     }
-    
+
     /**
      * Problems with extracting check boxes from
      *  HSSFObjectData
@@ -975,35 +975,35 @@ public final class TestBugs extends TestCase {
         // Take a look at the embeded objects
         List objects = wb.getAllEmbeddedObjects();
         assertEquals(1, objects.size());
-        
+
         HSSFObjectData obj = (HSSFObjectData)objects.get(0);
         assertNotNull(obj);
-        
+
         // Peek inside the underlying record
         EmbeddedObjectRefSubRecord rec = obj.findObjectRecord();
         assertNotNull(rec);
-        
+
         assertEquals(32, rec.field_1_stream_id_offset);
         assertEquals(0, rec.field_6_stream_id); // WRONG!
         assertEquals("Forms.CheckBox.1", rec.field_5_ole_classname);
         assertEquals(12, rec.remainingBytes.length);
-        
+
         // Doesn't have a directory
         assertFalse(obj.hasDirectoryEntry());
         assertNotNull(obj.getObjectData());
         assertEquals(12, obj.getObjectData().length);
         assertEquals("Forms.CheckBox.1", obj.getOLE2ClassName());
-        
+
         try {
             obj.getDirectory();
             fail();
         } catch(FileNotFoundException e) {
-               // expectd during successful test
+            // expectd during successful test
         } catch (IOException e) {
-                       throw new RuntimeException(e);
-               }
+            throw new RuntimeException(e);
+        }
     }
-    
+
     /**
      * Test that we can delete sheets without
      *  breaking the build in named ranges
@@ -1013,73 +1013,73 @@ public final class TestBugs extends TestCase {
         HSSFWorkbook wb = openSample("30978-alt.xls");
         assertEquals(1, wb.getNumberOfNames());
         assertEquals(3, wb.getNumberOfSheets());
-        
+
         // Check all names fit within range, and use
         //  DeletedArea3DPtg
         Workbook w = wb.getWorkbook();
         for(int i=0; i<w.getNumNames(); i++) {
             NameRecord r = w.getNameRecord(i);
             assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets());
-            
+
             Ptg[] nd = r.getNameDefinition();
             assertEquals(1, nd.length);
             assertTrue(nd[0] instanceof DeletedArea3DPtg);
         }
-        
-        
+
+
         // Delete the 2nd sheet
         wb.removeSheetAt(1);
-        
-        
+
+
         // Re-check
         assertEquals(1, wb.getNumberOfNames());
         assertEquals(2, wb.getNumberOfSheets());
-        
+
         for(int i=0; i<w.getNumNames(); i++) {
             NameRecord r = w.getNameRecord(i);
             assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets());
-            
+
             Ptg[] nd = r.getNameDefinition();
             assertEquals(1, nd.length);
             assertTrue(nd[0] instanceof DeletedArea3DPtg);
         }
-        
-        
+
+
         // Save and re-load
         wb = writeOutAndReadBack(wb);
         w = wb.getWorkbook();
-        
+
         assertEquals(1, wb.getNumberOfNames());
         assertEquals(2, wb.getNumberOfSheets());
-        
+
         for(int i=0; i<w.getNumNames(); i++) {
             NameRecord r = w.getNameRecord(i);
             assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets());
-            
+
             Ptg[] nd = r.getNameDefinition();
             assertEquals(1, nd.length);
             assertTrue(nd[0] instanceof DeletedArea3DPtg);
         }
     }
-    
+
     /**
      * Test that fonts get added properly
      */
     public void test45338() {
         HSSFWorkbook wb = new HSSFWorkbook();
         assertEquals(4, wb.getNumberOfFonts());
-        
+
         HSSFSheet s = wb.createSheet();
         s.createRow(0);
         s.createRow(1);
         HSSFCell c1 = s.getRow(0).createCell(0);
         HSSFCell c2 = s.getRow(1).createCell(0);
-        
+
         assertEquals(4, wb.getNumberOfFonts());
-        
+
         HSSFFont f1 = wb.getFontAt((short)0);
         assertEquals(400, f1.getBoldweight());
-        
+
         // Check that asking for the same font
         //  multiple times gives you the same thing.
         // Otherwise, our tests wouldn't work!
@@ -1096,22 +1096,22 @@ public final class TestBugs extends TestCase {
                 !=
                 wb.getFontAt((short)2)
         );
-        
+
         // Look for a new font we have
         //  yet to add
         assertNull(
             wb.findFont(
-                (short)11, (short)123, (short)22, 
+                (short)11, (short)123, (short)22,
                 "Thingy", false, true, (short)2, (byte)2
             )
         );
-        
+
         HSSFFont nf = wb.createFont();
         assertEquals(5, wb.getNumberOfFonts());
-        
+
         assertEquals(5, nf.getIndex());
         assertEquals(nf, wb.getFontAt((short)5));
-        
+
         nf.setBoldweight((short)11);
         nf.setColor((short)123);
         nf.setFontHeight((short)22);
@@ -1120,32 +1120,32 @@ public final class TestBugs extends TestCase {
         nf.setStrikeout(true);
         nf.setTypeOffset((short)2);
         nf.setUnderline((byte)2);
-        
+
         assertEquals(5, wb.getNumberOfFonts());
         assertEquals(nf, wb.getFontAt((short)5));
-        
+
         // Find it now
         assertNotNull(
             wb.findFont(
-                (short)11, (short)123, (short)22, 
+                (short)11, (short)123, (short)22,
                 "Thingy", false, true, (short)2, (byte)2
             )
         );
         assertEquals(
             5,
             wb.findFont(
-                   (short)11, (short)123, (short)22, 
+                   (short)11, (short)123, (short)22,
                    "Thingy", false, true, (short)2, (byte)2
                ).getIndex()
         );
         assertEquals(nf,
                wb.findFont(
-                   (short)11, (short)123, (short)22, 
+                   (short)11, (short)123, (short)22,
                    "Thingy", false, true, (short)2, (byte)2
                )
         );
     }
-    
+
     /**
      * From the mailing list - ensure we can handle a formula
      *  containing a zip code, eg ="70164"
@@ -1162,63 +1162,59 @@ public final class TestBugs extends TestCase {
         c1.setCellFormula("70164");
         c2.setCellFormula("\"70164\"");
         c3.setCellFormula("\"90210\"");
-        
+
         // Check the formulas
         assertEquals("70164.0", c1.getCellFormula());
         assertEquals("\"70164\"", c2.getCellFormula());
-        
+
         // And check the values - blank
-        assertEquals(0.0, c1.getNumericCellValue(), 0.00001);
-        assertEquals("", c1.getRichStringCellValue().getString());
-        assertEquals(0.0, c2.getNumericCellValue(), 0.00001);
-        assertEquals("", c2.getRichStringCellValue().getString());
-        assertEquals(0.0, c3.getNumericCellValue(), 0.00001);
-        assertEquals("", c3.getRichStringCellValue().getString());
-        
+        confirmCachedValue(0.0, c1);
+        confirmCachedValue(0.0, c2);
+        confirmCachedValue(0.0, c3);
+
         // Try changing the cached value on one of the string
         //  formula cells, so we can see it updates properly
         c3.setCellValue(new HSSFRichTextString("test"));
-        assertEquals(0.0, c3.getNumericCellValue(), 0.00001);
-        assertEquals("test", c3.getRichStringCellValue().getString());
-        
-        
+        confirmCachedValue("test", c3);
+        try {
+            c3.getNumericCellValue();
+            throw new AssertionFailedError("exception should have been thrown");
+        } catch (IllegalStateException e) {
+            assertEquals("Cannot get a numeric value from a text formula cell", e.getMessage());
+        }
+
+
         // Now evaluate, they should all be changed
-        HSSFFormulaEvaluator eval = new HSSFFormulaEvaluator(s, wb);
+        HSSFFormulaEvaluator eval = new HSSFFormulaEvaluator(wb);
         eval.evaluateFormulaCell(c1);
         eval.evaluateFormulaCell(c2);
         eval.evaluateFormulaCell(c3);
-        
+
         // Check that the cells now contain
         //  the correct values
-        assertEquals(70164.0, c1.getNumericCellValue(), 0.00001);
-        assertEquals("", c1.getRichStringCellValue().getString());
-        assertEquals(0.0, c2.getNumericCellValue(), 0.00001);
-        assertEquals("70164", c2.getRichStringCellValue().getString());
-        assertEquals(0.0, c3.getNumericCellValue(), 0.00001);
-        assertEquals("90210", c3.getRichStringCellValue().getString());
-  
-        
+        confirmCachedValue(70164.0, c1);
+        confirmCachedValue("70164", c2);
+        confirmCachedValue("90210", c3);
+
+
         // Write and read
         HSSFWorkbook nwb = writeOutAndReadBack(wb);
         HSSFSheet ns = nwb.getSheetAt(0);
         HSSFCell nc1 = ns.getRow(0).getCell(0);
         HSSFCell nc2 = ns.getRow(0).getCell(1);
         HSSFCell nc3 = ns.getRow(0).getCell(2);
-        
+
         // Re-check
-        assertEquals(70164.0, nc1.getNumericCellValue(), 0.00001);
-        assertEquals("", nc1.getRichStringCellValue().getString());
-        assertEquals(0.0, nc2.getNumericCellValue(), 0.00001);
-        assertEquals("70164", nc2.getRichStringCellValue().getString());
-        assertEquals(0.0, nc3.getNumericCellValue(), 0.00001);
-        assertEquals("90210", nc3.getRichStringCellValue().getString());
-        
+        confirmCachedValue(70164.0, nc1);
+        confirmCachedValue("70164", nc2);
+        confirmCachedValue("90210", nc3);
+
         CellValueRecordInterface[] cvrs = ns.getSheet().getValueRecords();
         for (int i = 0; i < cvrs.length; i++) {
             CellValueRecordInterface cvr = cvrs[i];
             if(cvr instanceof FormulaRecordAggregate) {
                 FormulaRecordAggregate fr = (FormulaRecordAggregate)cvr;
-                
+
                 if(i == 0) {
                     assertEquals(70164.0, fr.getFormulaRecord().getValue(), 0.0001);
                     assertNull(fr.getStringRecord());
@@ -1235,7 +1231,18 @@ public final class TestBugs extends TestCase {
         }
         assertEquals(3, cvrs.length);
     }
-    
+
+    private static void confirmCachedValue(double expectedValue, HSSFCell cell) {
+        assertEquals(HSSFCell.CELL_TYPE_FORMULA, cell.getCellType());
+        assertEquals(HSSFCell.CELL_TYPE_NUMERIC, cell.getCachedFormulaResultType());
+        assertEquals(expectedValue, cell.getNumericCellValue(), 0.0);
+    }
+    private static void confirmCachedValue(String expectedValue, HSSFCell cell) {
+        assertEquals(HSSFCell.CELL_TYPE_FORMULA, cell.getCellType());
+        assertEquals(HSSFCell.CELL_TYPE_STRING, cell.getCachedFormulaResultType());
+        assertEquals(expectedValue, cell.getRichStringCellValue().getString());
+    }
+
     /**
      * Problem with "Vector Rows", eg a whole
      *  column which is set to the result of
@@ -1244,37 +1251,37 @@ public final class TestBugs extends TestCase {
      *  {=sin(B1:B9){9,1)[rownum][0]
      * In this sample file, the vector column
      *  is C, and the data column is B.
-     *  
+     *
      * For now, blows up with an exception from ExtPtg
      *  Expected ExpPtg to be converted from Shared to Non-Shared...
      */
     public void DISABLEDtest43623() {
         HSSFWorkbook wb = openSample("43623.xls");
         assertEquals(1, wb.getNumberOfSheets());
-        
+
         HSSFSheet s1 = wb.getSheetAt(0);
-        
+
         HSSFCell c1 = s1.getRow(0).getCell(2);
         HSSFCell c2 = s1.getRow(1).getCell(2);
         HSSFCell c3 = s1.getRow(2).getCell(2);
-        
+
         // These formula contents are a guess...
         assertEquals("{=sin(B1:B9){9,1)[0][0]", c1.getCellFormula());
         assertEquals("{=sin(B1:B9){9,1)[1][0]", c2.getCellFormula());
         assertEquals("{=sin(B1:B9){9,1)[2][0]", c3.getCellFormula());
-        
+
         // Save and re-open, ensure it still works
         HSSFWorkbook nwb = writeOutAndReadBack(wb);
         HSSFSheet ns1 = nwb.getSheetAt(0);
         HSSFCell nc1 = ns1.getRow(0).getCell(2);
         HSSFCell nc2 = ns1.getRow(1).getCell(2);
         HSSFCell nc3 = ns1.getRow(2).getCell(2);
-        
+
         assertEquals("{=sin(B1:B9){9,1)[0][0]", nc1.getCellFormula());
         assertEquals("{=sin(B1:B9){9,1)[1][0]", nc2.getCellFormula());
         assertEquals("{=sin(B1:B9){9,1)[2][0]", nc3.getCellFormula());
     }
-    
+
     /**
      * People are all getting confused about the last
      *  row and cell number
@@ -1282,48 +1289,48 @@ public final class TestBugs extends TestCase {
     public void test30635() {
         HSSFWorkbook wb = new HSSFWorkbook();
         HSSFSheet s = wb.createSheet();
-        
+
         // No rows, everything is 0
         assertEquals(0, s.getFirstRowNum());
         assertEquals(0, s.getLastRowNum());
         assertEquals(0, s.getPhysicalNumberOfRows());
-        
+
         // One row, most things are 0, physical is 1
         s.createRow(0);
         assertEquals(0, s.getFirstRowNum());
         assertEquals(0, s.getLastRowNum());
         assertEquals(1, s.getPhysicalNumberOfRows());
-        
+
         // And another, things change
         s.createRow(4);
         assertEquals(0, s.getFirstRowNum());
         assertEquals(4, s.getLastRowNum());
         assertEquals(2, s.getPhysicalNumberOfRows());
-        
-        
+
+
         // Now start on cells
         HSSFRow r = s.getRow(0);
         assertEquals(-1, r.getFirstCellNum());
         assertEquals(-1, r.getLastCellNum());
         assertEquals(0, r.getPhysicalNumberOfCells());
-        
+
         // Add a cell, things move off -1
         r.createCell(0);
         assertEquals(0, r.getFirstCellNum());
         assertEquals(1, r.getLastCellNum()); // last cell # + 1
         assertEquals(1, r.getPhysicalNumberOfCells());
-        
+
         r.createCell(1);
         assertEquals(0, r.getFirstCellNum());
         assertEquals(2, r.getLastCellNum()); // last cell # + 1
         assertEquals(2, r.getPhysicalNumberOfCells());
-        
+
         r.createCell(4);
         assertEquals(0, r.getFirstCellNum());
         assertEquals(5, r.getLastCellNum()); // last cell # + 1
         assertEquals(3, r.getPhysicalNumberOfCells());
     }
-    
+
     /**
      * Data Tables - ptg 0x2
      */
@@ -1332,25 +1339,25 @@ public final class TestBugs extends TestCase {
         HSSFSheet s;
         HSSFRow r;
         HSSFCell c;
-        
+
         // Check the contents of the formulas
-        
+
         // E4 to G9 of sheet 4 make up the table
         s = wb.getSheet("OneVariable Table Completed");
         r = s.getRow(3);
         c = r.getCell(4);
         assertEquals(HSSFCell.CELL_TYPE_FORMULA, c.getCellType());
-        
+
         // TODO - check the formula once tables and
         //  arrays are properly supported
 
-        
+
         // E4 to H9 of sheet 5 make up the table
         s = wb.getSheet("TwoVariable Table Example");
         r = s.getRow(3);
         c = r.getCell(4);
         assertEquals(HSSFCell.CELL_TYPE_FORMULA, c.getCellType());
-        
+
         // TODO - check the formula once tables and
         //  arrays are properly supported
     }
@@ -1363,7 +1370,7 @@ public final class TestBugs extends TestCase {
         HSSFSheet sh = wb.getSheetAt(0);
         for(short i=0; i < 30; i++) sh.autoSizeColumn(i);
      }
-    
+
     /**
      * We used to add too many UncalcRecords to sheets
      *  with diagrams on. Don't any more
@@ -1373,41 +1380,41 @@ public final class TestBugs extends TestCase {
         wb.getSheetAt(0).setForceFormulaRecalculation(true);
         wb.getSheetAt(1).setForceFormulaRecalculation(false);
         wb.getSheetAt(2).setForceFormulaRecalculation(true);
-        
+
         // Write out and back in again
         // This used to break
         HSSFWorkbook nwb = writeOutAndReadBack(wb);
-        
+
         // Check now set as it should be
         assertTrue(nwb.getSheetAt(0).getForceFormulaRecalculation());
         assertFalse(nwb.getSheetAt(1).getForceFormulaRecalculation());
         assertTrue(nwb.getSheetAt(2).getForceFormulaRecalculation());
     }
-    
+
     /**
      * Very hidden sheets not displaying as such
      */
     public void test45761() {
-       HSSFWorkbook wb = openSample("45761.xls");
-       assertEquals(3, wb.getNumberOfSheets());
-       
-       assertFalse(wb.isSheetHidden(0));
-       assertFalse(wb.isSheetVeryHidden(0));
-       assertTrue(wb.isSheetHidden(1));
-       assertFalse(wb.isSheetVeryHidden(1));
-       assertFalse(wb.isSheetHidden(2));
-       assertTrue(wb.isSheetVeryHidden(2));
-       
-       // Change 0 to be very hidden, and re-load
-       wb.setSheetHidden(0, 2);
-       
+        HSSFWorkbook wb = openSample("45761.xls");
+        assertEquals(3, wb.getNumberOfSheets());
+
+        assertFalse(wb.isSheetHidden(0));
+        assertFalse(wb.isSheetVeryHidden(0));
+        assertTrue(wb.isSheetHidden(1));
+        assertFalse(wb.isSheetVeryHidden(1));
+        assertFalse(wb.isSheetHidden(2));
+        assertTrue(wb.isSheetVeryHidden(2));
+
+        // Change 0 to be very hidden, and re-load
+        wb.setSheetHidden(0, 2);
+
         HSSFWorkbook nwb = writeOutAndReadBack(wb);
 
-       assertFalse(nwb.isSheetHidden(0));
-       assertTrue(nwb.isSheetVeryHidden(0));
-       assertTrue(nwb.isSheetHidden(1));
-       assertFalse(nwb.isSheetVeryHidden(1));
-       assertFalse(nwb.isSheetHidden(2));
-       assertTrue(nwb.isSheetVeryHidden(2));
+        assertFalse(nwb.isSheetHidden(0));
+        assertTrue(nwb.isSheetVeryHidden(0));
+        assertTrue(nwb.isSheetHidden(1));
+        assertFalse(nwb.isSheetVeryHidden(1));
+        assertFalse(nwb.isSheetHidden(2));
+        assertTrue(nwb.isSheetVeryHidden(2));
     }
 }
index f1d838efa398c6ac664c5c37e4716792271694d5..dc4454d51574cb28cd32f4cb4b757aa6e653169d 100644 (file)
@@ -117,7 +117,7 @@ public final class TestFormulaEvaluatorBugs extends TestCase {
 
                HSSFSheet sheet = wb.getSheetAt(0);
 
-               HSSFFormulaEvaluator eva = new HSSFFormulaEvaluator(sheet, wb);
+               HSSFFormulaEvaluator eva = new HSSFFormulaEvaluator(wb);
 
                row = sheet.getRow(0);
                cell = row.getCell(0);
@@ -177,7 +177,7 @@ public final class TestFormulaEvaluatorBugs extends TestCase {
 
                HSSFSheet sheet = wb.getSheetAt(0);
 
-               HSSFFormulaEvaluator eva = new HSSFFormulaEvaluator(sheet, wb);
+               HSSFFormulaEvaluator eva = new HSSFFormulaEvaluator(wb);
 
                // =index(C:C,2,1) -> 2
                HSSFRow rowIDX = sheet.getRow(3);
@@ -238,7 +238,7 @@ public final class TestFormulaEvaluatorBugs extends TestCase {
 
                cell.setCellFormula("1=1");
 
-               HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb);
+               HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
                try {
                        fe.evaluateInCell(cell);
                } catch (NumberFormatException e) {
@@ -257,7 +257,7 @@ public final class TestFormulaEvaluatorBugs extends TestCase {
                int numSheets = wb.getNumberOfSheets();
                for (int i = 0; i < numSheets; i++) {
                        HSSFSheet s = wb.getSheetAt(i);
-                       HSSFFormulaEvaluator eval = new HSSFFormulaEvaluator(s, wb);
+                       HSSFFormulaEvaluator eval = new HSSFFormulaEvaluator(wb);
 
                        for (Iterator rows = s.rowIterator(); rows.hasNext();) {
                                HSSFRow r = (HSSFRow) rows.next();
@@ -276,7 +276,7 @@ public final class TestFormulaEvaluatorBugs extends TestCase {
                HSSFRow row = sheet.createRow(1);
                HSSFCell cell = row.createCell(0);
                cell.setCellFormula("na()"); // this formula evaluates to an Excel error code '#N/A'
-               HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb);
+               HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
                try {
                        fe.evaluateInCell(cell);
                } catch (NumberFormatException e) {
@@ -312,7 +312,7 @@ public final class TestFormulaEvaluatorBugs extends TestCase {
                
                // Choose cell A9, so that the failing test case doesn't take too long to execute.
                HSSFCell cell = row.getCell(8);
-               HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb);
+               HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(wb);
                evaluator.evaluate(cell);
                int evalCount = evaluator.getEvaluationCount();
                // With caching, the evaluationCount is 8 which is a big improvement
index bed8869d54501e489977023320572779bc583afe..d2c9d0753cd8a56462f5580505d9df2f90c7ca59 100644 (file)
@@ -68,7 +68,7 @@ public final class TestFormulaEvaluatorDocs extends TestCase {
                // uses evaluateFormulaCell()
                for(int sheetNum = 0; sheetNum < wb.getNumberOfSheets(); sheetNum++) {
                        HSSFSheet sheet = wb.getSheetAt(sheetNum);
-                       HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb);
+                       HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(wb);
 
                        for(Iterator rit = sheet.rowIterator(); rit.hasNext();) {
                                HSSFRow r = (HSSFRow)rit.next();
@@ -103,7 +103,7 @@ public final class TestFormulaEvaluatorDocs extends TestCase {
                // uses evaluateInCell()
                for(int sheetNum = 0; sheetNum < wb.getNumberOfSheets(); sheetNum++) {
                        HSSFSheet sheet = wb.getSheetAt(sheetNum);
-                       HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb);
+                       HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(wb);
 
                        for(Iterator rit = sheet.rowIterator(); rit.hasNext();) {
                                HSSFRow r = (HSSFRow)rit.next();
index f1be47fd1bd5bc530bd797016d13eb756c185b52..e1a6e1b3caf09242cac2fba2549fa09da7d5d377 100644 (file)
@@ -243,7 +243,7 @@ public final class TestHSSFDataFormatter extends TestCase {
                assertEquals("SUM(12.25,12.25)/100", formatter.formatCellValue(cell));
 
                // now with a formula evaluator
-               HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(wb.getSheetAt(0), wb);
+               HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(wb);
                log(formatter.formatCellValue(cell, evaluator) + "\t\t\t (with evaluator)");
                assertEquals("24.50%", formatter.formatCellValue(cell,evaluator));
        }
@@ -257,7 +257,7 @@ public final class TestHSSFDataFormatter extends TestCase {
                Iterator it = row.cellIterator();
                Format defaultFormat = new DecimalFormat("Balance $#,#00.00 USD;Balance -$#,#00.00 USD");
                formatter.setDefaultNumberFormat(defaultFormat);
-               double value = 10d;
+
                log("\n==== DEFAULT NUMBER FORMAT ====");
                while (it.hasNext()) {
                        HSSFCell cell = (HSSFCell) it.next();
index 970b166dd772feb2beca090ab3cf5c8910aa4116..d1c511feb23687826e03f64b7b22356b3b3c747a 100644 (file)
@@ -35,7 +35,7 @@ public final class TestHSSFFormulaEvaluator extends TestCase {
                HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("testNames.xls");
                HSSFSheet sheet = wb.getSheetAt(0);
                HSSFCell cell = sheet.getRow(8).getCell(0);
-               HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb);
+               HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
                CellValue cv = fe.evaluate(cell);
                assertEquals(HSSFCell.CELL_TYPE_NUMERIC, cv.getCellType());
                assertEquals(3.72, cv.getNumberValue(), 0.0);
@@ -67,7 +67,7 @@ public final class TestHSSFFormulaEvaluator extends TestCase {
                setValue(sheet, 3, 6, 100.0);
                
                
-               HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb);
+               HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
                assertEquals(26.0, fe.evaluate(cell0).getNumberValue(), 0.0);
                assertEquals(56.0, fe.evaluate(cell1).getNumberValue(), 0.0);
        }