From 2a02163d66f91be8335e1d63a3e14dc6760dd7d8 Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Sun, 7 Sep 2008 17:34:36 +0000 Subject: [PATCH] Merged revisions 638786-638802,638805-638811,638813-638814,638816-639230,639233-639241,639243-639253,639255-639486,639488-639601,639603-639835,639837-639917,639919-640056,640058-640710,640712-641156,641158-641184,641186-641795,641797-641798,641800-641933,641935-641963,641965-641966,641968-641995,641997-642230,642232-642562,642564-642565,642568-642570,642572-642573,642576-642736,642739-642877,642879,642881-642890,642892-642903,642905-642945,642947-643624,643626-643653,643655-643669,643671,643673-643830,643832-643833,643835-644342,644344-644472,644474-644508,644510-645347,645349-645351,645353-645559,645561-645565,645568-645951,645953-646193,646195-646311,646313-646404,646406-646665,646667-646853,646855-646869,646871-647151,647153-647185,647187-647277,647279-647566,647568-647573,647575,647578-647711,647714-647737,647739-647823,647825-648155,648157-648202,648204-648273,648275,648277-648302,648304-648333,648335-648588,648590-648622,648625-648673,648675-649141,649144,649146-649556,649558-649795,649799,649801-649910,649912-649913,649915-650128,650131-650132,650134-650137,650140-650914,650916-651991,651993-652284,652286-652287,652289,652291,652293-652297,652299-652328,652330-652425,652427-652445,652447-652560,652562-652933,652935,652937-652993,652995-653116,653118-653124,653126-653483,653487-653519,653522-653550,653552-653607,653609-653667,653669-653674,653676-653814,653817-653830,653832-653891,653893-653944,653946-654055,654057-654355,654357-654365,654367-654648,654651-655215,655217-655277,655279-655281,655283-655911,655913-656212,656214,656216-656251,656253-656698,656700-656756,656758-656892,656894-657135,657137-657165,657168-657179,657181-657354,657356-657357,657359-657701,657703-657874,657876-658032,658034-658284,658286,658288-658301,658303-658307,658309-658321,658323-658335,658337-658348,658351,658353-658832,658834-658983,658985,658987-659066,659068-659402,659404-659428,659430-659451,659453-659454,659456-659461,659463-659477,659479-659524,659526-659571,659574,659576-660255,660257-660262,660264-660279,660281-660343,660345-660473,660475-660827,660829-660833,660835-660888,660890-663321,663323-663435,663437-663764,663766-663854,663856-664219,664221-664489,664494-664514,664516-668013,668015-668142,668144-668152,668154,668156-668256,668258,668260-669139,669141-669455,669457-669657,669659-669808,669810-670189,670191-671321,671323-672229,672231-672549,672551-672552,672554-672561,672563-672566,672568,672571-673049,673051-673852,673854-673862,673864-673986,673988-673996,673998-674347,674349-674890,674892-674910,674912-674936,674938-674952,674954-675078,675080-675085,675087-675217,675219-675660,675662-675670,675672-675716,675718-675726,675728-675733,675735-675775,675777-675782,675784,675786-675791,675794-675852,675854-676200,676202,676204,676206-676220,676222-676309,676311-676456,676458-676994,676996-677027,677030-677040,677042-677056,677058-677375,677377-677968,677970-677971,677973,677975-677994,677996-678286,678288-678538,678540-680393,680395-680469,680471-680529,680531-680852,680854-681529,681531-681571,681573-682224,682226,682228,682231-682281,682283-682335,682337-682507,682509,682512-682517,682519-682532,682534-682619,682622-682777,682779-682998,683000-683019,683021-683022,683024-683080,683082-683092,683094-683095,683097-683127,683129-683131,683133-683166,683168-683698,683700-683705,683707-683757,683759-683787,683789-683870,683872-683879,683881-683900,683902-684066,684068-684074,684076-684222,684224-684254,684257-684281,684283-684286,684288-684292,684294-684298,684300-684301,684303-684308,684310-684317,684320,684323-684335,684337-684348,684350-684354,684356-684361,684363-684369,684371-684453,684455-684883,684885-684937,684940-684958,684960-684970,684972-684985,684987-685053,685055-685063,685065-685259,685261-685262,685264-685266,685268-685282,685285-686035,686037-686045,686047-686052,686054-686206,686208-686215,686217-686277,686279-686289,686291-686620,686622-686623,686626-686627,686629-686639,686641-686843,686845-686976,686978-687402,687404-687422,687424-687428,687430-687442,687444-688425,688427-688641,688643-688649,688651-688654,688656-688824,688826-688909,688911-689543,689545-689558,689560-689635,689637-689703,689705-689715,689717-689718,689720,689722-689972,689974-690090,690092-690093,690095-690111,690113-690258,690260-690261,690263-690403,690405-690410,690412-690460,690462-690516,690518-690533,690535,690537-690625,690627-690635,690637-690720,690722-690725,690727-690728,690730-691382 via svnmerge from https://svn.apache.org/repos/asf/poi/trunk ........ r690739 | nick | 2008-08-31 18:24:10 +0100 (Sun, 31 Aug 2008) | 1 line HPBF docs update ........ r690761 | josh | 2008-08-31 20:08:36 +0100 (Sun, 31 Aug 2008) | 1 line removed AreaEval.getValues (initial work for bug 45358) ........ r690772 | josh | 2008-08-31 20:44:11 +0100 (Sun, 31 Aug 2008) | 1 line Partial fix for bug 45358 - parsing area refs with rows above 32767 ........ r690825 | josh | 2008-09-01 00:59:26 +0100 (Mon, 01 Sep 2008) | 1 line Improving AreaI interface and AreaPtg hierarchy ........ r690835 | josh | 2008-09-01 02:48:45 +0100 (Mon, 01 Sep 2008) | 1 line Converted AreEval and RefEval to be lazy (part of fix for bug 45358) ........ r690836 | josh | 2008-09-01 03:26:33 +0100 (Mon, 01 Sep 2008) | 1 line Fixed IF() to handle different types for the first arg ........ r690837 | josh | 2008-09-01 03:34:05 +0100 (Mon, 01 Sep 2008) | 1 line Update status for bug 45358 (fixed with r690772, r690835 and r690836) ........ r691017 | nick | 2008-09-01 17:51:09 +0100 (Mon, 01 Sep 2008) | 1 line DV related fixes from Pierre Lavignotte ........ r691180 | yegor | 2008-09-02 10:59:53 +0100 (Tue, 02 Sep 2008) | 1 line improved handling of StyleTextPropAtom bit masks, added more read-write roundtrip tests ........ r691182 | yegor | 2008-09-02 11:03:11 +0100 (Tue, 02 Sep 2008) | 1 line continue making progress with hslf hyperlinks ........ git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@692907 13f79535-47bb-0310-9956-ffa450edef68 --- src/documentation/content/xdocs/changes.xml | 2 + .../content/xdocs/hpbf/file-format.xml | 21 + .../content/xdocs/hpbf/index.xml | 2 +- src/documentation/content/xdocs/status.xml | 2 + .../apache/poi/hssf/record/NameRecord.java | 8 +- .../hssf/record/formula/Area2DPtgBase.java | 59 + .../poi/hssf/record/formula/Area3DPtg.java | 244 +--- .../apache/poi/hssf/record/formula/AreaI.java | 90 +- .../poi/hssf/record/formula/AreaNPtg.java | 2 +- .../poi/hssf/record/formula/AreaPtg.java | 18 +- .../poi/hssf/record/formula/AreaPtgBase.java | 61 +- .../poi/hssf/record/formula/RefPtgBase.java | 8 - .../hssf/record/formula/eval/Area2DEval.java | 32 - .../hssf/record/formula/eval/Area3DEval.java | 39 - .../hssf/record/formula/eval/AreaEval.java | 13 +- .../record/formula/eval/AreaEvalBase.java | 38 +- .../record/formula/eval/FunctionEval.java | 3 +- .../record/formula/eval/LazyAreaEval.java | 65 + .../hssf/record/formula/eval/LazyRefEval.java | 52 + .../hssf/record/formula/eval/NumberEval.java | 8 +- .../hssf/record/formula/eval/Ref2DEval.java | 3 + .../hssf/record/formula/eval/Ref3DEval.java | 53 - .../poi/hssf/record/formula/eval/RefEval.java | 50 +- .../hssf/record/formula/eval/RefEvalBase.java | 18 + .../record/formula/functions/CountUtils.java | 9 +- .../poi/hssf/record/formula/functions/If.java | 87 +- .../hssf/record/formula/functions/Index.java | 102 +- .../record/formula/functions/LookupUtils.java | 10 +- .../hssf/record/formula/functions/MathX.java | 79 -- .../MultiOperandNumericFunction.java | 50 +- .../hssf/record/formula/functions/Offset.java | 102 +- .../record/formula/functions/Sumx2my2.java | 45 +- .../record/formula/functions/Sumx2py2.java | 45 +- .../record/formula/functions/Sumxmy2.java | 45 +- .../formula/functions/XYNumericFunction.java | 314 ++--- .../hssf/usermodel/HSSFFormulaEvaluator.java | 10 +- .../apache/poi/hssf/usermodel/HSSFSheet.java | 28 +- .../poi/hssf/usermodel/HSSFWorkbook.java | 32 +- .../apache/poi/hssf/util/AreaReference.java | 280 +--- .../poi/ss/usermodel/FormulaEvaluator.java | 407 ++---- .../org/apache/poi/ss/util/AreaReference.java | 296 ++++ .../org/apache/poi/hslf/dev/PPTXMLDump.java | 2 +- .../org/apache/poi/hslf/model/Hyperlink.java | 51 +- .../apache/poi/hslf/model/SimpleShape.java | 55 + .../textproperties/CharFlagsTextProp.java | 26 +- .../textproperties/TextPropCollection.java | 37 +- .../apache/poi/hslf/record/ExHyperlink.java | 10 +- .../poi/hslf/record/StyleTextPropAtom.java | 68 +- .../apache/poi/hslf/usermodel/SlideShow.java | 29 +- .../apache/poi/hssf/usermodel/HSSFChart.java | 16 +- .../hslf/record/TestStyleTextPropAtom.java | 1227 ++++++++--------- .../poi/hssf/data/FormulaEvalTestData.xls | Bin 153088 -> 153600 bytes .../poi/hssf/model/TestFormulaParser.java | 28 +- .../poi/hssf/record/TestLinkedDataRecord.java | 49 +- .../record/formula/eval/TestAreaEval.java | 9 +- .../formula/eval/TestUnaryPlusEval.java | 26 +- .../record/formula/functions/EvalFactory.java | 64 +- .../formula/functions/TestCountFuncs.java | 15 +- .../record/formula/functions/TestIndex.java | 5 +- .../record/formula/functions/TestMatch.java | 167 ++- .../record/formula/functions/TestMathX.java | 70 +- .../record/formula/functions/TestMid.java | 38 +- .../formula/functions/TestSumproduct.java | 9 +- .../record/formula/functions/TestTFunc.java | 8 +- .../functions/TestXYNumericFunction.java | 4 +- .../poi/hssf/usermodel/TestNamedRange.java | 4 +- .../poi/hssf/util/TestAreaReference.java | 2 + .../poi/hssf/util/TestCellReference.java | 3 +- 68 files changed, 2313 insertions(+), 2541 deletions(-) create mode 100644 src/java/org/apache/poi/hssf/record/formula/Area2DPtgBase.java delete mode 100644 src/java/org/apache/poi/hssf/record/formula/eval/Area2DEval.java delete mode 100644 src/java/org/apache/poi/hssf/record/formula/eval/Area3DEval.java create mode 100644 src/java/org/apache/poi/hssf/record/formula/eval/LazyAreaEval.java create mode 100644 src/java/org/apache/poi/hssf/record/formula/eval/LazyRefEval.java delete mode 100644 src/java/org/apache/poi/hssf/record/formula/eval/Ref3DEval.java create mode 100644 src/java/org/apache/poi/hssf/record/formula/eval/RefEvalBase.java create mode 100644 src/java/org/apache/poi/ss/util/AreaReference.java diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index 14dcd3b4f..cd8684240 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -65,6 +65,8 @@ Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx + Initial support for embedded movies and controls in HSLF + 45358 - signed/unsigned error when parsing 3-d area refs, performance problem evaluating area refs, and ClassCastExcecption in IF() Support for HPBF Publisher hyperlinks, including during text extraction 26321 and 44958 - preserve position of ArrayRecords and TableRecords among cell value records Impove empty header or footer handling in HWPF HeaderStories diff --git a/src/documentation/content/xdocs/hpbf/file-format.xml b/src/documentation/content/xdocs/hpbf/file-format.xml index e08ebbac0..ed0185828 100644 --- a/src/documentation/content/xdocs/hpbf/file-format.xml +++ b/src/documentation/content/xdocs/hpbf/file-format.xml @@ -171,6 +171,27 @@ PL 62 1a 00 00 48 00 00 00 // PL from: 1a62 (6754), len: 48 (72) think that the second 4 bytes of text describes the format of data block at the offset. The format of the text block is easy, but we're still trying to figure out the others.

+ +
Structure of TEXT bit +

This is very simple. All the text for the document is + stored in a single bit of the Quill CONTENTS. The text + is stored as little endian 16 bit unicode strings.

+
+
Structure of PLC bit +

The first four bytes seem to hold the count of the + entries in the bit, and the second four bytes seem to hold + the type. There is then some pre-data, and then data for + each of the entries, the exact format dependant on the type.

+

Type 0 has 4 2 byte unsigned ints, then a pair of 2 byte + unsigned ints for each entry.

+

Type 4 has 4 2 byte unsigned ints, then a pair of 4 byte + unsigned ints for each entry.

+

Type 8 has 7 2 byte unsigned ints, then a pair of 4 byte + unsigned ints for each entry.

+

Type 12 holds hyperlinks, and is very much more complex. + See org.apache.poi.hpbf.model.qcbits.QCPLCBit + for our best guess as to how the contents match up.

+
diff --git a/src/documentation/content/xdocs/hpbf/index.xml b/src/documentation/content/xdocs/hpbf/index.xml index 01f49f061..84d6948fd 100755 --- a/src/documentation/content/xdocs/hpbf/index.xml +++ b/src/documentation/content/xdocs/hpbf/index.xml @@ -41,7 +41,7 @@ lots of offsets to other parts of the file.

Our initial aim is to provude a text extractor for the format (now done), and be able to extract hyperlinks from within - the document (not yet supported). Additional low level + the document (partly supported). Additional low level code to process the file format may follow, if there is demand and developer interest warrant it.

At this time, there is no usermodel api or similar. diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index e92ca9b70..5c7198bce 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -62,6 +62,8 @@ Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx + Initial support for embedded movies and controls in HSLF + 45358 - signed/unsigned error when parsing 3-d area refs, performance problem evaluating area refs, and ClassCastExcecption in IF() Support for HPBF Publisher hyperlinks, including during text extraction 26321 and 44958 - preserve position of ArrayRecords and TableRecords among cell value records Impove empty header or footer handling in HWPF HeaderStories diff --git a/src/java/org/apache/poi/hssf/record/NameRecord.java b/src/java/org/apache/poi/hssf/record/NameRecord.java index 6c6c53a92..71f3bf6fb 100644 --- a/src/java/org/apache/poi/hssf/record/NameRecord.java +++ b/src/java/org/apache/poi/hssf/record/NameRecord.java @@ -27,8 +27,8 @@ import org.apache.poi.hssf.record.formula.Ptg; import org.apache.poi.hssf.record.formula.Ref3DPtg; import org.apache.poi.hssf.record.formula.UnionPtg; import org.apache.poi.hssf.usermodel.HSSFWorkbook; -import org.apache.poi.hssf.util.AreaReference; import org.apache.poi.hssf.util.RangeAddress; +import org.apache.poi.ss.util.AreaReference; import org.apache.poi.util.HexDump; import org.apache.poi.util.LittleEndian; import org.apache.poi.util.StringUtil; @@ -626,7 +626,7 @@ public final class NameRecord extends Record { } private Ptg createNewPtg(){ - Ptg ptg = new Area3DPtg(); + Ptg ptg = new Area3DPtg("A1", 0); // TODO - change to not be partially initialised field_13_name_definition.push(ptg); return ptg; @@ -673,9 +673,7 @@ public final class NameRecord extends Record { // Add the area reference(s) for(int i=0; i= 0x100) { throw new IllegalArgumentException("Specified colIx (" + col + ") is out of range"); diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/Area2DEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/Area2DEval.java deleted file mode 100644 index 5ae98b39f..000000000 --- a/src/java/org/apache/poi/hssf/record/formula/eval/Area2DEval.java +++ /dev/null @@ -1,32 +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.AreaPtg; -import org.apache.poi.hssf.record.formula.Ptg; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public final class Area2DEval extends AreaEvalBase { - - public Area2DEval(Ptg ptg, ValueEval[] values) { - super((AreaPtg) ptg, values); - } -} \ No newline at end of file diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/Area3DEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/Area3DEval.java deleted file mode 100644 index 89209e21b..000000000 --- a/src/java/org/apache/poi/hssf/record/formula/eval/Area3DEval.java +++ /dev/null @@ -1,39 +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.Area3DPtg; -import org.apache.poi.hssf.record.formula.Ptg; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public final class Area3DEval extends AreaEvalBase { - - private final int _externSheetIndex; - - public Area3DEval(Ptg ptg, ValueEval[] values) { - super((Area3DPtg) ptg, values); - _externSheetIndex = ((Area3DPtg) ptg).getExternSheetIndex(); - } - - public int getExternSheetIndex() { - return _externSheetIndex; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/AreaEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/AreaEval.java index 182b9b618..b528eb784 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/AreaEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/AreaEval.java @@ -61,13 +61,6 @@ public interface AreaEval extends ValueEval { */ boolean isColumn(); - /** - * The array of values in this area. Although the area - * maybe 1D (ie. isRow() or isColumn() returns true) or 2D - * the returned array is 1D. - */ - ValueEval[] getValues(); - /** * @return the ValueEval from within this area at the specified row and col index. Never * null (possibly {@link BlankEval}). The specified indexes should be absolute @@ -104,4 +97,10 @@ public interface AreaEval extends ValueEval { * specified indexes should relative to the top left corner of this area. */ ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex); + + /** + * Creates an {@link AreaEval} offset by a relative amount from from the upper left cell + * of this area + */ + AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx); } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/AreaEvalBase.java b/src/java/org/apache/poi/hssf/record/formula/eval/AreaEvalBase.java index 1686e75f3..94f6c9fe4 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/AreaEvalBase.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/AreaEvalBase.java @@ -22,20 +22,16 @@ import org.apache.poi.hssf.record.formula.AreaI; /** * @author Josh Micich */ -abstract class AreaEvalBase implements AreaEval { +public abstract class AreaEvalBase implements AreaEval { private final int _firstColumn; private final int _firstRow; private final int _lastColumn; private final int _lastRow; - private final ValueEval[] _values; private final int _nColumns; private final int _nRows; - protected AreaEvalBase(AreaI ptg, ValueEval[] values) { - if (values == null) { - throw new IllegalArgumentException("values must not be null"); - } + protected AreaEvalBase(AreaI ptg) { _firstRow = ptg.getFirstRow(); _firstColumn = ptg.getFirstColumn(); _lastRow = ptg.getLastRow(); @@ -43,22 +39,6 @@ abstract class AreaEvalBase implements AreaEval { _nColumns = _lastColumn - _firstColumn + 1; _nRows = _lastRow - _firstRow + 1; - - int expectedItemCount = _nRows * _nColumns; - if ((values.length != expectedItemCount)) { - // Note - this math may need alteration when POI starts to support full column or full row refs - throw new IllegalArgumentException("Array size should be (" + expectedItemCount - + ") but was (" + values.length + ")"); - } - - - - for (int i = values.length - 1; i >= 0; i--) { - if (values[i] == null) { - throw new IllegalArgumentException("value array elements must not be null"); - } - } - _values = values; } public final int getFirstColumn() { @@ -77,11 +57,6 @@ abstract class AreaEvalBase implements AreaEval { return _lastRow; } - public final ValueEval[] getValues() { - // TODO - clone() - but some junits rely on not cloning at the moment - return _values; - } - public final ValueEval getValueAt(int row, int col) { int rowOffsetIx = row - _firstRow; int colOffsetIx = col - _firstColumn; @@ -121,14 +96,7 @@ abstract class AreaEvalBase implements AreaEval { return _lastRow-_firstRow+1; } - public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) { - int index = relativeRowIndex * _nColumns + relativeColumnIndex; - ValueEval result = _values[index]; - if (result == null) { - return BlankEval.INSTANCE; - } - return result; - } + public abstract ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex); public int getWidth() { return _lastColumn-_firstColumn+1; diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/FunctionEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/FunctionEval.java index 533c604a0..fe58c69c0 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/FunctionEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/FunctionEval.java @@ -50,7 +50,6 @@ public abstract class FunctionEval implements OperationEval { static { Map m = new HashMap(); - addMapping(m, ID.OFFSET, new Offset()); addMapping(m, ID.INDIRECT, new Indirect()); addMapping(m, ID.EXTERNAL_FUNC, new ExternalFunction()); freeRefFunctionsByIdMap = m; @@ -155,7 +154,7 @@ public abstract class FunctionEval implements OperationEval { retval[75] = new Areas(); // AREAS retval[76] = new Rows(); // ROWS retval[77] = new Columns(); // COLUMNS - retval[ID.OFFSET] = null; // Offset.evaluate has a different signature + retval[ID.OFFSET] = new Offset(); // OFFSET retval[79] = new Absref(); // ABSREF retval[80] = new Relref(); // RELREF retval[81] = new Argument(); // ARGUMENT 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 new file mode 100644 index 000000000..b5c633617 --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/formula/eval/LazyAreaEval.java @@ -0,0 +1,65 @@ +/* +* 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; + +/** + * @author Amol S. Deshmukh < amolweb at ya hoo dot com > + * + */ +public final class LazyAreaEval extends AreaEvalBase { + + private final Sheet _sheet; + private Workbook _workbook; + + public LazyAreaEval(AreaI ptg, Sheet sheet, Workbook workbook) { + super(ptg); + _sheet = sheet; + _workbook = workbook; + } + + 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 FormulaEvaluator.getEvalForCell(cell, _sheet, _workbook); + } + + public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) { + AreaI area = new OffsetArea(getFirstRow(), getFirstColumn(), + relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx); + + return new LazyAreaEval(area, _sheet, _workbook); + } +} 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 new file mode 100644 index 000000000..7daf96424 --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/formula/eval/LazyRefEval.java @@ -0,0 +1,52 @@ +package org.apache.poi.hssf.record.formula.eval; + +import org.apache.poi.hssf.record.formula.AreaI; +import org.apache.poi.hssf.record.formula.AreaI.OffsetArea; +import org.apache.poi.hssf.record.formula.Ref3DPtg; +import org.apache.poi.hssf.record.formula.RefPtg; +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; + +public final class LazyRefEval extends RefEvalBase { + + private final Sheet _sheet; + private final Workbook _workbook; + + + public LazyRefEval(RefPtg ptg, Sheet sheet, Workbook workbook) { + super(ptg.getRow(), ptg.getColumn()); + _sheet = sheet; + _workbook = workbook; + } + public LazyRefEval(Ref3DPtg ptg, Sheet sheet, Workbook workbook) { + super(ptg.getRow(), ptg.getColumn()); + _sheet = sheet; + _workbook = workbook; + } + + 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 FormulaEvaluator.getEvalForCell(cell, _sheet, _workbook); + } + + 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, _workbook); + } +} diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/NumberEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/NumberEval.java index 44a017f63..e22957bd4 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/NumberEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/NumberEval.java @@ -69,5 +69,11 @@ public class NumberEval implements NumericValueEval, StringValueEval { } } } - + public final String toString() { + StringBuffer sb = new StringBuffer(64); + sb.append(getClass().getName()).append(" ["); + sb.append(getStringValue()); + sb.append("]"); + return sb.toString(); + } } 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 index 033ecc1f8..e4bb47a43 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/Ref2DEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/Ref2DEval.java @@ -47,4 +47,7 @@ public final class Ref2DEval implements RefEval { 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/Ref3DEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/Ref3DEval.java deleted file mode 100644 index 622d68632..000000000 --- a/src/java/org/apache/poi/hssf/record/formula/eval/Ref3DEval.java +++ /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.Ref3DPtg; - -/** - * @author Amol S. Deshmukh - * - */ -public final class Ref3DEval implements RefEval { - - private final ValueEval value; - private final Ref3DPtg delegate; - - public Ref3DEval(Ref3DPtg ptg, ValueEval ve) { - if(ve == null) { - throw new IllegalArgumentException("ve must not be null"); - } - if(ptg == null) { - 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 int getExternSheetIndex() { - return delegate.getExternSheetIndex(); - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/RefEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/RefEval.java index e462586d7..397d1f1c9 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/RefEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/RefEval.java @@ -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; @@ -30,22 +30,22 @@ package org.apache.poi.hssf.record.formula.eval; public interface RefEval extends ValueEval { /** - * The (possibly evaluated) ValueEval contained - * in this RefEval. eg. if cell A1 contains "test" - * then in a formula referring to cell A1 - * the RefEval representing - * A1 will return as the getInnerValueEval() the - * object of concrete type StringEval + * @return the evaluated value of the cell referred to by this RefEval. */ - public ValueEval getInnerValueEval(); + ValueEval getInnerValueEval(); /** * returns the zero based column index. */ - public int getColumn(); + int getColumn(); /** * returns the zero based row index. */ - public int getRow(); + int getRow(); + + /** + * Creates an {@link AreaEval} offset by a relative amount from this RefEval + */ + AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx); } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/RefEvalBase.java b/src/java/org/apache/poi/hssf/record/formula/eval/RefEvalBase.java new file mode 100644 index 000000000..37d20f808 --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/formula/eval/RefEvalBase.java @@ -0,0 +1,18 @@ +package org.apache.poi.hssf.record.formula.eval; + +public abstract class RefEvalBase implements RefEval { + + private final int _rowIndex; + private final int _columnIndex; + + protected RefEvalBase(int rowIndex, int columnIndex) { + _rowIndex = rowIndex; + _columnIndex = columnIndex; + } + public final int getRow() { + return _rowIndex; + } + public final int getColumn() { + return _columnIndex; + } +} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/CountUtils.java b/src/java/org/apache/poi/hssf/record/formula/functions/CountUtils.java index f8d888382..27dfcc1b6 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/CountUtils.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/CountUtils.java @@ -50,9 +50,9 @@ final class CountUtils { for (int rrIx=0; rrIx @@ -51,15 +51,23 @@ public final class Index implements Function { return ErrorEval.VALUE_INVALID; } Eval firstArg = args[0]; - if(firstArg instanceof AreaEval) { - AreaEval reference = (AreaEval) firstArg; + if(!(firstArg instanceof AreaEval)) { - int rowIx = 0; - int columnIx = 0; - int areaIx = 0; + // else the other variation of this function takes an array as the first argument + // it seems like interface 'ArrayEval' does not even exist yet + + throw new RuntimeException("Incomplete code - cannot handle first arg of type (" + + firstArg.getClass().getName() + ")"); + } + AreaEval reference = (AreaEval) firstArg; + + int rowIx = 0; + int columnIx = 0; + int areaIx = 0; + try { switch(nArgs) { case 4: - areaIx = convertIndexArgToZeroBase(args[3]); + areaIx = convertIndexArgToZeroBase(args[3], srcCellRow, srcCellCol); throw new RuntimeException("Incomplete code" + " - don't know how to support the 'area_num' parameter yet)"); // Excel expression might look like this "INDEX( (A1:B4, C3:D6, D2:E5 ), 1, 2, 3) @@ -68,41 +76,41 @@ public final class Index implements Function { // The formula parser doesn't seem to support this yet. Not sure if the evaluator does either case 3: - columnIx = convertIndexArgToZeroBase(args[2]); + columnIx = convertIndexArgToZeroBase(args[2], srcCellRow, srcCellCol); case 2: - rowIx = convertIndexArgToZeroBase(args[1]); + rowIx = convertIndexArgToZeroBase(args[1], srcCellRow, srcCellCol); break; default: // too many arguments return ErrorEval.VALUE_INVALID; } - - int nColumns = reference.getLastColumn()-reference.getFirstColumn()+1; - int index = rowIx * nColumns + columnIx; - - return reference.getValues()[index]; + return getValueFromArea(reference, rowIx, columnIx); + } catch (EvaluationException e) { + return e.getErrorEval(); } - - // else the other variation of this function takes an array as the first argument - // it seems like interface 'ArrayEval' does not even exist yet - - throw new RuntimeException("Incomplete code - cannot handle first arg of type (" - + firstArg.getClass().getName() + ")"); } + private static ValueEval getValueFromArea(AreaEval ae, int rowIx, int columnIx) throws EvaluationException { + 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); + } + if (rowIx < 0 || columnIx < 0) { + throw new EvaluationException(ErrorEval.VALUE_INVALID); + } + return ae.getRelativeValue(rowIx, columnIx); + } + /** * takes a NumberEval representing a 1-based index and returns the zero-based int value */ - private static int convertIndexArgToZeroBase(Eval ev) { - NumberEval ne; - if(ev instanceof RefEval) { - // TODO - write junit to justify this - RefEval re = (RefEval) ev; - ne = (NumberEval) re.getInnerValueEval(); - } else { - ne = (NumberEval)ev; - } + private static int convertIndexArgToZeroBase(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException { - return (int)ne.getNumberValue() - 1; + ValueEval ev = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol); + int oneBasedVal = OperandResolver.coerceValueToInt(ev); + return oneBasedVal - 1; } } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/LookupUtils.java b/src/java/org/apache/poi/hssf/record/formula/functions/LookupUtils.java index e8c083dc5..e6a3ec81c 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/LookupUtils.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/LookupUtils.java @@ -17,8 +17,6 @@ package org.apache.poi.hssf.record.formula.functions; -import org.apache.poi.hssf.record.formula.AreaPtg; -import org.apache.poi.hssf.record.formula.eval.Area2DEval; 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; @@ -366,13 +364,7 @@ final class LookupUtils { // Make this cell ref look like a 1x1 area ref. // It doesn't matter if eval is a 2D or 3D ref, because that detail is never asked of AreaEval. - // This code only requires the value array item. - // anything would be ok for rowIx and colIx, but may as well get it right. - int rowIx = refEval.getRow(); - int colIx = refEval.getColumn(); - AreaPtg ap = new AreaPtg(rowIx, rowIx, colIx, colIx, false, false, false, false); - ValueEval value = refEval.getInnerValueEval(); - return new Area2DEval(ap, new ValueEval[] { value, }); + return refEval.offset(0, 0, 0, 0); } throw EvaluationException.invalidValue(); } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/MathX.java b/src/java/org/apache/poi/hssf/record/formula/functions/MathX.java index 593998f1e..311707665 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/MathX.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/MathX.java @@ -455,85 +455,6 @@ public final class MathX { return d; } - /** - * returns the sum of difference of squares of corresponding double - * value in each subarray: ie. sigma (xarr[i]^2-yarr[i]^2) - *
- * It is the responsibility of the caller - * to ensure that the two subarrays are of equal length. If the - * subarrays are not of equal length, the return value can be - * unpredictable. - * @param xarr - * @param yarr - */ - public static double sumx2my2(double[] xarr, double[] yarr) { - double d = 0; - - try { - for (int i=0, iSize=xarr.length; i - * It is the responsibility of the caller - * to ensure that the two subarrays are of equal length. If the - * subarrays are not of equal length, the return value can be - * unpredictable. - * @param xarr - * @param yarr - */ - public static double sumx2py2(double[] xarr, double[] yarr) { - double d = 0; - - try { - for (int i=0, iSize=xarr.length; i - * It is the responsibility of the caller - * to ensure that the two subarrays are of equal length. If the - * subarrays are not of equal length, the return value can be - * unpredictable. - * @param xarr - * @param yarr - */ - public static double sumxmy2(double[] xarr, double[] yarr) { - double d = 0; - - try { - for (int i=0, iSize=xarr.length; i * @@ -51,7 +43,7 @@ import org.apache.poi.ss.usermodel.Workbook; * * @author Josh Micich */ -public final class Offset implements FreeRefFunction { +public final class Offset implements Function { // These values are specific to BIFF8 private static final int LAST_VALID_ROW_INDEX = 0xFFFF; private static final int LAST_VALID_COLUMN_INDEX = 0xFF; @@ -125,37 +117,29 @@ public final class Offset implements FreeRefFunction { * Encapsulates either an area or cell reference which may be 2d or 3d. */ private static final class BaseRef { - private static final int INVALID_SHEET_INDEX = -1; private final int _firstRowIndex; private final int _firstColumnIndex; private final int _width; private final int _height; - private final int _externalSheetIndex; + private final RefEval _refEval; + private final AreaEval _areaEval; public BaseRef(RefEval re) { + _refEval = re; + _areaEval = null; _firstRowIndex = re.getRow(); _firstColumnIndex = re.getColumn(); _height = 1; _width = 1; - if (re instanceof Ref3DEval) { - Ref3DEval r3e = (Ref3DEval) re; - _externalSheetIndex = r3e.getExternSheetIndex(); - } else { - _externalSheetIndex = INVALID_SHEET_INDEX; - } } public BaseRef(AreaEval ae) { + _refEval = null; + _areaEval = ae; _firstRowIndex = ae.getFirstRow(); _firstColumnIndex = ae.getFirstColumn(); _height = ae.getLastRow() - ae.getFirstRow() + 1; _width = ae.getLastColumn() - ae.getFirstColumn() + 1; - if (ae instanceof Area3DEval) { - Area3DEval a3e = (Area3DEval) ae; - _externalSheetIndex = a3e.getExternSheetIndex(); - } else { - _externalSheetIndex = INVALID_SHEET_INDEX; - } } public int getWidth() { @@ -170,20 +154,17 @@ public final class Offset implements FreeRefFunction { public int getFirstColumnIndex() { return _firstColumnIndex; } - public boolean isIs3d() { - return _externalSheetIndex > 0; - } - public short getExternalSheetIndex() { - if(_externalSheetIndex < 0) { - throw new IllegalStateException("external sheet index only available for 3d refs"); + public AreaEval offset(int relFirstRowIx, int relLastRowIx, + int relFirstColIx, int relLastColIx) { + if (_refEval == null) { + return _areaEval.offset(relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx); } - return (short) _externalSheetIndex; + return _refEval.offset(relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx); } } - public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, Workbook workbook, Sheet sheet) { - + public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) { if(args.length < 3 || args.length > 5) { return ErrorEval.VALUE_INVALID; } @@ -206,37 +187,24 @@ public final class Offset implements FreeRefFunction { } LinearOffsetRange rowOffsetRange = new LinearOffsetRange(rowOffset, height); LinearOffsetRange colOffsetRange = new LinearOffsetRange(columnOffset, width); - return createOffset(baseRef, rowOffsetRange, colOffsetRange, workbook, sheet); + return createOffset(baseRef, rowOffsetRange, colOffsetRange); } catch (EvaluationException e) { return e.getErrorEval(); } } private static AreaEval createOffset(BaseRef baseRef, - LinearOffsetRange rowOffsetRange, LinearOffsetRange colOffsetRange, - Workbook workbook, Sheet sheet) throws EvaluationException { - - LinearOffsetRange rows = rowOffsetRange.normaliseAndTranslate(baseRef.getFirstRowIndex()); - LinearOffsetRange cols = colOffsetRange.normaliseAndTranslate(baseRef.getFirstColumnIndex()); + LinearOffsetRange orRow, LinearOffsetRange orCol) throws EvaluationException { + LinearOffsetRange absRows = orRow.normaliseAndTranslate(baseRef.getFirstRowIndex()); + LinearOffsetRange absCols = orCol.normaliseAndTranslate(baseRef.getFirstColumnIndex()); - if(rows.isOutOfBounds(0, LAST_VALID_ROW_INDEX)) { + if(absRows.isOutOfBounds(0, LAST_VALID_ROW_INDEX)) { throw new EvaluationException(ErrorEval.REF_INVALID); } - if(cols.isOutOfBounds(0, LAST_VALID_COLUMN_INDEX)) { + if(absCols.isOutOfBounds(0, LAST_VALID_COLUMN_INDEX)) { throw new EvaluationException(ErrorEval.REF_INVALID); } - if(baseRef.isIs3d()) { - Area3DPtg a3dp = new Area3DPtg(rows.getFirstIndex(), rows.getLastIndex(), - cols.getFirstIndex(), cols.getLastIndex(), - false, false, false, false, - baseRef.getExternalSheetIndex()); - return HSSFFormulaEvaluator.evaluateArea3dPtg(workbook, a3dp); - } - - AreaPtg ap = new AreaPtg(rows.getFirstIndex(), rows.getLastIndex(), - cols.getFirstIndex(), cols.getLastIndex(), - false, false, false, false); - return HSSFFormulaEvaluator.evaluateAreaPtg(sheet, workbook, ap); + return baseRef.offset(orRow.getFirstIndex(), orRow.getLastIndex(), orCol.getFirstIndex(), orCol.getLastIndex()); } private static BaseRef evaluateBaseRef(Eval eval) throws EvaluationException { diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Sumx2my2.java b/src/java/org/apache/poi/hssf/record/formula/functions/Sumx2my2.java index 30ad5ec23..4dedf2657 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Sumx2my2.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Sumx2my2.java @@ -1,22 +1,23 @@ -/* -* 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; + /** * Implementation of Excel function SUMX2MY2()

* @@ -30,7 +31,13 @@ package org.apache.poi.hssf.record.formula.functions; */ public final class Sumx2my2 extends XYNumericFunction { - protected double evaluate(double[] xArray, double[] yArray) { - return MathX.sumx2my2(xArray, yArray); - } + private static final Accumulator XSquaredMinusYSquaredAccumulator = new Accumulator() { + public double accumulate(double x, double y) { + return x * x - y * y; + } + }; + + protected Accumulator createAccumulator() { + return XSquaredMinusYSquaredAccumulator; + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Sumx2py2.java b/src/java/org/apache/poi/hssf/record/formula/functions/Sumx2py2.java index dfd730d12..c3af0bd38 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Sumx2py2.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Sumx2py2.java @@ -1,22 +1,23 @@ -/* -* 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; + /** * Implementation of Excel function SUMX2PY2()

* @@ -30,7 +31,13 @@ package org.apache.poi.hssf.record.formula.functions; */ public final class Sumx2py2 extends XYNumericFunction { - protected double evaluate(double[] xArray, double[] yArray) { - return MathX.sumx2py2(xArray, yArray); - } + private static final Accumulator XSquaredPlusYSquaredAccumulator = new Accumulator() { + public double accumulate(double x, double y) { + return x * x + y * y; + } + }; + + protected Accumulator createAccumulator() { + return XSquaredPlusYSquaredAccumulator; + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Sumxmy2.java b/src/java/org/apache/poi/hssf/record/formula/functions/Sumxmy2.java index a1b2fec9b..4fa11e36b 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Sumxmy2.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Sumxmy2.java @@ -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.functions; @@ -30,7 +30,14 @@ package org.apache.poi.hssf.record.formula.functions; */ public final class Sumxmy2 extends XYNumericFunction { - protected double evaluate(double[] xArray, double[] yArray) { - return MathX.sumxmy2(xArray, yArray); - } + private static final Accumulator XMinusYSquaredAccumulator = new Accumulator() { + public double accumulate(double x, double y) { + double xmy = x - y; + return xmy * xmy; + } + }; + + protected Accumulator createAccumulator() { + return XMinusYSquaredAccumulator; + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/XYNumericFunction.java b/src/java/org/apache/poi/hssf/record/formula/functions/XYNumericFunction.java index b989c33a2..1d83b363f 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/XYNumericFunction.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/XYNumericFunction.java @@ -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.functions; @@ -24,180 +24,162 @@ 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.ValueEval; +import org.apache.poi.hssf.record.formula.functions.LookupUtils.ValueVector; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * + * */ public abstract class XYNumericFunction implements Function { - protected static final int X = 0; - protected static final int Y = 1; - - protected static final class DoubleArrayPair { - private final double[] _xArray; - private final double[] _yArray; - - public DoubleArrayPair(double[] xArray, double[] yArray) { - _xArray = xArray; - _yArray = yArray; + private static abstract class ValueArray implements ValueVector { + private final int _size; + protected ValueArray(int size) { + _size = size; } - public double[] getXArray() { - return _xArray; + public ValueEval getItem(int index) { + if (index < 0 || index > _size) { + throw new IllegalArgumentException("Specified index " + index + + " is outside range (0.." + (_size - 1) + ")"); + } + return getItemInternal(index); } - public double[] getYArray() { - return _yArray; + protected abstract ValueEval getItemInternal(int index); + public final int getSize() { + return _size; } - } + } + private static final class SingleCellValueArray extends ValueArray { + private final ValueEval _value; + public SingleCellValueArray(ValueEval value) { + super(1); + _value = value; + } + protected ValueEval getItemInternal(int index) { + return _value; + } + } - public final Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) { - if(args.length != 2) { - return ErrorEval.VALUE_INVALID; - } - - double[][] values; + private static final class RefValueArray extends ValueArray { + private final RefEval _ref; + public RefValueArray(RefEval ref) { + super(1); + _ref = ref; + } + protected ValueEval getItemInternal(int index) { + return _ref.getInnerValueEval(); + } + } + + private static final class AreaValueArray extends ValueArray { + private final AreaEval _ae; + private final int _width; + + public AreaValueArray(AreaEval ae) { + super(ae.getWidth() * ae.getHeight()); + _ae = ae; + _width = ae.getWidth(); + } + protected ValueEval getItemInternal(int index) { + int rowIx = index / _width; + int colIx = index % _width; + return _ae.getRelativeValue(rowIx, colIx); + } + } + + protected static interface Accumulator { + double accumulate(double x, double y); + } + + /** + * Constructs a new instance of the Accumulator used to calculated this function + */ + protected abstract Accumulator createAccumulator(); + + public final Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) { + if (args.length != 2) { + return ErrorEval.VALUE_INVALID; + } + + double result; try { - values = getValues(args[0], args[1]); + ValueVector vvX = createValueVector(args[0]); + ValueVector vvY = createValueVector(args[1]); + int size = vvX.getSize(); + if (size == 0 || vvY.getSize() != size) { + return ErrorEval.NA; + } + result = evaluateInternal(vvX, vvY, size); } catch (EvaluationException e) { return e.getErrorEval(); } - if (values==null - || values[X] == null || values[Y] == null - || values[X].length == 0 || values[Y].length == 0 - || values[X].length != values[Y].length) { - return ErrorEval.VALUE_INVALID; - } - - double d = evaluate(values[X], values[Y]); - if (Double.isNaN(d) || Double.isInfinite(d)) { + if (Double.isNaN(result) || Double.isInfinite(result)) { return ErrorEval.NUM_ERROR; } - return new NumberEval(d); - } - protected abstract double evaluate(double[] xArray, double[] yArray); + return new NumberEval(result); + } - /** - * 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 null. - */ - private static double[][] getNumberArray(Eval[] xops, Eval[] yops) throws EvaluationException { - - // check for errors first: size mismatch, value errors in x, value errors in y - - int nArrayItems = xops.length; - if(nArrayItems != yops.length) { - throw new EvaluationException(ErrorEval.NA); - } - for (int i = 0; i < xops.length; i++) { - Eval eval = xops[i]; - if (eval instanceof ErrorEval) { - throw new EvaluationException((ErrorEval) eval); + private double evaluateInternal(ValueVector x, ValueVector y, int size) + throws EvaluationException { + Accumulator acc = createAccumulator(); + + // error handling is as if the x is fully evaluated before y + ErrorEval firstXerr = null; + ErrorEval firstYerr = null; + boolean accumlatedSome = false; + double result = 0.0; + + for (int i = 0; i < size; i++) { + ValueEval vx = x.getItem(i); + ValueEval vy = y.getItem(i); + if (vx instanceof ErrorEval) { + if (firstXerr == null) { + firstXerr = (ErrorEval) vx; + continue; + } + } + if (vy instanceof ErrorEval) { + if (firstYerr == null) { + firstYerr = (ErrorEval) vy; + continue; + } + } + // only count pairs if both elements are numbers + if (vx instanceof NumberEval && vy instanceof NumberEval) { + accumlatedSome = true; + NumberEval nx = (NumberEval) vx; + NumberEval ny = (NumberEval) vy; + result += acc.accumulate(nx.getNumberValue(), ny.getNumberValue()); + } else { + // all other combinations of value types are silently ignored } } - for (int i = 0; i < yops.length; i++) { - Eval eval = yops[i]; - if (eval instanceof ErrorEval) { - throw new EvaluationException((ErrorEval) eval); - } + if (firstXerr != null) { + throw new EvaluationException(firstXerr); } - - double[] xResult = new double[nArrayItems]; - double[] yResult = new double[nArrayItems]; - - int count = 0; - - for (int i=0, iSize=nArrayItems; i len) { - tarr = new double[len]; - System.arraycopy(arr, 0, tarr, 0, len); - } - return tarr; - } - - private static boolean isNumberEval(Eval eval) { - boolean retval = false; - - if (eval instanceof NumberEval) { - retval = true; - } - else if (eval instanceof RefEval) { - RefEval re = (RefEval) eval; - ValueEval ve = re.getInnerValueEval(); - retval = (ve instanceof NumberEval); - } - - return retval; - } - - private static double getDoubleValue(Eval eval) { - double retval = 0; - if (eval instanceof NumberEval) { - NumberEval ne = (NumberEval) eval; - retval = ne.getNumberValue(); - } - else if (eval instanceof RefEval) { - RefEval re = (RefEval) eval; - ValueEval ve = re.getInnerValueEval(); - retval = (ve instanceof NumberEval) - ? ((NumberEval) ve).getNumberValue() - : Double.NaN; - } - else if (eval instanceof ErrorEval) { - retval = Double.NaN; - } - return retval; - } + if (arg instanceof ValueEval) { + return new SingleCellValueArray((ValueEval) arg); + } + throw new RuntimeException("Unexpected eval class (" + arg.getClass().getName() + ")"); + } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java b/src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java index 97e605928..4fb31606e 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java @@ -43,19 +43,11 @@ public class HSSFFormulaEvaluator extends FormulaEvaluator { return new FormulaParser(formula, workbook); } - /** * debug method - * - * @param formula - * @param sheet - * @param workbook */ void inspectPtgs(String formula) { - HSSFWorkbook hssfWb = (HSSFWorkbook)_workbook; - FormulaParser fp = new FormulaParser(formula, hssfWb); - fp.parse(); - Ptg[] ptgs = fp.getRPNPtg(); + Ptg[] ptgs = FormulaParser.parse(formula, _workbook); System.out.println(""); for (int i = 0, iSize = ptgs.length; i < iSize; i++) { System.out.println(""); diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java index 280284439..6670b2041 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java @@ -35,17 +35,11 @@ import org.apache.poi.hssf.model.FormulaParser; import org.apache.poi.hssf.model.Sheet; import org.apache.poi.hssf.model.Workbook; import org.apache.poi.hssf.record.CellValueRecordInterface; -import org.apache.poi.hssf.record.CFRuleRecord; -import org.apache.poi.hssf.record.DVALRecord; import org.apache.poi.hssf.record.DVRecord; -import org.apache.poi.hssf.record.EOFRecord; import org.apache.poi.hssf.record.EscherAggregate; -import org.apache.poi.hssf.record.HCenterRecord; -import org.apache.poi.hssf.record.PageBreakRecord; import org.apache.poi.hssf.record.Record; import org.apache.poi.hssf.record.RowRecord; import org.apache.poi.hssf.record.SCLRecord; -import org.apache.poi.hssf.record.VCenterRecord; import org.apache.poi.hssf.record.WSBoolRecord; import org.apache.poi.hssf.record.WindowTwoRecord; import org.apache.poi.hssf.record.aggregates.DataValidityTable; @@ -385,6 +379,22 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet DVRecord dvRecord = dataValidation.createDVRecord(workbook); dvt.addDataValidation(dvRecord); } + + /** + * Get the DVRecords objects that are associated to this sheet + * @return a list of DVRecord instances + */ + public List getDVRecords() { + List dvRecords = new ArrayList(); + List records = sheet.getRecords(); + + for(int index=0; index= 0) - { - Area3DPtg colArea = new Area3DPtg(); - colArea.setExternSheetIndex(externSheetIndex); - colArea.setFirstColumn((short)startColumn); - colArea.setLastColumn((short)endColumn); - colArea.setFirstRow(0); - colArea.setLastRow(MAX_ROW); - colArea.setFirstColRelative(false); - colArea.setLastColRelative(false); - colArea.setFirstRowRelative(false); - colArea.setLastRowRelative(false); + if (startColumn >= 0) { + Area3DPtg colArea = new Area3DPtg(0, MAX_ROW, startColumn, endColumn, + false, false, false, false, externSheetIndex); ptgs.add(colArea); } - if (startRow >= 0) - { - Area3DPtg rowArea = new Area3DPtg(); - rowArea.setExternSheetIndex(externSheetIndex); - rowArea.setFirstColumn((short)0); - rowArea.setLastColumn(MAX_COLUMN); - rowArea.setFirstRow(startRow); - rowArea.setLastRow(endRow); - rowArea.setFirstColRelative(false); - rowArea.setLastColRelative(false); - rowArea.setFirstRowRelative(false); - rowArea.setLastRowRelative(false); + if (startRow >= 0) { + Area3DPtg rowArea = new Area3DPtg(startRow, endRow, 0, MAX_COLUMN, + false, false, false, false, externSheetIndex); ptgs.add(rowArea); } if (settingRowAndColumn) diff --git a/src/java/org/apache/poi/hssf/util/AreaReference.java b/src/java/org/apache/poi/hssf/util/AreaReference.java index a10b9976a..d6575642e 100644 --- a/src/java/org/apache/poi/hssf/util/AreaReference.java +++ b/src/java/org/apache/poi/hssf/util/AreaReference.java @@ -17,293 +17,21 @@ package org.apache.poi.hssf.util; -import java.util.ArrayList; -import java.util.StringTokenizer; - -import org.apache.poi.hssf.record.formula.AreaI; - -public final class AreaReference { - - /** The character (!) that separates sheet names from cell references */ - private static final char SHEET_NAME_DELIMITER = '!'; - /** The character (:) that separates the two cell references in a multi-cell area reference */ - private static final char CELL_DELIMITER = ':'; - /** The character (') used to quote sheet names when they contain special characters */ - private static final char SPECIAL_NAME_DELIMITER = '\''; - - private final CellReference _firstCell; - private final CellReference _lastCell; - private final boolean _isSingleCell; +public final class AreaReference extends org.apache.poi.ss.util.AreaReference { /** * Create an area ref from a string representation. Sheet names containing special characters should be * delimited and escaped as per normal syntax rules for formulas.
* The area reference must be contiguous (i.e. represent a single rectangle, not a union of rectangles) */ public AreaReference(String reference) { - if(! isContiguous(reference)) { - throw new IllegalArgumentException( - "References passed to the AreaReference must be contiguous, " + - "use generateContiguous(ref) if you have non-contiguous references"); - } - - String[] parts = separateAreaRefs(reference); - - // Special handling for whole-column references - if(parts.length == 2 && parts[0].length() == 1 && - parts[1].length() == 1 && - parts[0].charAt(0) >= 'A' && parts[0].charAt(0) <= 'Z' && - parts[1].charAt(0) >= 'A' && parts[1].charAt(0) <= 'Z') { - // Represented internally as x$1 to x$65536 - // which is the maximum range of rows - parts[0] = parts[0] + "$1"; - parts[1] = parts[1] + "$65536"; - } - - _firstCell = new CellReference(parts[0]); - - if(parts.length == 2) { - _lastCell = new CellReference(parts[1]); - _isSingleCell = false; - } else { - _lastCell = _firstCell; - _isSingleCell = true; - } + super(reference); } /** * Creates an area ref from a pair of Cell References. */ public AreaReference(CellReference topLeft, CellReference botRight) { - _firstCell = topLeft; - _lastCell = botRight; - _isSingleCell = false; + super(topLeft, botRight); } - - /** - * Is the reference for a contiguous (i.e. - * unbroken) area, or is it made up of - * several different parts? - * (If it is, you will need to call - * .... - */ - public static boolean isContiguous(String reference) { - if(reference.indexOf(',') == -1) { - return true; - } - return false; - } - - /** - * Is the reference for a whole-column reference, - * such as C:C or D:G ? - */ - public static boolean isWholeColumnReference(CellReference topLeft, CellReference botRight) { - // These are represented as something like - // C$1:C$65535 or D$1:F$0 - // i.e. absolute from 1st row to 0th one - if(topLeft.getRow() == 0 && topLeft.isRowAbsolute() && - botRight.getRow() == 65535 && botRight.isRowAbsolute()) { - return true; - } - return false; - } - public boolean isWholeColumnReference() { - return isWholeColumnReference(_firstCell, _lastCell); - } - - /** - * Takes a non-contiguous area reference, and - * returns an array of contiguous area references. - */ - public static AreaReference[] generateContiguous(String reference) { - ArrayList refs = new ArrayList(); - StringTokenizer st = new StringTokenizer(reference, ","); - while(st.hasMoreTokens()) { - refs.add( - new AreaReference(st.nextToken()) - ); - } - return (AreaReference[])refs.toArray(new AreaReference[refs.size()]); - } - - /** - * @return false if this area reference involves more than one cell - */ - public boolean isSingleCell() { - return _isSingleCell; - } - - /** - * @return the first cell reference which defines this area. Usually this cell is in the upper - * left corner of the area (but this is not a requirement). - */ - public CellReference getFirstCell() { - return _firstCell; - } - - /** - * Note - if this area reference refers to a single cell, the return value of this method will - * be identical to that of getFirstCell() - * @return the second cell reference which defines this area. For multi-cell areas, this is - * cell diagonally opposite the 'first cell'. Usually this cell is in the lower right corner - * of the area (but this is not a requirement). - */ - public CellReference getLastCell() { - return _lastCell; - } - /** - * Returns a reference to every cell covered by this area - */ - public CellReference[] getAllReferencedCells() { - // Special case for single cell reference - if(_isSingleCell) { - return new CellReference[] { _firstCell, }; - } - - // Interpolate between the two - int minRow = Math.min(_firstCell.getRow(), _lastCell.getRow()); - int maxRow = Math.max(_firstCell.getRow(), _lastCell.getRow()); - int minCol = Math.min(_firstCell.getCol(), _lastCell.getCol()); - int maxCol = Math.max(_firstCell.getCol(), _lastCell.getCol()); - String sheetName = _firstCell.getSheetName(); - - ArrayList refs = new ArrayList(); - for(int row=minRow; row<=maxRow; row++) { - for(int col=minCol; col<=maxCol; col++) { - CellReference ref = new CellReference(sheetName, row, col, _firstCell.isRowAbsolute(), _firstCell.isColAbsolute()); - refs.add(ref); - } - } - return (CellReference[])refs.toArray(new CellReference[refs.size()]); - } - - /** - * Example return values: - * - * - * - * - * - * - *
ResultComment
A1:A1Single cell area reference without sheet
A1:$C$1Multi-cell area reference without sheet
Sheet1!A$1:B4Standard sheet name
'O''Brien''s Sales'!B5:C6' Sheet name with special characters
- * @return the text representation of this area reference as it would appear in a formula. - */ - public String formatAsString() { - // Special handling for whole-column references - if(isWholeColumnReference()) { - return - CellReference.convertNumToColString(_firstCell.getCol()) - + ":" + - CellReference.convertNumToColString(_lastCell.getCol()); - } - - StringBuffer sb = new StringBuffer(32); - sb.append(_firstCell.formatAsString()); - if(!_isSingleCell) { - sb.append(CELL_DELIMITER); - if(_lastCell.getSheetName() == null) { - sb.append(_lastCell.formatAsString()); - } else { - // don't want to include the sheet name twice - _lastCell.appendCellReference(sb); - } - } - return sb.toString(); - } - /** - * Formats a 2-D area as it would appear in a formula. See formatAsString() (no-arg) - */ - public static String formatAsString(AreaI area) { - CellReference topLeft = new CellReference(area.getFirstRow(),area.getFirstColumn(),!area.isFirstRowRelative(),!area.isFirstColRelative()); - CellReference botRight = new CellReference(area.getLastRow(),area.getLastColumn(),!area.isLastRowRelative(),!area.isLastColRelative()); - - if(isWholeColumnReference(topLeft, botRight)) { - return (new AreaReference(topLeft, botRight)).formatAsString(); - } - return topLeft.formatAsString() + ":" + botRight.formatAsString(); - } - public String toString() { - StringBuffer sb = new StringBuffer(64); - sb.append(getClass().getName()).append(" ["); - sb.append(formatAsString()); - sb.append("]"); - return sb.toString(); - } - - /** - * Separates Area refs in two parts and returns them as separate elements in a String array, - * each qualified with the sheet name (if present) - * - * @return array with one or two elements. never null - */ - private static String[] separateAreaRefs(String reference) { - // TODO - refactor cell reference parsing logic to one place. - // Current known incarnations: - // FormulaParser.GetName() - // CellReference.separateRefParts() - // AreaReference.separateAreaRefs() (here) - // SheetNameFormatter.format() (inverse) - - - int len = reference.length(); - int delimiterPos = -1; - boolean insideDelimitedName = false; - for(int i=0; i=0) { - throw new IllegalArgumentException("More than one cell delimiter '" - + CELL_DELIMITER + "' appears in area reference '" + reference + "'"); - } - delimiterPos = i; - } - default: - continue; - case SPECIAL_NAME_DELIMITER: - // fall through - } - if(!insideDelimitedName) { - insideDelimitedName = true; - continue; - } - - if(i >= len-1) { - // reference ends with the delimited name. - // Assume names like: "Sheet1!'A1'" are never legal. - throw new IllegalArgumentException("Area reference '" + reference - + "' ends with special name delimiter '" + SPECIAL_NAME_DELIMITER + "'"); - } - if(reference.charAt(i+1) == SPECIAL_NAME_DELIMITER) { - // two consecutive quotes is the escape sequence for a single one - i++; // skip this and keep parsing the special name - } else { - // this is the end of the delimited name - insideDelimitedName = false; - } - } - if(delimiterPos < 0) { - return new String[] { reference, }; - } - - String partA = reference.substring(0, delimiterPos); - String partB = reference.substring(delimiterPos+1); - if(partB.indexOf(SHEET_NAME_DELIMITER) >=0) { - // TODO - are references like "Sheet1!A1:Sheet1:B2" ever valid? - // FormulaParser has code to handle that. - - throw new RuntimeException("Unexpected " + SHEET_NAME_DELIMITER - + " in second cell reference of '" + reference + "'"); - } - - int plingPos = partA.lastIndexOf(SHEET_NAME_DELIMITER); - if(plingPos < 0) { - return new String [] { partA, partB, }; - } - - String sheetName = partA.substring(0, plingPos + 1); // +1 to include delimiter - - return new String [] { partA, sheetName + partB, }; - } -} \ No newline at end of file +} diff --git a/src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java b/src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java index 74e92b0a8..aa71c68c5 100644 --- a/src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java +++ b/src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java @@ -1,26 +1,23 @@ -/* -* 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.ss.usermodel; -import java.lang.reflect.Constructor; -import java.util.HashMap; import java.util.Iterator; -import java.util.Map; import java.util.Stack; import org.apache.poi.hssf.model.FormulaParser; @@ -28,6 +25,7 @@ import org.apache.poi.hssf.record.formula.Area3DPtg; import org.apache.poi.hssf.record.formula.AreaPtg; import org.apache.poi.hssf.record.formula.BoolPtg; import org.apache.poi.hssf.record.formula.ControlPtg; +import org.apache.poi.hssf.record.formula.ErrPtg; import org.apache.poi.hssf.record.formula.IntPtg; import org.apache.poi.hssf.record.formula.MemErrPtg; import org.apache.poi.hssf.record.formula.MissingArgPtg; @@ -41,77 +39,51 @@ import org.apache.poi.hssf.record.formula.RefPtg; import org.apache.poi.hssf.record.formula.StringPtg; import org.apache.poi.hssf.record.formula.UnionPtg; import org.apache.poi.hssf.record.formula.UnknownPtg; -import org.apache.poi.hssf.record.formula.eval.Area2DEval; -import org.apache.poi.hssf.record.formula.eval.Area3DEval; import org.apache.poi.hssf.record.formula.eval.AreaEval; import org.apache.poi.hssf.record.formula.eval.BlankEval; import org.apache.poi.hssf.record.formula.eval.BoolEval; import org.apache.poi.hssf.record.formula.eval.ErrorEval; import org.apache.poi.hssf.record.formula.eval.Eval; import org.apache.poi.hssf.record.formula.eval.FunctionEval; +import org.apache.poi.hssf.record.formula.eval.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; import org.apache.poi.hssf.record.formula.eval.OperationEval; -import org.apache.poi.hssf.record.formula.eval.Ref2DEval; -import org.apache.poi.hssf.record.formula.eval.Ref3DEval; import org.apache.poi.hssf.record.formula.eval.RefEval; import org.apache.poi.hssf.record.formula.eval.StringEval; import org.apache.poi.hssf.record.formula.eval.ValueEval; -import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * + * */ public class FormulaEvaluator { - - // params to lookup the right constructor using reflection - private static final Class[] VALUE_CONTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class }; - private static final Class[] AREA3D_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class, ValueEval[].class }; - - private static final Class[] REFERENCE_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class, ValueEval.class }; - - private static final Class[] REF3D_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class, ValueEval.class }; - - // Maps for mapping *Eval to *Ptg - private static final Map VALUE_EVALS_MAP = new HashMap(); - - /* - * Following is the mapping between the Ptg tokens returned - * by the FormulaParser and the *Eval classes that are used - * by the FormulaEvaluator - */ - static { - VALUE_EVALS_MAP.put(BoolPtg.class, BoolEval.class); - VALUE_EVALS_MAP.put(IntPtg.class, NumberEval.class); - VALUE_EVALS_MAP.put(NumberPtg.class, NumberEval.class); - VALUE_EVALS_MAP.put(StringPtg.class, StringEval.class); - - } - - protected Sheet _sheet; protected Workbook _workbook; - + public FormulaEvaluator(Sheet sheet, Workbook workbook) { - this._sheet = sheet; - this._workbook = workbook; + _sheet = sheet; + _workbook = workbook; } - + /** * Does nothing * @deprecated (Aug 2008) - not needed, since the current row can be derived from the cell */ - public void setCurrentRow(Row row) {} + public void setCurrentRow(Row row) { + // do nothing + } + /** * If cell contains a formula, the formula is evaluated and returned, * else the CellValue simply copies the appropriate cell value from * the cell and also its cell type. This method should be preferred over * evaluateInCell() when the call should not modify the contents of the - * original cell. + * original cell. * @param cell */ public CellValue evaluate(Cell cell) { @@ -144,25 +116,25 @@ public class FormulaEvaluator { } return retval; } - - + + /** * If cell contains formula, it evaluates the formula, * and saves the result of the formula. The cell * remains as a formula cell. * Else if cell does not contain formula, this method leaves - * the cell unchanged. + * the cell unchanged. * Note that the type of the formula result is returned, * so you know what kind of value is also stored with - * the formula. + * the formula. *

      * int evaluatedCellType = evaluator.evaluateFormulaCell(cell);
      * 
* Be aware that your cell will hold both the formula, * and the result. If you want the cell replaced with - * the result of the formula, use {@link #evaluateInCell(HSSFCell)} + * the result of the formula, use {@link #evaluateInCell(Cell)} * @param cell The cell to evaluate - * @return The type of the formula result (the cell's type remains as HSSFCell.CELL_TYPE_FORMULA however) + * @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) { @@ -192,21 +164,21 @@ public class FormulaEvaluator { } return -1; } - + /** * If cell contains formula, it evaluates the formula, and * puts the formula result back into the cell, in place * of the old formula. * Else if cell does not contain formula, this method leaves - * the cell unchanged. - * Note that the same instance of HSSFCell is returned to + * the cell unchanged. + * Note that the same instance of Cell is returned to * allow chained calls like: *
      * int evaluatedCellType = evaluator.evaluateInCell(cell).getCellType();
      * 
* Be aware that your cell value will be changed to hold the * result of the formula. If you simply want the formula - * value computed for you, use {@link #evaluateFormulaCell(HSSFCell)} + * value computed for you, use {@link #evaluateFormulaCell(Cell)} * @param cell */ public Cell evaluateInCell(Cell cell) { @@ -239,7 +211,7 @@ public class FormulaEvaluator { } return cell; } - + /** * Loops over all cells in all sheets of the supplied * workbook. @@ -248,32 +220,32 @@ public class FormulaEvaluator { * remain as formula cells. * For cells that do not contain formulas, no changes * are made. - * This is a helpful wrapper around looping over all + * This is a helpful wrapper around looping over all * cells, and calling evaluateFormulaCell on each one. */ - public static void evaluateAllFormulaCells(Workbook wb) { - for(int i=0; iNumberEval, StringEval, BoolEval, - * BlankEval or ErrorEval. Never null. + * @return a NumberEval, StringEval, BoolEval, + * BlankEval or ErrorEval. Never null. */ private static ValueEval dereferenceValue(ValueEval evaluationResult, int srcRowNum, short srcColNum) { if (evaluationResult instanceof RefEval) { @@ -460,107 +402,48 @@ public class FormulaEvaluator { } return operation.evaluate(ops, srcRowNum, srcColNum); } - - public static AreaEval evaluateAreaPtg(Sheet sheet, Workbook workbook, AreaPtg ap) { - int row0 = ap.getFirstRow(); - int col0 = ap.getFirstColumn(); - int row1 = ap.getLastRow(); - int col1 = ap.getLastColumn(); - - // If the last row is -1, then the - // reference is for the rest of the column - // (eg C:C) - // TODO: Handle whole column ranges properly - if(row1 == -1 && row0 >= 0) { - row1 = (short)sheet.getLastRowNum(); - } - ValueEval[] values = evalArea(workbook, sheet, row0, col0, row1, col1); - return new Area2DEval(ap, values); - } - - public static AreaEval evaluateArea3dPtg(Workbook workbook, Area3DPtg a3dp) { - int row0 = a3dp.getFirstRow(); - int col0 = a3dp.getFirstColumn(); - int row1 = a3dp.getLastRow(); - int col1 = a3dp.getLastColumn(); - Sheet xsheet = workbook.getSheetAt( - workbook.getSheetIndexFromExternSheetIndex(a3dp.getExternSheetIndex()) - ); - - // If the last row is -1, then the - // reference is for the rest of the column - // (eg C:C) - // TODO: Handle whole column ranges properly - if(row1 == -1 && row0 >= 0) { - row1 = (short)xsheet.getLastRowNum(); - } - - ValueEval[] values = evalArea(workbook, xsheet, row0, col0, row1, col1); - return new Area3DEval(a3dp, values); - } - - private static ValueEval[] evalArea(Workbook workbook, Sheet sheet, - int row0, int col0, int row1, int col1) { - ValueEval[] values = new ValueEval[(row1 - row0 + 1) * (col1 - col0 + 1)]; - for (int x = row0; sheet != null && x < row1 + 1; x++) { - Row row = sheet.getRow(x); - for (int y = col0; y < col1 + 1; y++) { - ValueEval cellEval; - if(row == null) { - cellEval = BlankEval.INSTANCE; - } else { - cellEval = getEvalForCell(row.getCell(y), row, sheet, workbook); - } - values[(x - row0) * (col1 - col0 + 1) + (y - col0)] = cellEval; - } - } - return values; - } /** * returns an appropriate Eval impl instance for the Ptg. The Ptg must be - * one of: Area3DPtg, AreaPtg, RefPtg, Ref3DPtg, IntPtg, NumberPtg, + * one of: Area3DPtg, AreaPtg, ReferencePtg, Ref3DPtg, IntPtg, NumberPtg, * StringPtg, BoolPtg
special Note: OperationPtg subtypes cannot be * passed here! - * - * @param ptg */ - protected static Eval getEvalForPtg(Ptg ptg) { - Eval retval = null; - - Class clazz = (Class) VALUE_EVALS_MAP.get(ptg.getClass()); - try { - if (ptg instanceof Area3DPtg) { - Constructor constructor = clazz.getConstructor(AREA3D_CONSTRUCTOR_CLASS_ARRAY); - retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg }); - } - else if (ptg instanceof AreaPtg) { - Constructor constructor = clazz.getConstructor(AREA3D_CONSTRUCTOR_CLASS_ARRAY); - retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg }); - } - else if (ptg instanceof RefPtg) { - Constructor constructor = clazz.getConstructor(REFERENCE_CONSTRUCTOR_CLASS_ARRAY); - retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg }); - } - else if (ptg instanceof Ref3DPtg) { - Constructor constructor = clazz.getConstructor(REF3D_CONSTRUCTOR_CLASS_ARRAY); - retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg }); - } - else { - if (ptg instanceof IntPtg || ptg instanceof NumberPtg || ptg instanceof StringPtg - || ptg instanceof BoolPtg) { - Constructor constructor = clazz.getConstructor(VALUE_CONTRUCTOR_CLASS_ARRAY); - retval = (ValueEval) constructor.newInstance(new Ptg[] { ptg }); - } - } + private static Eval getEvalForPtg(Ptg ptg, Sheet sheet, Workbook workbook) { + if (ptg instanceof RefPtg) { + return new LazyRefEval(((RefPtg) ptg), sheet, workbook); } - catch (Exception e) { - throw new RuntimeException("Fatal Error: ", e); + if (ptg instanceof Ref3DPtg) { + Ref3DPtg refPtg = (Ref3DPtg) ptg; + Sheet xsheet = workbook.getSheetAt(workbook.getSheetIndexFromExternSheetIndex(refPtg.getExternSheetIndex())); + return new LazyRefEval(refPtg, xsheet, workbook); + } + if (ptg instanceof AreaPtg) { + return new LazyAreaEval(((AreaPtg) ptg), sheet, workbook); + } + if (ptg instanceof Area3DPtg) { + Area3DPtg a3dp = (Area3DPtg) ptg; + Sheet xsheet = workbook.getSheetAt(workbook.getSheetIndexFromExternSheetIndex(a3dp.getExternSheetIndex())); + return new LazyAreaEval(a3dp, xsheet, workbook); } - return retval; + if (ptg instanceof IntPtg) { + return new NumberEval(((IntPtg)ptg).getValue()); + } + if (ptg instanceof NumberPtg) { + return new NumberEval(((NumberPtg)ptg).getValue()); + } + if (ptg instanceof StringPtg) { + return new StringEval(((StringPtg) ptg).getValue()); + } + if (ptg instanceof BoolPtg) { + return BoolEval.valueOf(((BoolPtg) ptg).getValue()); + } + if (ptg instanceof ErrPtg) { + return ErrorEval.valueOf(((ErrPtg) ptg).getErrorCode()); + } + throw new RuntimeException("Unexpected ptg class (" + ptg.getClass().getName() + ")"); } - /** * Given a cell, find its type and from that create an appropriate ValueEval * impl instance and return that. Since the cell could be an external @@ -570,7 +453,7 @@ public class FormulaEvaluator { * @param sheet * @param workbook */ - protected static ValueEval getEvalForCell(Cell cell, Row row, Sheet sheet, Workbook workbook) { + public static ValueEval getEvalForCell(Cell cell, Sheet sheet, Workbook workbook) { if (cell == null) { return BlankEval.INSTANCE; @@ -593,73 +476,21 @@ public class FormulaEvaluator { } /** - * Creates a Ref2DEval for RefPtg. - * Non existent cells are treated as RefEvals containing BlankEval. - */ - private static Ref2DEval createRef2DEval(RefPtg ptg, Cell cell, - Sheet sheet, Workbook workbook) { - if (cell == null) { - return new Ref2DEval(ptg, BlankEval.INSTANCE); - } - - switch (cell.getCellType()) { - case Cell.CELL_TYPE_NUMERIC: - return new Ref2DEval(ptg, new NumberEval(cell.getNumericCellValue())); - case Cell.CELL_TYPE_STRING: - return new Ref2DEval(ptg, new StringEval(cell.getRichStringCellValue().getString())); - case Cell.CELL_TYPE_FORMULA: - return new Ref2DEval(ptg, internalEvaluate(cell, sheet, workbook)); - case Cell.CELL_TYPE_BOOLEAN: - return new Ref2DEval(ptg, BoolEval.valueOf(cell.getBooleanCellValue())); - case Cell.CELL_TYPE_BLANK: - return new Ref2DEval(ptg, BlankEval.INSTANCE); - case Cell.CELL_TYPE_ERROR: - return new Ref2DEval(ptg, ErrorEval.valueOf(cell.getErrorCellValue())); - } - throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")"); - } - - /** - * create a Ref3DEval for Ref3DPtg. - */ - private static Ref3DEval createRef3DEval(Ref3DPtg ptg, Cell cell, - Sheet sheet, Workbook workbook) { - if (cell == null) { - return new Ref3DEval(ptg, BlankEval.INSTANCE); - } - switch (cell.getCellType()) { - case Cell.CELL_TYPE_NUMERIC: - return new Ref3DEval(ptg, new NumberEval(cell.getNumericCellValue())); - case Cell.CELL_TYPE_STRING: - return new Ref3DEval(ptg, new StringEval(cell.getRichStringCellValue().getString())); - case Cell.CELL_TYPE_FORMULA: - return new Ref3DEval(ptg, internalEvaluate(cell, sheet, workbook)); - case Cell.CELL_TYPE_BOOLEAN: - return new Ref3DEval(ptg, BoolEval.valueOf(cell.getBooleanCellValue())); - case Cell.CELL_TYPE_BLANK: - return new Ref3DEval(ptg, BlankEval.INSTANCE); - case Cell.CELL_TYPE_ERROR: - return new Ref3DEval(ptg, ErrorEval.valueOf(cell.getErrorCellValue())); - } - throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")"); - } - - /** - * Mimics the 'data view' of a cell. This allows formula evaluator + * Mimics the 'data view' of a cell. This allows formula evaluator * to return a CellValue instead of precasting the value to String * or Number or boolean type. * @author Amol S. Deshmukh < amolweb at ya hoo dot com > */ public static class CellValue { - private CreationHelper creationHelper; + private CreationHelper creationHelper; private int cellType; private RichTextString richTextStringValue; private double numberValue; private boolean booleanValue; private byte errorValue; - + /** - * CellType should be one of the types defined in HSSFCell + * CellType should be one of the types defined in Cell * @param cellType */ public CellValue(int cellType, CreationHelper creationHelper) { @@ -705,8 +536,8 @@ public class FormulaEvaluator { * @deprecated */ public void setStringValue(String stringValue) { - this.richTextStringValue = - creationHelper.createRichTextString(stringValue); + this.richTextStringValue = + creationHelper.createRichTextString(stringValue); } /** * @return Returns the cellType. diff --git a/src/java/org/apache/poi/ss/util/AreaReference.java b/src/java/org/apache/poi/ss/util/AreaReference.java new file mode 100644 index 000000000..b5e06b9af --- /dev/null +++ b/src/java/org/apache/poi/ss/util/AreaReference.java @@ -0,0 +1,296 @@ +/* ==================================================================== + 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.util; + +import java.util.ArrayList; +import java.util.StringTokenizer; + +public class AreaReference { + + /** The character (!) that separates sheet names from cell references */ + private static final char SHEET_NAME_DELIMITER = '!'; + /** The character (:) that separates the two cell references in a multi-cell area reference */ + private static final char CELL_DELIMITER = ':'; + /** The character (') used to quote sheet names when they contain special characters */ + private static final char SPECIAL_NAME_DELIMITER = '\''; + + private final CellReference _firstCell; + private final CellReference _lastCell; + private final boolean _isSingleCell; + + /** + * Create an area ref from a string representation. Sheet names containing special characters should be + * delimited and escaped as per normal syntax rules for formulas.
+ * The area reference must be contiguous (i.e. represent a single rectangle, not a union of rectangles) + */ + public AreaReference(String reference) { + if(! isContiguous(reference)) { + throw new IllegalArgumentException( + "References passed to the AreaReference must be contiguous, " + + "use generateContiguous(ref) if you have non-contiguous references"); + } + + String[] parts = separateAreaRefs(reference); + + // Special handling for whole-column references + if(parts.length == 2 && parts[0].length() == 1 && + parts[1].length() == 1 && + parts[0].charAt(0) >= 'A' && parts[0].charAt(0) <= 'Z' && + parts[1].charAt(0) >= 'A' && parts[1].charAt(0) <= 'Z') { + // Represented internally as x$1 to x$65536 + // which is the maximum range of rows + parts[0] = parts[0] + "$1"; + parts[1] = parts[1] + "$65536"; + } + + _firstCell = new CellReference(parts[0]); + + if(parts.length == 2) { + _lastCell = new CellReference(parts[1]); + _isSingleCell = false; + } else { + _lastCell = _firstCell; + _isSingleCell = true; + } + } + + /** + * Creates an area ref from a pair of Cell References. + */ + public AreaReference(CellReference topLeft, CellReference botRight) { + _firstCell = topLeft; + _lastCell = botRight; + _isSingleCell = false; + } + + /** + * Is the reference for a contiguous (i.e. + * unbroken) area, or is it made up of + * several different parts? + * (If it is, you will need to call + * .... + */ + public static boolean isContiguous(String reference) { + if(reference.indexOf(',') == -1) { + return true; + } + return false; + } + + /** + * Is the reference for a whole-column reference, + * such as C:C or D:G ? + */ + public static boolean isWholeColumnReference(CellReference topLeft, CellReference botRight) { + // These are represented as something like + // C$1:C$65535 or D$1:F$0 + // i.e. absolute from 1st row to 0th one + if(topLeft.getRow() == 0 && topLeft.isRowAbsolute() && + botRight.getRow() == 65535 && botRight.isRowAbsolute()) { + return true; + } + return false; + } + public boolean isWholeColumnReference() { + return isWholeColumnReference(_firstCell, _lastCell); + } + + /** + * Takes a non-contiguous area reference, and + * returns an array of contiguous area references. + */ + public static AreaReference[] generateContiguous(String reference) { + ArrayList refs = new ArrayList(); + StringTokenizer st = new StringTokenizer(reference, ","); + while(st.hasMoreTokens()) { + refs.add( + new AreaReference(st.nextToken()) + ); + } + return (AreaReference[])refs.toArray(new AreaReference[refs.size()]); + } + + /** + * @return false if this area reference involves more than one cell + */ + public boolean isSingleCell() { + return _isSingleCell; + } + + /** + * @return the first cell reference which defines this area. Usually this cell is in the upper + * left corner of the area (but this is not a requirement). + */ + public CellReference getFirstCell() { + return _firstCell; + } + + /** + * Note - if this area reference refers to a single cell, the return value of this method will + * be identical to that of getFirstCell() + * @return the second cell reference which defines this area. For multi-cell areas, this is + * cell diagonally opposite the 'first cell'. Usually this cell is in the lower right corner + * of the area (but this is not a requirement). + */ + public CellReference getLastCell() { + return _lastCell; + } + /** + * Returns a reference to every cell covered by this area + */ + public CellReference[] getAllReferencedCells() { + // Special case for single cell reference + if(_isSingleCell) { + return new CellReference[] { _firstCell, }; + } + + // Interpolate between the two + int minRow = Math.min(_firstCell.getRow(), _lastCell.getRow()); + int maxRow = Math.max(_firstCell.getRow(), _lastCell.getRow()); + int minCol = Math.min(_firstCell.getCol(), _lastCell.getCol()); + int maxCol = Math.max(_firstCell.getCol(), _lastCell.getCol()); + String sheetName = _firstCell.getSheetName(); + + ArrayList refs = new ArrayList(); + for(int row=minRow; row<=maxRow; row++) { + for(int col=minCol; col<=maxCol; col++) { + CellReference ref = new CellReference(sheetName, row, col, _firstCell.isRowAbsolute(), _firstCell.isColAbsolute()); + refs.add(ref); + } + } + return (CellReference[])refs.toArray(new CellReference[refs.size()]); + } + + /** + * Example return values: + * + * + * + * + * + * + *
ResultComment
A1:A1Single cell area reference without sheet
A1:$C$1Multi-cell area reference without sheet
Sheet1!A$1:B4Standard sheet name
'O''Brien''s Sales'!B5:C6' Sheet name with special characters
+ * @return the text representation of this area reference as it would appear in a formula. + */ + public String formatAsString() { + // Special handling for whole-column references + if(isWholeColumnReference()) { + return + CellReference.convertNumToColString(_firstCell.getCol()) + + ":" + + CellReference.convertNumToColString(_lastCell.getCol()); + } + + StringBuffer sb = new StringBuffer(32); + sb.append(_firstCell.formatAsString()); + if(!_isSingleCell) { + sb.append(CELL_DELIMITER); + if(_lastCell.getSheetName() == null) { + sb.append(_lastCell.formatAsString()); + } else { + // don't want to include the sheet name twice + _lastCell.appendCellReference(sb); + } + } + return sb.toString(); + } + + public String toString() { + StringBuffer sb = new StringBuffer(64); + sb.append(getClass().getName()).append(" ["); + sb.append(formatAsString()); + sb.append("]"); + return sb.toString(); + } + + /** + * Separates Area refs in two parts and returns them as separate elements in a String array, + * each qualified with the sheet name (if present) + * + * @return array with one or two elements. never null + */ + private static String[] separateAreaRefs(String reference) { + // TODO - refactor cell reference parsing logic to one place. + // Current known incarnations: + // FormulaParser.GetName() + // CellReference.separateRefParts() + // AreaReference.separateAreaRefs() (here) + // SheetNameFormatter.format() (inverse) + + + int len = reference.length(); + int delimiterPos = -1; + boolean insideDelimitedName = false; + for(int i=0; i=0) { + throw new IllegalArgumentException("More than one cell delimiter '" + + CELL_DELIMITER + "' appears in area reference '" + reference + "'"); + } + delimiterPos = i; + } + default: + continue; + case SPECIAL_NAME_DELIMITER: + // fall through + } + if(!insideDelimitedName) { + insideDelimitedName = true; + continue; + } + + if(i >= len-1) { + // reference ends with the delimited name. + // Assume names like: "Sheet1!'A1'" are never legal. + throw new IllegalArgumentException("Area reference '" + reference + + "' ends with special name delimiter '" + SPECIAL_NAME_DELIMITER + "'"); + } + if(reference.charAt(i+1) == SPECIAL_NAME_DELIMITER) { + // two consecutive quotes is the escape sequence for a single one + i++; // skip this and keep parsing the special name + } else { + // this is the end of the delimited name + insideDelimitedName = false; + } + } + if(delimiterPos < 0) { + return new String[] { reference, }; + } + + String partA = reference.substring(0, delimiterPos); + String partB = reference.substring(delimiterPos+1); + if(partB.indexOf(SHEET_NAME_DELIMITER) >=0) { + // TODO - are references like "Sheet1!A1:Sheet1:B2" ever valid? + // FormulaParser has code to handle that. + + throw new RuntimeException("Unexpected " + SHEET_NAME_DELIMITER + + " in second cell reference of '" + reference + "'"); + } + + int plingPos = partA.lastIndexOf(SHEET_NAME_DELIMITER); + if(plingPos < 0) { + return new String [] { partA, partB, }; + } + + String sheetName = partA.substring(0, plingPos + 1); // +1 to include delimiter + + return new String [] { partA, sheetName + partB, }; + } +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/dev/PPTXMLDump.java b/src/scratchpad/src/org/apache/poi/hslf/dev/PPTXMLDump.java index d02c4928e..8a68f8b37 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/dev/PPTXMLDump.java +++ b/src/scratchpad/src/org/apache/poi/hslf/dev/PPTXMLDump.java @@ -125,7 +125,7 @@ public class PPTXMLDump { dump(data, pos, size, padding); } else { //dump first 100 bytes of the atom data - dump(out, data, pos, size, padding, true); + dump(out, data, pos, Math.min(size, data.length-pos), padding, true); } padding--; write(out, "" + CR, padding); diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Hyperlink.java b/src/scratchpad/src/org/apache/poi/hslf/model/Hyperlink.java index 21a4dc59c..f0691c45c 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Hyperlink.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Hyperlink.java @@ -34,7 +34,14 @@ import java.util.Iterator; * @author Yegor Kozlov */ public class Hyperlink { + public static final byte LINK_NEXTSLIDE = InteractiveInfoAtom.LINK_NextSlide; + public static final byte LINK_PREVIOUSSLIDE = InteractiveInfoAtom.LINK_PreviousSlide; + public static final byte LINK_FIRSTSLIDE = InteractiveInfoAtom.LINK_FirstSlide; + public static final byte LINK_LASTSLIDE = InteractiveInfoAtom.LINK_LastSlide; + public static final byte LINK_URL = InteractiveInfoAtom.LINK_Url; + public static final byte LINK_NULL = InteractiveInfoAtom.LINK_NULL; + private int id=-1; private int type; private String address; private String title; @@ -42,7 +49,7 @@ public class Hyperlink { /** * Gets the type of the hyperlink action. - * Must be a ACTION_* constant defined in InteractiveInfoAtom + * Must be a LINK_* constant * * @return the hyperlink URL * @see InteractiveInfoAtom @@ -51,6 +58,32 @@ public class Hyperlink { return type; } + public void setType(int val) { + type = val; + switch(type){ + case LINK_NEXTSLIDE: + title = "NEXT"; + address = "1,-1,NEXT"; + break; + case LINK_PREVIOUSSLIDE: + title = "PREV"; + address = "1,-1,PREV"; + break; + case LINK_FIRSTSLIDE: + title = "FIRST"; + address = "1,-1,FIRST"; + break; + case LINK_LASTSLIDE: + title = "LAST"; + address = "1,-1,LAST"; + break; + default: + title = ""; + address = ""; + break; + } + } + /** * Gets the hyperlink URL * @@ -60,6 +93,18 @@ public class Hyperlink { return address; } + public void setAddress(String str) { + address = str; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + /** * Gets the hyperlink user-friendly title (if different from URL) * @@ -69,6 +114,10 @@ public class Hyperlink { return title; } + public void setTitle(String str) { + title = str; + } + /** * Gets the beginning character position * diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java b/src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java index 71d10948c..d5816b0aa 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java @@ -21,6 +21,8 @@ import org.apache.poi.ddf.*; import org.apache.poi.util.LittleEndian; import org.apache.poi.hslf.record.ColorSchemeAtom; import org.apache.poi.hslf.record.Record; +import org.apache.poi.hslf.record.InteractiveInfo; +import org.apache.poi.hslf.record.InteractiveInfoAtom; import org.apache.poi.hslf.exceptions.HSLFException; import java.awt.*; @@ -343,4 +345,57 @@ public class SimpleShape extends Shape { _clientData.setRemainingData(out.toByteArray()); } } + + public void setHyperlink(Hyperlink link){ + if(link.getId() == -1){ + throw new HSLFException("You must call SlideShow.addHyperlink(Hyperlink link) first"); + } + + EscherClientDataRecord cldata = new EscherClientDataRecord(); + cldata.setOptions((short)0xF); + getSpContainer().getChildRecords().add(cldata); + + InteractiveInfo info = new InteractiveInfo(); + InteractiveInfoAtom infoAtom = info.getInteractiveInfoAtom(); + + switch(link.getType()){ + case Hyperlink.LINK_FIRSTSLIDE: + infoAtom.setAction(InteractiveInfoAtom.ACTION_JUMP); + infoAtom.setJump(InteractiveInfoAtom.JUMP_FIRSTSLIDE); + infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_FirstSlide); + break; + case Hyperlink.LINK_LASTSLIDE: + infoAtom.setAction(InteractiveInfoAtom.ACTION_JUMP); + infoAtom.setJump(InteractiveInfoAtom.JUMP_LASTSLIDE); + infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_LastSlide); + break; + case Hyperlink.LINK_NEXTSLIDE: + infoAtom.setAction(InteractiveInfoAtom.ACTION_JUMP); + infoAtom.setJump(InteractiveInfoAtom.JUMP_NEXTSLIDE); + infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_NextSlide); + break; + case Hyperlink.LINK_PREVIOUSSLIDE: + infoAtom.setAction(InteractiveInfoAtom.ACTION_JUMP); + infoAtom.setJump(InteractiveInfoAtom.JUMP_PREVIOUSSLIDE); + infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_PreviousSlide); + break; + case Hyperlink.LINK_URL: + infoAtom.setAction(InteractiveInfoAtom.ACTION_HYPERLINK); + infoAtom.setJump(InteractiveInfoAtom.JUMP_NONE); + infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_Url); + break; + } + + infoAtom.setHyperlinkID(link.getId()); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + info.writeOut(out); + } catch(Exception e){ + throw new HSLFException(e); + } + cldata.setRemainingData(out.toByteArray()); + + } + } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/CharFlagsTextProp.java b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/CharFlagsTextProp.java index a78c2c27e..f03878153 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/CharFlagsTextProp.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/CharFlagsTextProp.java @@ -35,19 +35,19 @@ public class CharFlagsTextProp extends BitMaskTextProp { public static String NAME = "char_flags"; public CharFlagsTextProp() { super(2,0xffff, NAME, new String[] { - "bold", // 0x0001 - "italic", // 0x0002 - "underline", // 0x0004 - "char_unknown_1",// 0x0008 - "shadow", // 0x0010 - "char_unknown_2",// 0x0020 - "char_unknown_3",// 0x0040 - "char_unknown_4",// 0x0080 - "strikethrough", // 0x0100 - "relief", // 0x0200 - "reset_numbering", // 0x0400 - "enable_numbering_1", // 0x0800 - "enable_numbering_2", // 0x1000 + "bold", // 0x0001 A bit that specifies whether the characters are bold. + "italic", // 0x0002 A bit that specifies whether the characters are italicized. + "underline", // 0x0004 A bit that specifies whether the characters are underlined. + "char_unknown_1", // 0x0008 Undefined and MUST be ignored. + "shadow", // 0x0010 A bit that specifies whether the characters have a shadow effect. + "fehint", // 0x0020 A bit that specifies whether characters originated from double-byte input. + "char_unknown_2", // 0x0040 Undefined and MUST be ignored. + "kumi", // 0x0080 A bit that specifies whether Kumimoji are used for vertical text. + "strikethrough", // 0x0100 Undefined and MUST be ignored. + "emboss", // 0x0200 A bit that specifies whether the characters are embossed. + "char_unknown_3", // 0x0400 Undefined and MUST be ignored. + "char_unknown_4", // 0x0800 Undefined and MUST be ignored. + "char_unknown_5", // 0x1000 Undefined and MUST be ignored. } ); } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java index bcf4b997c..f876c7d38 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java @@ -34,6 +34,7 @@ public class TextPropCollection { private int charactersCovered; private short reservedField; private LinkedList textPropList; + private int maskSpecial = 0; /** Fetch the number of characters this styling applies to */ public int getCharactersCovered() { return charactersCovered; } @@ -94,21 +95,28 @@ public class TextPropCollection { // If we do, decode that, save it, and shuffle on for(int i=0; i= data.length) { - // Out of data, can't be any more properties to go - return bytesPassed; - } - + // Check if this property is found in the mask if((containsField & potentialProperties[i].getMask()) != 0) { + if(dataOffset+bytesPassed >= data.length) { + // Out of data, can't be any more properties to go + // remember the mask and return + maskSpecial |= potentialProperties[i].getMask(); + return bytesPassed; + } + // Bingo, data contains this property TextProp prop = (TextProp)potentialProperties[i].clone(); int val = 0; if(prop.getSize() == 2) { val = LittleEndian.getShort(data,dataOffset+bytesPassed); - } else { + } else if(prop.getSize() == 4){ val = LittleEndian.getInt(data,dataOffset+bytesPassed); - } + } else if (prop.getSize() == 0){ + //remember "special" bits. + maskSpecial |= potentialProperties[i].getMask(); + continue; + } prop.setValue(val); bytesPassed += prop.getSize(); textPropList.add(prop); @@ -161,15 +169,18 @@ public class TextPropCollection { } // Then the mask field - int mask = 0; + int mask = maskSpecial; for(int i=0; i8GoMl{X%Y#phX~75s)efDz|E)GLsl-Po+~fYCWP0T9`N)^MX>_IdZyT zGA~v6THbZGgW5SgtLJ#hsh>J$Q|GFdXzZjNU?*5z3+gLeP2Ag^qxKX%~Q~@x9B*c-mhVcnBIOq;m@7MlW3GzO21^!>*U+gPfVV*&4<-s zG$}9FEH^E6Y>k9G@ptL!|4P%(@UnctkSzaKrypR$^--267wL_WSfcE{&te%Q!Da*i z29ZX?pa(xc)+d;95WxzE*+9j|`m_NFy&)==ERQwZA2stnGfa7)a>0-oi!k+JjRaHm zw`JcqL}lr(pBs;jvwpFR27(!;uc}(Ux~_V0?V_@(6?H2b$?e9As$DY_FRoos`$AR8 z;)MgLA&_%j4%?ELi^<88zSM2d0PK?hM!8QmX zEfHa?1!4Fwg!e`u{39LV)Ds9D*$A`q5&k+AVaU@6+e#7U{|MnP)d+zl2rt&69p zSiBm+=|R}vh;Y=0P|=J~^9F+F9ctP|4f_$oA0io}L7!0W9D@Bk!n__7=Zx%ULR`Td zf5qzSI@WR8Kl_!b6889;*RJBcY)gyZyly;VuRL}A6TEeyPhPbCQ=a*|7TLZbj~|Hf z%5@udW51lSaXU|a-75z-?%;cTu=|*QhkSP=-jcT~Be+lY`j0D5n)ZE&jK@I6z7s5sZ>C;N zUoIojiG79KbI2{%_St#n37@>TZwjwC;+FgSCh%ceuRrT@zYfFxp_1_~(~1CDQ18^bB!iUCg5F)++*(V4(v2fW|M z)jkLHX`_8#HcH{JIz<=G2AAl~#pB%W@QHnSxU(S!R6jxO5#{P{>pNZ7I_X10~4qiHE{hGTdOQ6z|WHygv9h2IEJ60 zFJt)(oX9;}TEwmyXf?FT-pH+w=VEsgCJLzpllV^AD^f}@pYI{^!xDUs=|ajMOYnM} z)caBy6WiwEle~9bp6Hs36L^4%9xKNzzGszNOf9D|0U|XjffGLQMmg13wO;YJa{Pr9 zcsGxWvU!*%wG&f*Jr9#~`~)eyQ;FkwkQzU!#9S61651*p$C5+J#40?M!%{;crim1L z=ne{+=ni_{%@aRuLMuN*%{!a0koOSjZX(ycL=t3jof%S=$Y>tPJxAPP&3>H3E9j*j z+D}WPpL9$L)^m8lCwvNiPYS%Zjf;;I{4p;hvjqomCXc6F;{kk%dngh*fY0&{GC1NO zx>#;VR2`)06<;)p)`R#w@28=+58`C*(RqdS5SB7)NUT4EdHj~_Ro*{@*@KupB(An$ z3VS*vVuP5<3WH)CUHK(aEe+xrcBf6OQ4@#TM2njE4f#GABn{nbUhz$ko^dK!NtpmqOc9qo;<8P9;-*;dE5B#%H zbal{M+(Q!vcVZsP2#V>Qm?>4O#mhQr!u2-MN)aAUv)ejpb}r4n*@@%0T9VmCdIv~v zP8a60V4K*`g~jYxo6_Ee7Kxwv$S3;GV==c=ldYTH$}A$a-Rd4hdT)2*Xg*67?k3@W zy8oNq>i1Ib78yM%Y4eHF9{OBZq#mZYR!!#B+_&Kn34iY_Bt`knlr?x7_vW-e#2QL9isg|T@- zaXXI9=X2;y%r&tr?rHXlwI;TLS5oATiDh#iMaG!f0^UmGRWo~=FRgbg`SI*DDxNY{ zgZl@wTUM-5{__m`oXxU1Y*wqo;j%iNc@HB!&fuEmEK0W(+g#RSht-xxnCP%YLD{FK x6{R~09TS{|&I0E&r_Je7QK)2npAA99K9ku|4^ms?Uq3EiSUph*%wl%U{{RKO_t*dc delta 2518 zcmZuz4Nz3q6+Y+Qx3Ie`yHH_)2rD8g1khdnTm$GL_%jVLkkEz+Sh9^oh=dR^qG_jW zZEVS66n(~POeQ6n#?XnAYIouTld)(U#0sxwqzbDYfocubQete2&D8CAfFWs}op0Z_ z-#tIyzI*qb^&ByH4jWo?(Qq{HG@{<8V;h*E4d%SQ-*5(v@{7_>*l&a7e@oAr+pZr! zpovq9@+RFTbL*^AlhV1rg;2cjmTsKkDS6l!Eq|#uOtUz{6tl{ChLB0kDp%fPCI(T^ ziU7c1GSL`5^YK$dggF%vobWN5j`-9tce;eZI8`iKPBY$X_2@lj%$=z+Y_ysXmVdoQ zgt_{gWq)IwDogJ^9`d_I&XE_WAy{DLma0wLHdoiwZ78j3*xay#+};_{>{?(|O`W^$ z>8hfd?@up9!htX3z|tso1m)<5uIpQL07oSN`D9LnZ+4CpkK26lnb*HzHhp4qb5OMT9?Y zMfjizAz=r?qGp8N*ANB{Al!V1k`7bCF@%lXRJ;%2^?q9OBOLw{LeCWx&zsouB4rFy zee1W~*0VQ%?|X9latUAd9sKDQ&fCj`Z%qDF9Az6u z?TpIijArsRkmT!+{^EW3deQ7tw^?283`{G2H)J5&Z5XThh zs>Uo5rtop*7I_k;a^P-JtC6Q%e({EcCEP^H-!&zgh)sufp2*!wi4L8~O(CGyjt>A@ zK+nS9JVW?jt^YpS#rtRj_t7rhN4q?unZIg$MH3b4V$49)iErC6p(&Nz=)g5;4?s`k zJPQg~!G>UCV2EMCV557a=dgm3gCIj&hx~8E2M6N*FWv|?X)29tY59~EJefB1iB}2) zv^M%c3Wyta?BUSu6(2gVUG%48t#~p6x5qqAKcrNQ1v6+^Fnw)gwaPy-a3qRtR>k1M z_#m&R4G1d2CA_pu2v-q0`GD0ceo%y&e2B=ei!hykM&x=Ceuveo%CblBcBtek6I^7M zV-|1U^K(&Gj@f*ig5E2~WNxPoJYP`(3ro^qoC_SH`0!ylJF&~iJqNWP%tX36oRN*34ttw}$urHOZSH;1-T17H_dM}yw zPqtu1u!&L>go=9Y&;GCeu%;NEW#Chh`U0)Vy!}lu9fP%qhKR% zBNE$z%OqHPSrXstz-M?rnLXWskMbk57SoBVd57IEp6%>AKy=Y^_s%Y!Q`1wWZ zA<#B-xh^=MheQitSN?X|avLdACuN-aUF*dM*&0=Z_fbmWS1AT9#RzOB#V~nF?DSo~ z+edwq$dR{?wxx@{#<4z3V=h%(>%&BrtBMenu1-~PsOT{+$ega)sFSE_VGu}N`CBH9 zkOGgWmEs2W*m?pJrOh1TeygyJWoP(Ibfo?+GARUP{)cepNI+&f#*dQiJU0Sft zPrXxBk?F@IZYNijesXn=T($Ud5!XUKrVtKvmv8zpoh7Lv{t~WYiKoKyVMFw+a8kXjAza8^THzro3_7=H8KSddBGRD|52arlqGK3K9c;to zSnF9jOgG6%O7k#oXYs0H8bMPqhcvI4J&GHGiAb)5%i@($3?LJ)+`yE)P>i9`8%Ohu zPyLRko=z|+p^nz*i2*u#F{VW^D~Xz9MJtNWZ(vu*tgAGQZXuk!H8T`y_TR+Vrn`Ya zAdCvqcmf((Kyw&E31)(YKqCxje8IN}vj`D{NCJ&Dh$2K29v~zVW)tEFT1|Q&0gXF| zC(I=z5atnR^g$9~0bwDM_hjYmn^=i^>ZSc-^pwjCV4QMn3=dj@KDdog52DV*>a`)( zqLc(MDm18@F}M??kZfZtRu#L8lI!5(+X4vI{epW@Tj-(wl~KC8d}}qOz=ntw4k4$l1w1VO)M;+Y+Vg IG3L_!7ZRDl&j0`b diff --git a/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java b/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java index 3584e37ea..c9fea7f1e 100644 --- a/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java +++ b/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java @@ -24,6 +24,7 @@ import org.apache.poi.hssf.HSSFTestDataSamples; import org.apache.poi.hssf.model.FormulaParser.FormulaParseException; 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.AttrPtg; import org.apache.poi.hssf.record.formula.BoolPtg; @@ -836,4 +837,29 @@ public final class TestFormulaParser extends TestCase { } cell.setCellFormula("count(fp1)"); // plain cell ref, col is in range } -} + + public void testParseAreaRefHighRow_bug45358() { + Ptg[] ptgs; + AreaI aptg; + + HSSFWorkbook book = new HSSFWorkbook(); + book.createSheet("Sheet1"); + + ptgs = FormulaParser.parse("Sheet1!A10:A40000", book); + aptg = (AreaI) ptgs[0]; + if (aptg.getLastRow() == -25537) { + throw new AssertionFailedError("Identified bug 45358"); + } + assertEquals(39999, aptg.getLastRow()); + + ptgs = FormulaParser.parse("Sheet1!A10:A65536", book); + aptg = (AreaI) ptgs[0]; + assertEquals(65535, aptg.getLastRow()); + + // plain area refs should be ok too + ptgs = parseFormula("A10:A65536"); + aptg = (AreaI) ptgs[0]; + assertEquals(65535, aptg.getLastRow()); + + } +} \ No newline at end of file diff --git a/src/testcases/org/apache/poi/hssf/record/TestLinkedDataRecord.java b/src/testcases/org/apache/poi/hssf/record/TestLinkedDataRecord.java index bff51be2f..c7ef2acc1 100644 --- a/src/testcases/org/apache/poi/hssf/record/TestLinkedDataRecord.java +++ b/src/testcases/org/apache/poi/hssf/record/TestLinkedDataRecord.java @@ -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,8 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - - package org.apache.poi.hssf.record; @@ -33,9 +30,7 @@ import java.util.Stack; * * @author Glen Stampoultzis (glens at apache.org) */ -public class TestLinkedDataRecord - extends TestCase -{ +public final class TestLinkedDataRecord extends TestCase { /* The records below are records that would appear in a simple bar chart @@ -160,14 +155,7 @@ recordid = 0x1051, size =8 (byte)0x00,(byte)0x00, // index to last column and relative flags }; - public TestLinkedDataRecord(String name) - { - super(name); - } - - public void testLoad() - throws Exception - { + public void testLoad() { LinkedDataRecord record = new LinkedDataRecord(new TestcaseRecordInputStream((short)0x1051, (short)data.length, data)); assertEquals( LinkedDataRecord.LINK_TYPE_VALUES, record.getLinkType()); @@ -176,19 +164,11 @@ recordid = 0x1051, size =8 assertEquals( false, record.isCustomNumberFormat() ); assertEquals( 0, record.getIndexNumberFmtRecord()); - Area3DPtg ptg = new Area3DPtg(); - ptg.setExternSheetIndex((short)0); - ptg.setFirstColumn((short)0); - ptg.setLastColumn((short)0); - ptg.setFirstRow((short)0); - ptg.setLastRow((short)7936); - ptg.setFirstColRelative(false); - ptg.setLastColRelative(false); - ptg.setFirstRowRelative(false); - ptg.setLastRowRelative(false); - Stack s = new Stack(); - s.push(ptg); - assertEquals( s, record.getFormulaOfLink().getFormulaTokens() ); + Area3DPtg ptgExpected = new Area3DPtg(0, 7936, 0, 0, + false, false, false, false, 0); + + Object ptgActual = record.getFormulaOfLink().getFormulaTokens().get(0); + assertEquals(ptgExpected.toString(), ptgActual.toString()); assertEquals( data.length + 4, record.getRecordSize() ); @@ -196,24 +176,15 @@ recordid = 0x1051, size =8 } - public void testStore() - { + public void testStore() { LinkedDataRecord record = new LinkedDataRecord(); record.setLinkType( LinkedDataRecord.LINK_TYPE_VALUES ); record.setReferenceType( LinkedDataRecord.REFERENCE_TYPE_WORKSHEET ); record.setOptions( (short)0 ); record.setCustomNumberFormat( false ); record.setIndexNumberFmtRecord( (short)0 ); - Area3DPtg ptg = new Area3DPtg(); - ptg.setExternSheetIndex((short)0); - ptg.setFirstColumn((short)0); - ptg.setLastColumn((short)0); - ptg.setFirstRow((short)0); - ptg.setLastRow((short)7936); - ptg.setFirstColRelative(false); - ptg.setLastColRelative(false); - ptg.setFirstRowRelative(false); - ptg.setLastRowRelative(false); + Area3DPtg ptg = new Area3DPtg(0, 7936, 0, 0, + false, false, false, false, 0); Stack s = new Stack(); s.push(ptg); LinkedDataFormulaField formulaOfLink = new LinkedDataFormulaField(); diff --git a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestAreaEval.java b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestAreaEval.java index 6c855707b..71d15745e 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestAreaEval.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestAreaEval.java @@ -20,7 +20,8 @@ package org.apache.poi.hssf.record.formula.eval; import junit.framework.AssertionFailedError; import junit.framework.TestCase; -import org.apache.poi.hssf.record.formula.Area3DPtg; +import org.apache.poi.hssf.record.formula.AreaPtg; +import org.apache.poi.hssf.record.formula.functions.EvalFactory; /** * Tests for AreaEval @@ -30,8 +31,8 @@ import org.apache.poi.hssf.record.formula.Area3DPtg; public final class TestAreaEval extends TestCase { public void testGetValue_bug44950() { - - Area3DPtg ptg = new Area3DPtg("B2:D3", (short)0); + // TODO - this test probably isn't testing much anymore + AreaPtg ptg = new AreaPtg("B2:D3"); NumberEval one = new NumberEval(1); ValueEval[] values = { one, @@ -41,7 +42,7 @@ public final class TestAreaEval extends TestCase { new NumberEval(5), new NumberEval(6), }; - AreaEval ae = new Area3DEval(ptg, values); + AreaEval ae = EvalFactory.createAreaEval(ptg, values); if (one == ae.getValueAt(1, 2)) { throw new AssertionFailedError("Identified bug 44950 a"); } diff --git a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestUnaryPlusEval.java b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestUnaryPlusEval.java index dc20c0d8e..33c38a6c1 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestUnaryPlusEval.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestUnaryPlusEval.java @@ -18,22 +18,23 @@ package org.apache.poi.hssf.record.formula.eval; +import junit.framework.TestCase; + import org.apache.poi.hssf.record.formula.AreaPtg; import org.apache.poi.hssf.record.formula.UnaryPlusPtg; +import org.apache.poi.hssf.record.formula.functions.EvalFactory; import org.apache.poi.hssf.record.formula.functions.NumericFunctionInvoker; -import junit.framework.TestCase; - /** * Test for unary plus operator evaluator. - * + * * @author Josh Micich */ public final class TestUnaryPlusEval extends TestCase { - + /** * Test for bug observable at svn revision 618865 (5-Feb-2008)
- * The code for handling column operands had been copy-pasted from the row handling code. + * The code for handling column operands had been copy-pasted from the row handling code. */ public void testColumnOperand() { @@ -42,15 +43,14 @@ public final class TestUnaryPlusEval extends TestCase { short colNum = (short)5; AreaPtg areaPtg = new AreaPtg(firstRow, lastRow, colNum, colNum, false, false, false, false); ValueEval[] values = { - new NumberEval(27), - new NumberEval(29), - new NumberEval(35), // value in row 10 - new NumberEval(37), - new NumberEval(38), + new NumberEval(27), + new NumberEval(29), + new NumberEval(35), // value in row 10 + new NumberEval(37), + new NumberEval(38), }; - Eval areaEval = new Area2DEval(areaPtg, values); - Eval[] args = { - areaEval, + Eval[] args = { + EvalFactory.createAreaEval(areaPtg, values), }; double result = NumericFunctionInvoker.invoke(new UnaryPlusEval(UnaryPlusPtg.instance), args, 10, (short)20); diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/EvalFactory.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/EvalFactory.java index 51435d314..1c8d74f44 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/EvalFactory.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/EvalFactory.java @@ -14,17 +14,17 @@ 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.AreaPtg; +import org.apache.poi.hssf.record.formula.Ref3DPtg; import org.apache.poi.hssf.record.formula.RefPtg; -import org.apache.poi.hssf.record.formula.eval.Area2DEval; 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.NumberEval; -import org.apache.poi.hssf.record.formula.eval.Ref2DEval; import org.apache.poi.hssf.record.formula.eval.RefEval; +import org.apache.poi.hssf.record.formula.eval.RefEvalBase; import org.apache.poi.hssf.record.formula.eval.ValueEval; /** @@ -32,7 +32,7 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval; * * @author Josh Micich */ -final class EvalFactory { +public final class EvalFactory { private EvalFactory() { // no instances of this class @@ -44,6 +44,14 @@ final class EvalFactory { */ public static AreaEval createAreaEval(String areaRefStr, ValueEval[] values) { AreaPtg areaPtg = new AreaPtg(areaRefStr); + return createAreaEval(areaPtg, values); + } + + /** + * Creates a dummy AreaEval + * @param values empty (null) entries in this array will be converted to NumberEval.ZERO + */ + public static AreaEval createAreaEval(AreaPtg areaPtg, ValueEval[] values) { int nCols = areaPtg.getLastColumn() - areaPtg.getFirstColumn() + 1; int nRows = areaPtg.getLastRow() - areaPtg.getFirstRow() + 1; int nExpected = nRows * nCols; @@ -55,13 +63,57 @@ final class EvalFactory { values[i] = NumberEval.ZERO; } } - return new Area2DEval(areaPtg, values); + return new MockAreaEval(areaPtg, values); } /** * Creates a single RefEval (with value zero) */ public static RefEval createRefEval(String refStr) { - return new Ref2DEval(new RefPtg(refStr), NumberEval.ZERO); + return createRefEval(refStr, NumberEval.ZERO); } + public static RefEval createRefEval(String refStr, ValueEval value) { + return new MockRefEval(new RefPtg(refStr), value); + } + + private static final class MockAreaEval extends AreaEvalBase { + private final ValueEval[] _values; + public MockAreaEval(AreaPtg areaPtg, ValueEval[] values) { + super(areaPtg); + _values = values; + } + public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) { + if (relativeRowIndex < 0 || relativeRowIndex >=getHeight()) { + throw new IllegalArgumentException("row index out of range"); + } + int width = getWidth(); + if (relativeColumnIndex < 0 || relativeColumnIndex >=width) { + throw new IllegalArgumentException("column index out of range"); + } + int oneDimensionalIndex = relativeRowIndex * width + relativeColumnIndex; + return _values[oneDimensionalIndex]; + } + public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) { + throw new RuntimeException("Operation not implemented on this mock object"); + } + } + + private static final class MockRefEval extends RefEvalBase { + private final ValueEval _value; + public MockRefEval(RefPtg ptg, ValueEval value) { + super(ptg.getRow(), ptg.getColumn()); + _value = value; + } + public MockRefEval(Ref3DPtg ptg, ValueEval value) { + super(ptg.getRow(), ptg.getColumn()); + _value = value; + } + public ValueEval getInnerValueEval() { + return _value; + } + public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) { + throw new RuntimeException("Operation not implemented on this mock object"); + } + } + } diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestCountFuncs.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestCountFuncs.java index eca00872a..41d703e2c 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestCountFuncs.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestCountFuncs.java @@ -21,9 +21,7 @@ import junit.framework.AssertionFailedError; import junit.framework.TestCase; import org.apache.poi.hssf.HSSFTestDataSamples; -import org.apache.poi.hssf.record.formula.AreaPtg; import org.apache.poi.hssf.record.formula.RefPtg; -import org.apache.poi.hssf.record.formula.eval.Area2DEval; 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; @@ -91,7 +89,7 @@ public final class TestCountFuncs extends TestCase { BoolEval.TRUE, BlankEval.INSTANCE, }; - range = createAreaEval("A1:B3", values); + range = EvalFactory.createAreaEval("A1:B3", values); confirmCountIf(2, range, BoolEval.TRUE); // when criteria is numeric @@ -103,7 +101,7 @@ public final class TestCountFuncs extends TestCase { new NumberEval(2), BoolEval.TRUE, }; - range = createAreaEval("A1:B3", values); + range = EvalFactory.createAreaEval("A1:B3", values); confirmCountIf(3, range, new NumberEval(2)); // note - same results when criteria is a string that parses as the number with the same value confirmCountIf(3, range, new StringEval("2.00")); @@ -126,20 +124,15 @@ public final class TestCountFuncs extends TestCase { new NumberEval(25), new NumberEval(25), }; - Area2DEval arg0 = new Area2DEval(new AreaPtg("C1:C6"), values); + AreaEval arg0 = EvalFactory.createAreaEval("C1:C6", values); - Ref2DEval criteriaArg = new Ref2DEval(new RefPtg("A1"), new NumberEval(25)); + ValueEval criteriaArg = EvalFactory.createRefEval("A1", new NumberEval(25)); Eval[] args= { arg0, criteriaArg, }; double actual = NumericFunctionInvoker.invoke(new Countif(), args); assertEquals(4, actual, 0D); } - - private static AreaEval createAreaEval(String areaRefStr, ValueEval[] values) { - return new Area2DEval(new AreaPtg(areaRefStr), values); - } - private static void confirmCountA(int expected, Eval[] args) { double result = NumericFunctionInvoker.invoke(new Counta(), args); assertEquals(expected, result, 0); diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestIndex.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestIndex.java index 492d68e98..80c154596 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestIndex.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestIndex.java @@ -19,8 +19,7 @@ package org.apache.poi.hssf.record.formula.functions; import junit.framework.TestCase; -import org.apache.poi.hssf.record.formula.AreaPtg; -import org.apache.poi.hssf.record.formula.eval.Area2DEval; +import org.apache.poi.hssf.record.formula.eval.AreaEval; 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; @@ -68,7 +67,7 @@ public final class TestIndex extends TestCase { for (int i = 0; i < values.length; i++) { values[i] = new NumberEval(dValues[i]); } - Area2DEval arg0 = new Area2DEval(new AreaPtg(areaRefString), values); + AreaEval arg0 = EvalFactory.createAreaEval(areaRefString, values); Eval[] args; if (colNum > 0) { diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestMatch.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestMatch.java index d275e5f33..48104c22e 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestMatch.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestMatch.java @@ -19,8 +19,6 @@ package org.apache.poi.hssf.record.formula.functions; import junit.framework.TestCase; -import org.apache.poi.hssf.record.formula.AreaPtg; -import org.apache.poi.hssf.record.formula.eval.Area2DEval; import org.apache.poi.hssf.record.formula.eval.AreaEval; import org.apache.poi.hssf.record.formula.eval.BoolEval; import org.apache.poi.hssf.record.formula.eval.ErrorEval; @@ -32,7 +30,7 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval; /** * Test cases for MATCH() - * + * * @author Josh Micich */ public final class TestMatch extends TestCase { @@ -41,8 +39,8 @@ public final class TestMatch extends TestCase { private static final NumberEval MATCH_EXACT = new NumberEval(0); /** greater than or equal to */ private static final NumberEval MATCH_SMALLEST_GTE = new NumberEval(-1); - - + + private static Eval invokeMatch(Eval lookup_value, Eval lookup_array, Eval match_type) { Eval[] args = { lookup_value, lookup_array, match_type, }; return new Match().evaluate(args, -1, (short)-1); @@ -54,26 +52,19 @@ public final class TestMatch extends TestCase { NumericValueEval nve = (NumericValueEval)actualEval; assertEquals(expected, nve.getNumberValue(), 0); } - /** - * Convenience method - * @return new Area2DEval(new AreaPtg(ref), values) - */ - private static AreaEval createAreaEval(String ref, ValueEval[] values) { - return new Area2DEval(new AreaPtg(ref), values); - } - + public void testSimpleNumber() { - + ValueEval[] values = { - new NumberEval(4), - new NumberEval(5), - new NumberEval(10), - new NumberEval(10), - new NumberEval(25), + new NumberEval(4), + new NumberEval(5), + new NumberEval(10), + new NumberEval(10), + new NumberEval(25), }; - - AreaEval ae = createAreaEval("A1:A5", values); - + + AreaEval ae = EvalFactory.createAreaEval("A1:A5", values); + confirmInt(2, invokeMatch(new NumberEval(5), ae, MATCH_LARGEST_LTE)); confirmInt(2, invokeMatch(new NumberEval(5), ae, MATCH_EXACT)); confirmInt(4, invokeMatch(new NumberEval(10), ae, MATCH_LARGEST_LTE)); @@ -81,19 +72,19 @@ public final class TestMatch extends TestCase { confirmInt(4, invokeMatch(new NumberEval(20), ae, MATCH_LARGEST_LTE)); assertEquals(ErrorEval.NA, invokeMatch(new NumberEval(20), ae, MATCH_EXACT)); } - + public void testReversedNumber() { - + ValueEval[] values = { - new NumberEval(25), - new NumberEval(10), - new NumberEval(10), - new NumberEval(10), - new NumberEval(4), + new NumberEval(25), + new NumberEval(10), + new NumberEval(10), + new NumberEval(10), + new NumberEval(4), }; - - AreaEval ae = createAreaEval("A1:A5", values); - + + AreaEval ae = EvalFactory.createAreaEval("A1:A5", values); + confirmInt(2, invokeMatch(new NumberEval(10), ae, MATCH_SMALLEST_GTE)); confirmInt(2, invokeMatch(new NumberEval(10), ae, MATCH_EXACT)); confirmInt(4, invokeMatch(new NumberEval(9), ae, MATCH_SMALLEST_GTE)); @@ -101,19 +92,19 @@ public final class TestMatch extends TestCase { assertEquals(ErrorEval.NA, invokeMatch(new NumberEval(20), ae, MATCH_EXACT)); assertEquals(ErrorEval.NA, invokeMatch(new NumberEval(26), ae, MATCH_SMALLEST_GTE)); } - + public void testSimpleString() { - + ValueEval[] values = { - new StringEval("Albert"), - new StringEval("Charles"), - new StringEval("Ed"), - new StringEval("Greg"), - new StringEval("Ian"), + new StringEval("Albert"), + new StringEval("Charles"), + new StringEval("Ed"), + new StringEval("Greg"), + new StringEval("Ian"), }; - - AreaEval ae = createAreaEval("A1:A5", values); - + + AreaEval ae = EvalFactory.createAreaEval("A1:A5", values); + // Note String comparisons are case insensitive confirmInt(3, invokeMatch(new StringEval("Ed"), ae, MATCH_LARGEST_LTE)); confirmInt(3, invokeMatch(new StringEval("eD"), ae, MATCH_LARGEST_LTE)); @@ -122,90 +113,90 @@ public final class TestMatch extends TestCase { confirmInt(4, invokeMatch(new StringEval("Hugh"), ae, MATCH_LARGEST_LTE)); assertEquals(ErrorEval.NA, invokeMatch(new StringEval("Hugh"), ae, MATCH_EXACT)); } - + public void testSimpleBoolean() { - + ValueEval[] values = { - BoolEval.FALSE, - BoolEval.FALSE, - BoolEval.TRUE, - BoolEval.TRUE, + BoolEval.FALSE, + BoolEval.FALSE, + BoolEval.TRUE, + BoolEval.TRUE, }; - - AreaEval ae = createAreaEval("A1:A4", values); - + + AreaEval ae = EvalFactory.createAreaEval("A1:A4", values); + // Note String comparisons are case insensitive confirmInt(2, invokeMatch(BoolEval.FALSE, ae, MATCH_LARGEST_LTE)); confirmInt(1, invokeMatch(BoolEval.FALSE, ae, MATCH_EXACT)); confirmInt(4, invokeMatch(BoolEval.TRUE, ae, MATCH_LARGEST_LTE)); confirmInt(3, invokeMatch(BoolEval.TRUE, ae, MATCH_EXACT)); } - + public void testHeterogeneous() { - + ValueEval[] values = { - new NumberEval(4), - BoolEval.FALSE, + new NumberEval(4), + BoolEval.FALSE, new NumberEval(5), - new StringEval("Albert"), - BoolEval.FALSE, - BoolEval.TRUE, - new NumberEval(10), - new StringEval("Charles"), - new StringEval("Ed"), - new NumberEval(10), - new NumberEval(25), - BoolEval.TRUE, - new StringEval("Ed"), + new StringEval("Albert"), + BoolEval.FALSE, + BoolEval.TRUE, + new NumberEval(10), + new StringEval("Charles"), + new StringEval("Ed"), + new NumberEval(10), + new NumberEval(25), + BoolEval.TRUE, + new StringEval("Ed"), }; - - AreaEval ae = createAreaEval("A1:A13", values); - + + AreaEval ae = EvalFactory.createAreaEval("A1:A13", values); + assertEquals(ErrorEval.NA, invokeMatch(new StringEval("Aaron"), ae, MATCH_LARGEST_LTE)); - + confirmInt(5, invokeMatch(BoolEval.FALSE, ae, MATCH_LARGEST_LTE)); confirmInt(2, invokeMatch(BoolEval.FALSE, ae, MATCH_EXACT)); confirmInt(3, invokeMatch(new NumberEval(5), ae, MATCH_LARGEST_LTE)); confirmInt(3, invokeMatch(new NumberEval(5), ae, MATCH_EXACT)); - + confirmInt(8, invokeMatch(new StringEval("CHARLES"), ae, MATCH_EXACT)); - + confirmInt(4, invokeMatch(new StringEval("Ben"), ae, MATCH_LARGEST_LTE)); - + confirmInt(13, invokeMatch(new StringEval("ED"), ae, MATCH_LARGEST_LTE)); confirmInt(9, invokeMatch(new StringEval("ED"), ae, MATCH_EXACT)); - + confirmInt(13, invokeMatch(new StringEval("Hugh"), ae, MATCH_LARGEST_LTE)); assertEquals(ErrorEval.NA, invokeMatch(new StringEval("Hugh"), ae, MATCH_EXACT)); - + confirmInt(11, invokeMatch(new NumberEval(30), ae, MATCH_LARGEST_LTE)); confirmInt(12, invokeMatch(BoolEval.TRUE, ae, MATCH_LARGEST_LTE)); } - + /** * Ensures that the match_type argument can be an AreaEval.
* Bugzilla 44421 */ public void testMatchArgTypeArea() { - - ValueEval[] values = { - new NumberEval(4), - new NumberEval(5), - new NumberEval(10), - new NumberEval(10), - new NumberEval(25), - }; - - AreaEval ae = createAreaEval("A1:A5", values); - AreaEval matchAE = createAreaEval("C1:C1", new ValueEval[] { MATCH_LARGEST_LTE, }); - + ValueEval[] values = { + new NumberEval(4), + new NumberEval(5), + new NumberEval(10), + new NumberEval(10), + new NumberEval(25), + }; + + AreaEval ae = EvalFactory.createAreaEval("A1:A5", values); + + AreaEval matchAE = EvalFactory.createAreaEval("C1:C1", new ValueEval[] { MATCH_LARGEST_LTE, }); + try { confirmInt(4, invokeMatch(new NumberEval(10), ae, matchAE)); } catch (RuntimeException e) { if(e.getMessage().startsWith("Unexpected match_type type")) { - // identified bug 44421 + // identified bug 44421 fail(e.getMessage()); } // some other error ?? diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestMathX.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestMathX.java index eb576506a..d95718524 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestMathX.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestMathX.java @@ -20,6 +20,8 @@ */ package org.apache.poi.hssf.record.formula.functions; +import org.apache.poi.hssf.record.formula.functions.XYNumericFunction.Accumulator; + /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > @@ -566,99 +568,99 @@ public class TestMathX extends AbstractNumericTestCase { } public void testSumx2my2() { - double d = 0; double[] xarr = null; double[] yarr = null; xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - d = MathX.sumx2my2(xarr, yarr); - assertEquals("sumx2my2 ", 100, d); + confirmSumx2my2(xarr, yarr, 100); xarr = new double[]{-1, -2, -3, -4, -5, -6, -7, -8, -9, -10}; yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - d = MathX.sumx2my2(xarr, yarr); - assertEquals("sumx2my2 ", 100, d); + confirmSumx2my2(xarr, yarr, 100); xarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; - d = MathX.sumx2my2(xarr, yarr); - assertEquals("sumx2my2 ", -100, d); + confirmSumx2my2(xarr, yarr, -100); xarr = new double[]{10}; yarr = new double[]{9}; - d = MathX.sumx2my2(xarr, yarr); - assertEquals("sumx2my2 ", 19, d); + confirmSumx2my2(xarr, yarr, 19); xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; - d = MathX.sumx2my2(xarr, yarr); - assertEquals("sumx2my2 ", 0, d); - + confirmSumx2my2(xarr, yarr, 0); } public void testSumx2py2() { - double d = 0; double[] xarr = null; double[] yarr = null; xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - d = MathX.sumx2py2(xarr, yarr); - assertEquals("sumx2py2 ", 670, d); + confirmSumx2py2(xarr, yarr, 670); xarr = new double[]{-1, -2, -3, -4, -5, -6, -7, -8, -9, -10}; yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - d = MathX.sumx2py2(xarr, yarr); - assertEquals("sumx2py2 ", 670, d); + confirmSumx2py2(xarr, yarr, 670); xarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; - d = MathX.sumx2py2(xarr, yarr); - assertEquals("sumx2py2 ", 670, d); + confirmSumx2py2(xarr, yarr, 670); xarr = new double[]{10}; yarr = new double[]{9}; - d = MathX.sumx2py2(xarr, yarr); - assertEquals("sumx2py2 ", 181, d); + confirmSumx2py2(xarr, yarr, 181); xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; - d = MathX.sumx2py2(xarr, yarr); - assertEquals("sumx2py2 ", 770, d); + confirmSumx2py2(xarr, yarr, 770); } public void testSumxmy2() { - double d = 0; double[] xarr = null; double[] yarr = null; xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - d = MathX.sumxmy2(xarr, yarr); - assertEquals("sumxmy2 ", 10, d); + confirmSumxmy2(xarr, yarr, 10); xarr = new double[]{-1, -2, -3, -4, -5, -6, -7, -8, -9, -10}; yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - d = MathX.sumxmy2(xarr, yarr); - assertEquals("sumxmy2 ", 1330, d); + confirmSumxmy2(xarr, yarr, 1330); xarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; - d = MathX.sumxmy2(xarr, yarr); - assertEquals("sumxmy2 ", 10, d); + confirmSumxmy2(xarr, yarr, 10); xarr = new double[]{10}; yarr = new double[]{9}; - d = MathX.sumxmy2(xarr, yarr); - assertEquals("sumxmy2 ", 1, d); + confirmSumxmy2(xarr, yarr, 1); xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; - d = MathX.sumxmy2(xarr, yarr); - assertEquals("sumxmy2 ", 0, d); + confirmSumxmy2(xarr, yarr, 0); } + private static void confirmSumx2my2(double[] xarr, double[] yarr, double expectedResult) { + confirmXY(new Sumx2my2().createAccumulator(), xarr, yarr, expectedResult); + } + private static void confirmSumx2py2(double[] xarr, double[] yarr, double expectedResult) { + confirmXY(new Sumx2py2().createAccumulator(), xarr, yarr, expectedResult); + } + private static void confirmSumxmy2(double[] xarr, double[] yarr, double expectedResult) { + confirmXY(new Sumxmy2().createAccumulator(), xarr, yarr, expectedResult); + } + + private static void confirmXY(Accumulator acc, double[] xarr, double[] yarr, + double expectedResult) { + double result = 0.0; + for (int i = 0; i < xarr.length; i++) { + result += acc.accumulate(xarr[i], yarr[i]); + } + assertEquals(expectedResult, result, 0.0); + } + public void testRound() { double d = 0; int p = 0; diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestMid.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestMid.java index 87dd470c6..76cd1056e 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestMid.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestMid.java @@ -17,29 +17,25 @@ package org.apache.poi.hssf.record.formula.functions; -import org.apache.poi.hssf.record.formula.AreaPtg; -import org.apache.poi.hssf.record.formula.RefPtg; -import org.apache.poi.hssf.record.formula.eval.Area2DEval; +import junit.framework.TestCase; + 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.NumberEval; -import org.apache.poi.hssf.record.formula.eval.Ref2DEval; 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 junit.framework.TestCase; /** * Tests for Excel function MID() - * + * * @author Josh Micich */ 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); @@ -56,28 +52,28 @@ public final class TestMid extends TestCase { assertEquals(ErrorEval.class, result.getClass()); assertEquals(expectedError.getErrorCode(), ((ErrorEval)result).getErrorCode()); } - + public void testBasic() { - + confirmMid(new StringEval("galactic"), new NumberEval(3), new NumberEval(4), "lact"); } - + /** * Valid cases where args are not precisely (string, int, int) but can be resolved OK. */ public void testUnusualArgs() { // startPos with fractional digits confirmMid(new StringEval("galactic"), new NumberEval(3.1), new NumberEval(4), "lact"); - + // string startPos confirmMid(new StringEval("galactic"), new StringEval("3"), new NumberEval(4), "lact"); - - // text (first) arg type is number, other args are strings with fractional digits + + // text (first) arg type is number, other args are strings with fractional digits confirmMid(new NumberEval(123456), new StringEval("3.1"), new StringEval("2.9"), "34"); - + // startPos is 1x1 area ref, numChars is cell ref - AreaEval aeStart = new Area2DEval(new AreaPtg("A1:A1"), new ValueEval[] { new NumberEval(2), } ); - RefEval reNumChars = new Ref2DEval(new RefPtg("B1"), new NumberEval(3)); + AreaEval aeStart = EvalFactory.createAreaEval("A1:A1", new ValueEval[] { new NumberEval(2), } ); + RefEval reNumChars = EvalFactory.createRefEval("B1", new NumberEval(3)); confirmMid(new StringEval("galactic"), aeStart, reNumChars, "ala"); confirmMid(new StringEval("galactic"), new NumberEval(3.1), BlankEval.INSTANCE, ""); @@ -85,7 +81,7 @@ public final class TestMid extends TestCase { confirmMid(new StringEval("galactic"), new NumberEval(3), BoolEval.FALSE, ""); confirmMid(new StringEval("galactic"), new NumberEval(3), BoolEval.TRUE, "l"); confirmMid(BlankEval.INSTANCE, new NumberEval(3), BoolEval.TRUE, ""); - + } /** @@ -93,7 +89,7 @@ public final class TestMid extends TestCase { */ public void testExtremes() { confirmMid(new StringEval("galactic"), new NumberEval(4), new NumberEval(400), "actic"); - + confirmMid(new StringEval("galactic"), new NumberEval(30), new NumberEval(4), ""); confirmMid(new StringEval("galactic"), new NumberEval(3), new NumberEval(0), ""); } @@ -106,9 +102,9 @@ public final class TestMid extends TestCase { confirmMid(new StringEval("galactic"), ErrorEval.NAME_INVALID, new NumberEval(4), ErrorEval.NAME_INVALID); confirmMid(new StringEval("galactic"), new NumberEval(3), ErrorEval.NAME_INVALID, ErrorEval.NAME_INVALID); confirmMid(new StringEval("galactic"), ErrorEval.DIV_ZERO, ErrorEval.NAME_INVALID, ErrorEval.DIV_ZERO); - + confirmMid(new StringEval("galactic"), BlankEval.INSTANCE, new NumberEval(3.1), ErrorEval.VALUE_INVALID); - + confirmMid(new StringEval("galactic"), new NumberEval(0), new NumberEval(4), ErrorEval.VALUE_INVALID); confirmMid(new StringEval("galactic"), new NumberEval(1), new NumberEval(-1), ErrorEval.VALUE_INVALID); } diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestSumproduct.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestSumproduct.java index 2f9a6314b..47ee0afbb 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestSumproduct.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestSumproduct.java @@ -17,18 +17,16 @@ package org.apache.poi.hssf.record.formula.functions; -import org.apache.poi.hssf.record.formula.RefPtg; +import junit.framework.TestCase; + 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.NumberEval; 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.RefEval; import org.apache.poi.hssf.record.formula.eval.ValueEval; -import junit.framework.TestCase; - /** * Test cases for SUMPRODUCT() * @@ -50,7 +48,7 @@ public final class TestSumproduct extends TestCase { public void testScalarSimple() { - RefEval refEval = new Ref2DEval(new RefPtg("A1"), new NumberEval(3)); + RefEval refEval = EvalFactory.createRefEval("A1", new NumberEval(3)); Eval[] args = { refEval, new NumberEval(2), @@ -113,7 +111,6 @@ public final class TestSumproduct extends TestCase { }; AreaEval aeA = EvalFactory.createAreaEval("A1:A2", aValues); AreaEval aeB = EvalFactory.createAreaEval("B1:B2", new ValueEval[2]); - aeB.getValues()[1] = ErrorEval.REF_INVALID; Eval[] args = { aeA, aeB, }; assertEquals(ErrorEval.REF_INVALID, invokeSumproduct(args)); diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestTFunc.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestTFunc.java index c57640eab..ac7152d0e 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestTFunc.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestTFunc.java @@ -17,18 +17,16 @@ package org.apache.poi.hssf.record.formula.functions; -import org.apache.poi.hssf.record.formula.RefPtg; +import junit.framework.TestCase; + 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.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 junit.framework.TestCase; - /** * Test cases for Excel function T() * @@ -50,7 +48,7 @@ public final class TestTFunc extends TestCase { * where cell A1 has the specified innerValue */ private Eval invokeTWithReference(ValueEval innerValue) { - Eval arg = new Ref2DEval(new RefPtg((short)1, (short)1, false, false), innerValue); + Eval arg = EvalFactory.createRefEval("$B$2", innerValue); return invokeT(arg); } diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestXYNumericFunction.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestXYNumericFunction.java index c9f043bd3..0d62b1fb0 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestXYNumericFunction.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestXYNumericFunction.java @@ -19,8 +19,6 @@ package org.apache.poi.hssf.record.formula.functions; import junit.framework.TestCase; -import org.apache.poi.hssf.record.formula.AreaPtg; -import org.apache.poi.hssf.record.formula.eval.Area2DEval; 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; @@ -100,7 +98,7 @@ public final class TestXYNumericFunction extends TestCase { private static ValueEval createAreaEval(ValueEval[] values) { String refStr = "A1:A" + values.length; - return new Area2DEval(new AreaPtg(refStr), values); + return EvalFactory.createAreaEval(refStr, values); } public void testErrors() { diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestNamedRange.java b/src/testcases/org/apache/poi/hssf/usermodel/TestNamedRange.java index 2382a8235..f3430d8bc 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestNamedRange.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestNamedRange.java @@ -24,8 +24,8 @@ import java.io.IOException; import junit.framework.TestCase; import org.apache.poi.hssf.HSSFTestDataSamples; -import org.apache.poi.hssf.util.AreaReference; -import org.apache.poi.hssf.util.CellReference; +import org.apache.poi.ss.util.AreaReference; +import org.apache.poi.ss.util.CellReference; /** * diff --git a/src/testcases/org/apache/poi/hssf/util/TestAreaReference.java b/src/testcases/org/apache/poi/hssf/util/TestAreaReference.java index 5eb2abb6e..a5dbb096d 100644 --- a/src/testcases/org/apache/poi/hssf/util/TestAreaReference.java +++ b/src/testcases/org/apache/poi/hssf/util/TestAreaReference.java @@ -34,6 +34,8 @@ import org.apache.poi.hssf.usermodel.HSSFName; import org.apache.poi.hssf.usermodel.HSSFRow; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.util.AreaReference; +import org.apache.poi.ss.util.CellReference; /** * */ diff --git a/src/testcases/org/apache/poi/hssf/util/TestCellReference.java b/src/testcases/org/apache/poi/hssf/util/TestCellReference.java index 8ec8f9946..9cc8659ff 100644 --- a/src/testcases/org/apache/poi/hssf/util/TestCellReference.java +++ b/src/testcases/org/apache/poi/hssf/util/TestCellReference.java @@ -20,7 +20,8 @@ package org.apache.poi.hssf.util; import junit.framework.TestCase; -import org.apache.poi.hssf.util.CellReference.NameType; +import org.apache.poi.ss.util.CellReference; +import org.apache.poi.ss.util.CellReference.NameType; public final class TestCellReference extends TestCase {