From e6ec8e26018f4b8bba86716073dee5ea90f28a79 Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Sun, 7 Sep 2008 20:11:32 +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-690738,690740-690760,690762-690771,690773-690824,690826-690834,690838-691016,691018-691179,691181,691183-692908 via svnmerge from https://svn.apache.org/repos/asf/poi/trunk ........ r691533 | yegor | 2008-09-03 09:04:07 +0100 (Wed, 03 Sep 2008) | 1 line fixed bug #45728: SlideShow.reorderSlide didn't work properly ........ r691687 | josh | 2008-09-03 18:03:02 +0100 (Wed, 03 Sep 2008) | 1 line Fixed ArrayPtg.toString to not crash when partially initialised ........ r691740 | josh | 2008-09-03 20:22:53 +0100 (Wed, 03 Sep 2008) | 1 line Initial work on bug 45720 - copy 'FilterDatabase' named record when cloning sheets. Some clean-up in NameRecord. ........ r692239 | josh | 2008-09-04 21:58:37 +0100 (Thu, 04 Sep 2008) | 1 line Fixed 2 small bugs in RelationalOperationEval (added junits). Refactored hierarchy. ........ r692241 | josh | 2008-09-04 22:01:48 +0100 (Thu, 04 Sep 2008) | 1 line Fix unused import (correction to r692239) ........ r692243 | josh | 2008-09-04 22:05:50 +0100 (Thu, 04 Sep 2008) | 1 line Fixed compiler warnings, linked junit test to suite ........ r692255 | josh | 2008-09-04 22:32:17 +0100 (Thu, 04 Sep 2008) | 1 line Made HSSFFormulaEvaluator capable of handling simple named ranges ........ r692300 | josh | 2008-09-05 00:16:15 +0100 (Fri, 05 Sep 2008) | 1 line Fix for bug 45376 - added caching to HSSFFormulaEvaluator ........ r692506 | josh | 2008-09-05 19:22:30 +0100 (Fri, 05 Sep 2008) | 1 line Minor fixes for numeric operators - junit added. Some refactoring. ........ r692538 | josh | 2008-09-05 21:38:51 +0100 (Fri, 05 Sep 2008) | 1 line Modified formula evaluator to handle whole column refs ........ r692541 | josh | 2008-09-05 21:43:37 +0100 (Fri, 05 Sep 2008) | 1 line reverted changes accidentally submitted with r692538 ........ r692612 | josh | 2008-09-06 06:30:31 +0100 (Sat, 06 Sep 2008) | 1 line Fixes for special cases of lookup functions (test cases added) ........ r692614 | josh | 2008-09-06 07:04:01 +0100 (Sat, 06 Sep 2008) | 1 line Minor fixes to YEARFRAC(). Added ISEVEN() and ISODD(). Added test cases. ........ r692893 | yegor | 2008-09-07 17:30:35 +0100 (Sun, 07 Sep 2008) | 1 line fixed bug #45720: cloneSheet breaks autofilters. ........ git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@692932 13f79535-47bb-0310-9956-ffa450edef68 --- src/documentation/content/xdocs/changes.xml | 2 + src/documentation/content/xdocs/status.xml | 2 + .../apache/poi/ddf/EscherContainerRecord.java | 17 + .../poi/hssf/model/DrawingManager2.java | 14 +- .../org/apache/poi/hssf/model/Workbook.java | 115 +- .../apache/poi/hssf/record/NameRecord.java | 706 +++---- .../poi/hssf/record/formula/ArrayPtg.java | 12 +- .../poi/hssf/record/formula/Ref3DPtg.java | 6 +- .../record/formula/atp/AnalysisToolPak.java | 4 +- .../record/formula/atp/ParityFunction.java | 74 + .../poi/hssf/record/formula/atp/YearFrac.java | 7 +- .../poi/hssf/record/formula/eval/AddEval.java | 97 +- .../hssf/record/formula/eval/DivideEval.java | 111 +- .../hssf/record/formula/eval/EqualEval.java | 83 +- .../record/formula/eval/ExternalFunction.java | 22 +- .../record/formula/eval/GreaterEqualEval.java | 83 +- .../record/formula/eval/GreaterThanEval.java | 83 +- .../record/formula/eval/LazyAreaEval.java | 61 +- .../hssf/record/formula/eval/LazyRefEval.java | 51 +- .../record/formula/eval/LessEqualEval.java | 83 +- .../record/formula/eval/LessThanEval.java | 84 +- .../record/formula/eval/MultiplyEval.java | 102 +- .../hssf/record/formula/eval/NameEval.java | 18 +- .../record/formula/eval/NotEqualEval.java | 84 +- .../formula/eval/NumericOperationEval.java | 66 - .../hssf/record/formula/eval/PercentEval.java | 51 +- .../hssf/record/formula/eval/PowerEval.java | 103 +- .../formula/eval/RelationalOperationEval.java | 339 ++-- .../record/formula/eval/SubtractEval.java | 106 +- .../eval/TwoOperandNumericOperation.java | 55 + .../record/formula/eval/UnaryMinusEval.java | 105 +- .../record/formula/eval/UnaryPlusEval.java | 14 +- .../eval/ValueEvalToNumericXlator.java | 37 +- .../record/formula/functions/Hlookup.java | 16 +- .../record/formula/functions/LookupUtils.java | 61 +- .../record/formula/functions/Vlookup.java | 10 +- .../apache/poi/hssf/usermodel/HSSFName.java | 61 +- .../poi/hssf/usermodel/HSSFWorkbook.java | 109 +- .../usermodel/OperationEvaluatorFactory.java | 184 ++ .../poi/ss/usermodel/EvaluationCache.java | 95 + .../poi/ss/usermodel/FormulaEvaluator.java | 189 +- .../usermodel/OperationEvaluatorFactory.java | 55 +- .../org/apache/poi/ss/util/AreaReference.java | 68 +- .../org/apache/poi/ss/util/CellReference.java | 31 +- .../org/apache/poi/hslf/record/Document.java | 33 +- .../poi/hslf/record/RecordContainer.java | 12 +- .../poi/hslf/record/SlideListWithText.java | 62 +- .../apache/poi/hslf/usermodel/SlideShow.java | 50 +- .../hslf/usermodel/TestReOrderingSlides.java | 25 +- .../org/apache/poi/hssf/data/45376.xls | Bin 27648 -> 0 bytes .../org/apache/poi/hssf/data/45720.xls | Bin 0 -> 16384 bytes .../poi/hssf/data/FormulaEvalTestData.xls | Bin 153600 -> 153600 bytes .../hssf/data/LookupFunctionsTestCaseData.xls | Bin 39936 -> 41984 bytes .../poi/hssf/data/externalFunctionExample.xls | Bin 16384 -> 16896 bytes .../org/apache/poi/hssf/data/testNames.xls | Bin 27648 -> 28160 bytes .../formula/TestExternalFunctionFormulas.java | 15 +- .../formula/atp/TestYearFracCalculator.java | 1 + .../formula/eval/AllFormulaEvalTests.java | 2 + .../formula/eval/TestCircularReferences.java | 2 - .../record/formula/eval/TestDivideEval.java | 62 + .../record/formula/eval/TestEqualEval.java | 69 + .../record/formula/eval/TestPercentEval.java | 10 +- .../formula/eval/TestUnaryPlusEval.java | 34 +- .../AllIndividualFunctionEvaluationTests.java | 1 + .../record/formula/functions/TestDate.java | 29 +- .../poi/hssf/usermodel/AllUserModelTests.java | 1 + .../apache/poi/hssf/usermodel/TestBugs.java | 19 +- .../usermodel/TestFormulaEvaluatorBugs.java | 122 +- .../usermodel/TestHSSFFormulaEvaluator.java | 82 + .../poi/hssf/usermodel/TestHSSFSheet.java | 1657 +++++++++-------- .../poi/hssf/usermodel/TestHSSFWorkbook.java | 12 +- .../poi/hssf/util/TestAreaReference.java | 53 +- 72 files changed, 3105 insertions(+), 2924 deletions(-) create mode 100644 src/java/org/apache/poi/hssf/record/formula/atp/ParityFunction.java delete mode 100644 src/java/org/apache/poi/hssf/record/formula/eval/NumericOperationEval.java create mode 100644 src/java/org/apache/poi/hssf/record/formula/eval/TwoOperandNumericOperation.java create mode 100755 src/java/org/apache/poi/hssf/usermodel/OperationEvaluatorFactory.java create mode 100644 src/java/org/apache/poi/ss/usermodel/EvaluationCache.java delete mode 100644 src/testcases/org/apache/poi/hssf/data/45376.xls create mode 100755 src/testcases/org/apache/poi/hssf/data/45720.xls create mode 100644 src/testcases/org/apache/poi/hssf/record/formula/eval/TestDivideEval.java create mode 100644 src/testcases/org/apache/poi/hssf/record/formula/eval/TestEqualEval.java create mode 100644 src/testcases/org/apache/poi/hssf/usermodel/TestHSSFFormulaEvaluator.java diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index cd8684240..a78c832e5 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 + 45720 Fixed HSSFWorkbook.cloneSheet to correctly clone sheets with drawings + 45728 Fix for SlideShow.reorderSlide in HSLF 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 diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 5c7198bce..85bda8b74 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 + 45720 Fixed HSSFWorkbook.cloneSheet to correctly clone sheets with drawings + 45728 Fix for SlideShow.reorderSlide in HSLF 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 diff --git a/src/java/org/apache/poi/ddf/EscherContainerRecord.java b/src/java/org/apache/poi/ddf/EscherContainerRecord.java index 28b4a976c..bb188d538 100644 --- a/src/java/org/apache/poi/ddf/EscherContainerRecord.java +++ b/src/java/org/apache/poi/ddf/EscherContainerRecord.java @@ -235,4 +235,21 @@ public class EscherContainerRecord extends EscherRecord return null; } + /** + * Recursively find records with the specified record ID + * + * @param out - list to store found records + */ + public void getRecordsById(short recordId, List out){ + for(Iterator it = childRecords.iterator(); it.hasNext();) { + Object er = it.next(); + if(er instanceof EscherContainerRecord) { + EscherContainerRecord c = (EscherContainerRecord)er; + c.getRecordsById(recordId, out ); + } else if (er instanceof EscherSpRecord){ + out.add(er); + } + } + } + } diff --git a/src/java/org/apache/poi/hssf/model/DrawingManager2.java b/src/java/org/apache/poi/hssf/model/DrawingManager2.java index dee95fb4f..c9993c241 100644 --- a/src/java/org/apache/poi/hssf/model/DrawingManager2.java +++ b/src/java/org/apache/poi/hssf/model/DrawingManager2.java @@ -67,6 +67,17 @@ public class DrawingManager2 * @return a new shape id. */ public int allocateShapeId(short drawingGroupId) + { + EscherDgRecord dg = getDrawingGroup(drawingGroupId); + return allocateShapeId(drawingGroupId, dg); + } + + /** + * Allocates new shape id for the new drawing group id. + * + * @return a new shape id. + */ + public int allocateShapeId(short drawingGroupId, EscherDgRecord dg) { dgg.setNumShapesSaved( dgg.getNumShapesSaved() + 1 ); @@ -78,7 +89,6 @@ public class DrawingManager2 { int result = c.getNumShapeIdsUsed() + (1024 * (i+1)); c.incrementShapeId(); - EscherDgRecord dg = getDrawingGroup(drawingGroupId); dg.setNumShapes( dg.getNumShapes() + 1 ); dg.setLastMSOSPID( result ); if (result >= dgg.getShapeIdMax()) @@ -90,7 +100,6 @@ public class DrawingManager2 // Create new cluster dgg.addCluster( drawingGroupId, 0 ); dgg.getFileIdClusters()[dgg.getFileIdClusters().length-1].incrementShapeId(); - EscherDgRecord dg = getDrawingGroup(drawingGroupId); dg.setNumShapes( dg.getNumShapes() + 1 ); int result = (1024 * dgg.getFileIdClusters().length); dg.setLastMSOSPID( result ); @@ -98,7 +107,6 @@ public class DrawingManager2 dgg.setShapeIdMax( result + 1 ); return result; } - //////////// Non-public methods ///////////// /** diff --git a/src/java/org/apache/poi/hssf/model/Workbook.java b/src/java/org/apache/poi/hssf/model/Workbook.java index 5ad1ef28d..f093feef6 100644 --- a/src/java/org/apache/poi/hssf/model/Workbook.java +++ b/src/java/org/apache/poi/hssf/model/Workbook.java @@ -22,57 +22,8 @@ import java.util.Iterator; import java.util.List; import java.util.Locale; -import org.apache.poi.ddf.EscherBSERecord; -import org.apache.poi.ddf.EscherBoolProperty; -import org.apache.poi.ddf.EscherContainerRecord; -import org.apache.poi.ddf.EscherDggRecord; -import org.apache.poi.ddf.EscherOptRecord; -import org.apache.poi.ddf.EscherProperties; -import org.apache.poi.ddf.EscherRGBProperty; -import org.apache.poi.ddf.EscherRecord; -import org.apache.poi.ddf.EscherSplitMenuColorsRecord; -import org.apache.poi.hssf.record.BOFRecord; -import org.apache.poi.hssf.record.BackupRecord; -import org.apache.poi.hssf.record.BookBoolRecord; -import org.apache.poi.hssf.record.BoundSheetRecord; -import org.apache.poi.hssf.record.CodepageRecord; -import org.apache.poi.hssf.record.CountryRecord; -import org.apache.poi.hssf.record.DSFRecord; -import org.apache.poi.hssf.record.DateWindow1904Record; -import org.apache.poi.hssf.record.DrawingGroupRecord; -import org.apache.poi.hssf.record.EOFRecord; -import org.apache.poi.hssf.record.ExtSSTRecord; -import org.apache.poi.hssf.record.ExtendedFormatRecord; -import org.apache.poi.hssf.record.ExternSheetRecord; -import org.apache.poi.hssf.record.FileSharingRecord; -import org.apache.poi.hssf.record.FnGroupCountRecord; -import org.apache.poi.hssf.record.FontRecord; -import org.apache.poi.hssf.record.FormatRecord; -import org.apache.poi.hssf.record.HideObjRecord; -import org.apache.poi.hssf.record.HyperlinkRecord; -import org.apache.poi.hssf.record.InterfaceEndRecord; -import org.apache.poi.hssf.record.InterfaceHdrRecord; -import org.apache.poi.hssf.record.MMSRecord; -import org.apache.poi.hssf.record.NameRecord; -import org.apache.poi.hssf.record.PaletteRecord; -import org.apache.poi.hssf.record.PasswordRecord; -import org.apache.poi.hssf.record.PasswordRev4Record; -import org.apache.poi.hssf.record.PrecisionRecord; -import org.apache.poi.hssf.record.ProtectRecord; -import org.apache.poi.hssf.record.ProtectionRev4Record; -import org.apache.poi.hssf.record.RecalcIdRecord; -import org.apache.poi.hssf.record.Record; -import org.apache.poi.hssf.record.RefreshAllRecord; -import org.apache.poi.hssf.record.SSTRecord; -import org.apache.poi.hssf.record.StyleRecord; -import org.apache.poi.hssf.record.SupBookRecord; -import org.apache.poi.hssf.record.TabIdRecord; -import org.apache.poi.hssf.record.UnicodeString; -import org.apache.poi.hssf.record.UseSelFSRecord; -import org.apache.poi.hssf.record.WindowOneRecord; -import org.apache.poi.hssf.record.WindowProtectRecord; -import org.apache.poi.hssf.record.WriteAccessRecord; -import org.apache.poi.hssf.record.WriteProtectRecord; +import org.apache.poi.ddf.*; +import org.apache.poi.hssf.record.*; import org.apache.poi.hssf.record.formula.NameXPtg; import org.apache.poi.hssf.util.HSSFColor; import org.apache.poi.hssf.util.SheetReferences; @@ -2206,17 +2157,17 @@ public final class Workbook implements Model { // contains a EscherDggRecord for(Iterator rit = records.iterator(); rit.hasNext();) { Record r = (Record)rit.next(); - + if(r instanceof DrawingGroupRecord) { DrawingGroupRecord dg = (DrawingGroupRecord)r; dg.processChildRecords(); - + EscherContainerRecord cr = dg.getEscherContainer(); if(cr == null) { continue; } - + EscherDggRecord dgg = null; for(Iterator it = cr.getChildRecords().iterator(); it.hasNext();) { Object er = it.next(); @@ -2224,7 +2175,7 @@ public final class Workbook implements Model { dgg = (EscherDggRecord)er; } } - + if(dgg != null) { drawingManager = new DrawingManager2(dgg); return; @@ -2234,7 +2185,7 @@ public final class Workbook implements Model { // Look for the DrawingGroup record int dgLoc = findFirstRecordLocBySid(DrawingGroupRecord.sid); - + // If there is one, does it have a EscherDggRecord? if(dgLoc != -1) { DrawingGroupRecord dg = @@ -2246,7 +2197,7 @@ public final class Workbook implements Model { dgg = (EscherDggRecord)er; } } - + if(dgg != null) { drawingManager = new DrawingManager2(dgg); } @@ -2455,4 +2406,54 @@ public final class Workbook implements Model { public NameXPtg getNameXPtg(String name) { return getOrCreateLinkTable().getNameXPtg(name); } + + /** + * Check if the cloned sheet has drawings. If yes, then allocate a new drawing group ID and + * re-generate shape IDs + * + * @param sheet the cloned sheet + */ + public void cloneDrawings(Sheet sheet){ + + findDrawingGroup(); + + if(drawingManager == null) { + //this workbook does not have drawings + return; + } + + //check if the cloned sheet has drawings + int aggLoc = sheet.aggregateDrawingRecords(drawingManager, false); + if(aggLoc != -1) { + EscherAggregate agg = (EscherAggregate) sheet.findFirstRecordBySid(EscherAggregate.sid); + + EscherDggRecord dgg = drawingManager.getDgg(); + + //register a new drawing group for the cloned sheet + int dgId = drawingManager.findNewDrawingGroupId(); + dgg.addCluster( dgId, 0 ); + dgg.setDrawingsSaved(dgg.getDrawingsSaved() + 1); + + EscherDgRecord dg = null; + for(Iterator it = agg.getEscherContainer().getChildRecords().iterator(); it.hasNext();) { + Object er = it.next(); + if(er instanceof EscherDgRecord) { + dg = (EscherDgRecord)er; + //update id of the drawing in the cloned sheet + dg.setOptions( (short) ( dgId << 4 ) ); + } else if (er instanceof EscherContainerRecord){ + //recursively find shape records and re-generate shapeId + ArrayList spRecords = new ArrayList(); + EscherContainerRecord cp = (EscherContainerRecord)er; + cp.getRecordsById(EscherSpRecord.RECORD_ID, spRecords); + for(Iterator spIt = spRecords.iterator(); spIt.hasNext();) { + EscherSpRecord sp = (EscherSpRecord)spIt.next(); + int shapeId = drawingManager.allocateShapeId((short)dgId, dg); + sp.setShapeId(shapeId); + } + } + } + + } + } } diff --git a/src/java/org/apache/poi/hssf/record/NameRecord.java b/src/java/org/apache/poi/hssf/record/NameRecord.java index 71f3bf6fb..77296becd 100644 --- a/src/java/org/apache/poi/hssf/record/NameRecord.java +++ b/src/java/org/apache/poi/hssf/record/NameRecord.java @@ -17,9 +17,8 @@ package org.apache.poi.hssf.record; -import java.util.Iterator; +import java.util.ArrayList; import java.util.List; -import java.util.Stack; import org.apache.poi.hssf.model.FormulaParser; import org.apache.poi.hssf.record.formula.Area3DPtg; @@ -44,51 +43,33 @@ import org.apache.poi.util.StringUtil; */ public final class NameRecord extends Record { public final static short sid = 0x0018; - /**Included for completeness sake, not implemented - */ - public final static byte BUILTIN_CONSOLIDATE_AREA = (byte)1; - - /**Included for completeness sake, not implemented - */ - public final static byte BUILTIN_AUTO_OPEN = (byte)2; + /**Included for completeness sake, not implemented */ + public final static byte BUILTIN_CONSOLIDATE_AREA = 1; + /**Included for completeness sake, not implemented */ + public final static byte BUILTIN_AUTO_OPEN = 2; + /**Included for completeness sake, not implemented */ + public final static byte BUILTIN_AUTO_CLOSE = 3; + /**Included for completeness sake, not implemented */ + public final static byte BUILTIN_DATABASE = 4; + /**Included for completeness sake, not implemented */ + public final static byte BUILTIN_CRITERIA = 5; - /**Included for completeness sake, not implemented - */ - public final static byte BUILTIN_AUTO_CLOSE = (byte)3; + public final static byte BUILTIN_PRINT_AREA = 6; + public final static byte BUILTIN_PRINT_TITLE = 7; - /**Included for completeness sake, not implemented - */ - public final static byte BUILTIN_DATABASE = (byte)4; + /**Included for completeness sake, not implemented */ + public final static byte BUILTIN_RECORDER = 8; + /**Included for completeness sake, not implemented */ + public final static byte BUILTIN_DATA_FORM = 9; + /**Included for completeness sake, not implemented */ + public final static byte BUILTIN_AUTO_ACTIVATE = 10; + /**Included for completeness sake, not implemented */ + public final static byte BUILTIN_AUTO_DEACTIVATE = 11; + /**Included for completeness sake, not implemented */ + public final static byte BUILTIN_SHEET_TITLE = 12; - /**Included for completeness sake, not implemented - */ - public final static byte BUILTIN_CRITERIA = (byte)5; - - public final static byte BUILTIN_PRINT_AREA = (byte)6; - public final static byte BUILTIN_PRINT_TITLE = (byte)7; - - /**Included for completeness sake, not implemented - */ - public final static byte BUILTIN_RECORDER = (byte)8; - - /**Included for completeness sake, not implemented - */ - public final static byte BUILTIN_DATA_FORM = (byte)9; - - /**Included for completeness sake, not implemented - */ + public final static byte BUILTIN_FILTER_DB = 13; - public final static byte BUILTIN_AUTO_ACTIVATE = (byte)10; - - /**Included for completeness sake, not implemented - */ - - public final static byte BUILTIN_AUTO_DEACTIVATE = (byte)11; - - /**Included for completeness sake, not implemented - */ - public final static byte BUILTIN_SHEET_TITLE = (byte)12; - private static final class Option { public static final int OPT_HIDDEN_NAME = 0x0001; public static final int OPT_FUNCTION_NAME = 0x0002; @@ -98,22 +79,16 @@ public final class NameRecord extends Record { public static final int OPT_BUILTIN = 0x0020; public static final int OPT_BINDATA = 0x1000; } - + private short field_1_option_flag; private byte field_2_keyboard_shortcut; - private byte field_3_length_name_text; - private short field_4_length_name_definition; private short field_5_index_to_sheet; // unused: see field_6 /** the one based sheet number. Zero if this is a global name */ private int field_6_sheetNumber; - private byte field_7_length_custom_menu; - private byte field_8_length_description_text; - private byte field_9_length_help_topic_text; - private byte field_10_length_status_bar_text; - private byte field_11_compressed_unicode_flag; // not documented - private byte field_12_builtIn_name; + private boolean field_11_nameIsMultibyte; + private byte field_12_built_in_code; private String field_12_name_text; - private Stack field_13_name_definition; + private Ptg[] field_13_name_definition; private String field_14_custom_menu_text; private String field_15_description_text; private String field_16_help_topic_text; @@ -122,13 +97,13 @@ public final class NameRecord extends Record { /** Creates new NameRecord */ public NameRecord() { - field_13_name_definition = new Stack(); + field_13_name_definition = Ptg.EMPTY_PTG_ARRAY; - field_12_name_text = new String(); - field_14_custom_menu_text = new String(); - field_15_description_text = new String(); - field_16_help_topic_text = new String(); - field_17_status_bar_text = new String(); + field_12_name_text = ""; + field_14_custom_menu_text = ""; + field_15_description_text = ""; + field_16_help_topic_text = ""; + field_17_status_bar_text = ""; } /** @@ -146,19 +121,10 @@ public final class NameRecord extends Record { */ public NameRecord(byte builtin, int sheetNumber) { - this(); - this.field_12_builtIn_name = builtin; - this.setOptionFlag((short)(this.field_1_option_flag | Option.OPT_BUILTIN)); - this.setNameTextLength((byte)1); + this(); + field_12_built_in_code = builtin; + setOptionFlag((short)(field_1_option_flag | Option.OPT_BUILTIN)); field_6_sheetNumber = sheetNumber; //the extern sheets are set through references - - //clearing these because they are not used with builtin records - this.setCustomMenuLength((byte)0); - this.setDescriptionTextLength((byte)0); - this.setHelpTopicLength((byte)0); - this.setStatusBarLength((byte)0); - - } /** sets the option flag for the named range @@ -176,34 +142,9 @@ public final class NameRecord extends Record { field_2_keyboard_shortcut = shortcut; } - /** sets the name of the named range length - * @param length name length - */ - public void setNameTextLength(byte length){ - field_3_length_name_text = length; - } - - /** sets the definition (reference - formula) length - * @param length defenition length - */ - public void setDefinitionTextLength(short length){ - field_4_length_name_definition = length; - } - - /** sets the index number to the extern sheet (thats is what writen in documentation - * but as i saw , it works differently) - * @param index extern sheet index - */ - public void setUnused(short index){ - field_5_index_to_sheet = index; - - // field_6_equals_to_index_to_sheet is equal to field_5_index_to_sheet -// field_6_equals_to_index_to_sheet = index; - } - /** * For named ranges, and built-in names - * @return the 1-based sheet number. Zero if this is a global name + * @return the 1-based sheet number. Zero if this is a global name */ public int getSheetNumber() { @@ -226,49 +167,12 @@ public final class NameRecord extends Record { } - /** sets the custom menu length - * @param length custom menu length - */ - public void setCustomMenuLength(byte length){ - field_7_length_custom_menu = length; - } - - /** sets the length of named range description - * @param length description length - */ - public void setDescriptionTextLength(byte length){ - field_8_length_description_text = length; - } - - /** sets the help topic length - * @param length help topic length - */ - public void setHelpTopicLength(byte length){ - field_9_length_help_topic_text = length; - } - - /** sets the length of the status bar text - * @param length status bar text length - */ - public void setStatusBarLength(byte length){ - field_10_length_status_bar_text = length; - } - - /** sets the compressed unicode flag - * @param flag unicode flag - */ - public void setCompressedUnicodeFlag(byte flag) { - field_11_compressed_unicode_flag = flag; - } - /** sets the name of the named range * @param name named range name */ public void setNameText(String name){ field_12_name_text = name; - setCompressedUnicodeFlag( - StringUtil.hasMultibyte(name) ? (byte)1 : (byte)0 - ); + field_11_nameIsMultibyte = StringUtil.hasMultibyte(name); } /** sets the custom menu text @@ -313,72 +217,15 @@ public final class NameRecord extends Record { return field_2_keyboard_shortcut ; } - /** + /** * gets the name length, in characters * @return name length */ - public byte getNameTextLength(){ - return field_3_length_name_text; - } - - /** - * gets the name length, in bytes - * @return raw name length - */ - public byte getRawNameTextLength(){ - if( (field_11_compressed_unicode_flag & 0x01) == 1 ) { - return (byte)(2 * field_3_length_name_text); + private int getNameTextLength(){ + if (isBuiltInName()) { + return 1; } - return field_3_length_name_text; - } - - /** get the definition length - * @return definition length - */ - public short getDefinitionLength(){ - return field_4_length_name_definition; - } - - /** gets the index to extern sheet - * @return index to extern sheet - */ - public short getUnused(){ - return field_5_index_to_sheet; - } - - /** gets the custom menu length - * @return custom menu length - */ - public byte getCustomMenuLength(){ - return field_7_length_custom_menu; - } - - /** gets the description text length - * @return description text length - */ - public byte getDescriptionTextLength(){ - return field_8_length_description_text; - } - - /** gets the help topic length - * @return help topic length - */ - public byte getHelpTopicLength(){ - return field_9_length_help_topic_text; - } - - /** get the status bar text length - * @return satus bar length - */ - public byte getStatusBarLength(){ - return field_10_length_status_bar_text; - } - - /** gets the name compressed Unicode flag - * @return compressed unicode flag - */ - public byte getCompressedUnicodeFlag() { - return field_11_compressed_unicode_flag; + return field_12_name_text.length(); } @@ -388,13 +235,26 @@ public final class NameRecord extends Record { public boolean isHiddenName() { return (field_1_option_flag & Option.OPT_HIDDEN_NAME) != 0; } + public void setHidden(boolean b) { + if (b) { + field_1_option_flag |= Option.OPT_HIDDEN_NAME; + } else { + field_1_option_flag &= (~Option.OPT_HIDDEN_NAME); + } + } /** - * @return true if name is a function + * @return true if name is a function */ public boolean isFunctionName() { return (field_1_option_flag & Option.OPT_FUNCTION_NAME) != 0; } + /** + * @return true if name has a formula (named range or defined value) + */ + public boolean hasFormula() { + return field_1_option_flag == 0 && field_13_name_definition.length > 0; + } /** * @return true if name is a command @@ -419,7 +279,7 @@ public final class NameRecord extends Record { */ public boolean isBuiltInName() { - return ((this.field_1_option_flag & Option.OPT_BUILTIN) != 0); + return ((field_1_option_flag & Option.OPT_BUILTIN) != 0); } @@ -428,7 +288,7 @@ public final class NameRecord extends Record { */ public String getNameText(){ - return this.isBuiltInName() ? this.translateBuiltInName(this.getBuiltInName()) : field_12_name_text; + return isBuiltInName() ? translateBuiltInName(getBuiltInName()) : field_12_name_text; } /** Gets the Built In Name @@ -436,19 +296,19 @@ public final class NameRecord extends Record { */ public byte getBuiltInName() { - return this.field_12_builtIn_name; + return field_12_built_in_code; } /** gets the definition, reference (Formula) - * @return definition -- can be null if we cant parse ptgs + * @return the name formula. never null */ - public List getNameDefinition() { - return field_13_name_definition; + public Ptg[] getNameDefinition() { + return (Ptg[]) field_13_name_definition.clone(); } - public void setNameDefinition(Stack nameDefinition) { - field_13_name_definition = nameDefinition; + public void setNameDefinition(Ptg[] ptgs) { + field_13_name_definition = (Ptg[]) ptgs.clone(); } /** get the custom menu text @@ -490,7 +350,8 @@ public final class NameRecord extends Record { throw new RecordFormatException("NOT A valid Name RECORD"); } } - + + /** * called by the class that is responsible for writing this sucker. * Subclasses should implement this so that their data is passed back in a @@ -498,109 +359,107 @@ public final class NameRecord extends Record { * @param data byte array containing instance data * @return number of bytes written */ - public int serialize( int offset, byte[] data ) - { - LittleEndian.putShort( data, 0 + offset, sid ); - short size = (short)( 15 + getTextsLength() + getNameDefinitionSize()); - LittleEndian.putShort( data, 2 + offset, size ); - // size defined below - LittleEndian.putShort( data, 4 + offset, getOptionFlag() ); - data[6 + offset] = getKeyboardShortcut(); - data[7 + offset] = getNameTextLength(); - LittleEndian.putShort( data, 8 + offset, getDefinitionLength() ); - LittleEndian.putShort( data, 10 + offset, getUnused() ); - LittleEndian.putUShort( data, 12 + offset, field_6_sheetNumber); - data[14 + offset] = getCustomMenuLength(); - data[15 + offset] = getDescriptionTextLength(); - data[16 + offset] = getHelpTopicLength(); - data[17 + offset] = getStatusBarLength(); - data[18 + offset] = getCompressedUnicodeFlag(); + public int serialize( int offset, byte[] data ) { - int start_of_name_definition = 19 + field_3_length_name_text; - - if (this.isBuiltInName()) { - //can send the builtin name directly in - data [19 + offset] = this.getBuiltInName(); - } else if ((this.getCompressedUnicodeFlag() & 0x01) == 1) { - StringUtil.putUnicodeLE( getNameText(), data, 19 + offset ); - start_of_name_definition = 19 + (2 * field_3_length_name_text); - } else { - StringUtil.putCompressedUnicode( getNameText(), data, 19 + offset ); - } - - - Ptg.serializePtgStack(field_13_name_definition, data, start_of_name_definition + offset ); - - - int start_of_custom_menu_text = start_of_name_definition + field_4_length_name_definition; - StringUtil.putCompressedUnicode( getCustomMenuText(), data, start_of_custom_menu_text + offset ); - - int start_of_description_text = start_of_custom_menu_text + field_7_length_custom_menu; - StringUtil.putCompressedUnicode( getDescriptionText(), data, start_of_description_text + offset ); - - int start_of_help_topic_text = start_of_description_text + field_8_length_description_text; - StringUtil.putCompressedUnicode( getHelpTopicText(), data, start_of_help_topic_text + offset ); - - int start_of_status_bar_text = start_of_help_topic_text + field_9_length_help_topic_text; - StringUtil.putCompressedUnicode( getStatusBarText(), data, start_of_status_bar_text + offset ); - - return getRecordSize(); - /* } */ - } - - /** - * Gets the length of all texts, in bytes - * @return total length - */ - public int getTextsLength(){ - int result; - - result = getRawNameTextLength() + getDescriptionTextLength() + - getHelpTopicLength() + getStatusBarLength(); - - return result; - } - - private int getNameDefinitionSize() { - int result = 0; - List list = field_13_name_definition; + int field_7_length_custom_menu = field_14_custom_menu_text.length(); + int field_8_length_description_text = field_15_description_text.length(); + int field_9_length_help_topic_text = field_16_help_topic_text.length(); + int field_10_length_status_bar_text = field_17_status_bar_text.length(); + int rawNameSize = getNameRawSize(); - for (int k = 0; k < list.size(); k++) - { - Ptg ptg = ( Ptg ) list.get(k); - - result += ptg.getSize(); + int formulaTotalSize = Ptg.getEncodedSize(field_13_name_definition); + int dataSize = 15 // 4 shorts + 7 bytes + + rawNameSize + + field_7_length_custom_menu + + field_8_length_description_text + + field_9_length_help_topic_text + + field_10_length_status_bar_text + + formulaTotalSize; + + LittleEndian.putShort(data, 0 + offset, sid); + LittleEndian.putUShort(data, 2 + offset, dataSize); + // size defined below + LittleEndian.putShort(data, 4 + offset, getOptionFlag()); + LittleEndian.putByte(data, 6 + offset, getKeyboardShortcut()); + LittleEndian.putByte(data, 7 + offset, getNameTextLength()); + // Note - + LittleEndian.putUShort(data, 8 + offset, Ptg.getEncodedSizeWithoutArrayData(field_13_name_definition)); + LittleEndian.putUShort(data, 10 + offset, field_5_index_to_sheet); + LittleEndian.putUShort(data, 12 + offset, field_6_sheetNumber); + LittleEndian.putByte(data, 14 + offset, field_7_length_custom_menu); + LittleEndian.putByte(data, 15 + offset, field_8_length_description_text); + LittleEndian.putByte(data, 16 + offset, field_9_length_help_topic_text); + LittleEndian.putByte(data, 17 + offset, field_10_length_status_bar_text); + LittleEndian.putByte(data, 18 + offset, field_11_nameIsMultibyte ? 1 : 0); + int pos = 19 + offset; + + if (isBuiltInName()) { + //can send the builtin name directly in + LittleEndian.putByte(data, pos, field_12_built_in_code); + } else { + String nameText = field_12_name_text; + if (field_11_nameIsMultibyte) { + StringUtil.putUnicodeLE(nameText, data, pos); + } else { + StringUtil.putCompressedUnicode(nameText, data, pos); + } } - return result; + pos += rawNameSize; + + Ptg.serializePtgs(field_13_name_definition, data, pos); + pos += formulaTotalSize; + + StringUtil.putCompressedUnicode( getCustomMenuText(), data, pos); + pos += field_7_length_custom_menu; + StringUtil.putCompressedUnicode( getDescriptionText(), data, pos); + pos += field_8_length_description_text; + StringUtil.putCompressedUnicode( getHelpTopicText(), data, pos); + pos += field_9_length_help_topic_text; + StringUtil.putCompressedUnicode( getStatusBarText(), data, pos); + + return 4 + dataSize; + } + private int getNameRawSize() { + if (isBuiltInName()) { + return 1; + } + int nChars = field_12_name_text.length(); + if(field_11_nameIsMultibyte) { + return 2 * nChars; + } + return nChars; } /** returns the record size */ public int getRecordSize(){ - int result; - - result = 19 + getTextsLength() + getNameDefinitionSize(); - - - return result; + return 4 // sid + size + + 15 // 4 shorts + 7 bytes + + getNameRawSize() + + field_14_custom_menu_text.length() + + field_15_description_text.length() + + field_16_help_topic_text.length() + + field_17_status_bar_text.length() + + Ptg.getEncodedSize(field_13_name_definition); } /** gets the extern sheet number * @return extern sheet index */ public short getExternSheetNumber(){ - if (field_13_name_definition == null || field_13_name_definition.isEmpty()) return 0; - Ptg ptg = (Ptg) field_13_name_definition.peek(); - short result = 0; + if (field_13_name_definition.length < 1) { + return 0; + } + Ptg ptg = field_13_name_definition[0]; if (ptg.getClass() == Area3DPtg.class){ - result = ((Area3DPtg) ptg).getExternSheetIndex(); + return ((Area3DPtg) ptg).getExternSheetIndex(); - } else if (ptg.getClass() == Ref3DPtg.class){ - result = ((Ref3DPtg) ptg).getExternSheetIndex(); } - - return result; + if (ptg.getClass() == Ref3DPtg.class){ + return ((Ref3DPtg) ptg).getExternSheetIndex(); + } + return 0; } /** sets the extern sheet number @@ -609,11 +468,13 @@ public final class NameRecord extends Record { public void setExternSheetNumber(short externSheetNumber){ Ptg ptg; - if (field_13_name_definition == null || field_13_name_definition.isEmpty()){ - field_13_name_definition = new Stack(); + if (field_13_name_definition.length < 1){ ptg = createNewPtg(); + field_13_name_definition = new Ptg[] { + ptg, + }; } else { - ptg = (Ptg) field_13_name_definition.peek(); + ptg = field_13_name_definition[0]; } if (ptg.getClass() == Area3DPtg.class){ @@ -625,11 +486,8 @@ public final class NameRecord extends Record { } - private Ptg createNewPtg(){ - Ptg ptg = new Area3DPtg("A1", 0); // TODO - change to not be partially initialised - field_13_name_definition.push(ptg); - - return ptg; + private static Ptg createNewPtg(){ + return new Area3DPtg("A1:A1", 0); // TODO - change to not be partially initialised } /** gets the reference , the area only (range) @@ -646,16 +504,14 @@ public final class NameRecord extends Record { //Trying to find if what ptg do we need RangeAddress ra = new RangeAddress(ref); Ptg oldPtg; - Ptg ptg; - if (field_13_name_definition==null ||field_13_name_definition.isEmpty()){ - field_13_name_definition = new Stack(); + if (field_13_name_definition.length < 1){ oldPtg = createNewPtg(); } else { //Trying to find extern sheet index - oldPtg = (Ptg) field_13_name_definition.pop(); + oldPtg = field_13_name_definition[0]; } - + List temp = new ArrayList(); short externSheetIndex = 0; if (oldPtg.getClass() == Area3DPtg.class){ @@ -667,29 +523,27 @@ public final class NameRecord extends Record { if (ra.hasRange()) { // Is it contiguous or not? - AreaReference[] refs = - AreaReference.generateContiguous(ref); - this.setDefinitionTextLength((short)0); + AreaReference[] refs = AreaReference.generateContiguous(ref); - // Add the area reference(s) + // Add the area reference(s) for(int i=0; i 1) { - ptg = UnionPtg.instance; - field_13_name_definition.push(ptg); - this.setDefinitionTextLength( (short)(getDefinitionLength() + ptg.getSize()) ); + Ptg ptg = UnionPtg.instance; + temp.add(ptg); } } else { - ptg = new Ref3DPtg(); + Ptg ptg = new Ref3DPtg(); ((Ref3DPtg) ptg).setExternSheetIndex(externSheetIndex); ((Ref3DPtg) ptg).setArea(ref); - field_13_name_definition.push(ptg); - this.setDefinitionTextLength((short)ptg.getSize()); + temp.add(ptg); } + Ptg[] ptgs = new Ptg[temp.size()]; + temp.toArray(ptgs); + field_13_name_definition = ptgs; } /** @@ -699,40 +553,36 @@ public final class NameRecord extends Record { * @param in the RecordInputstream to read the record from */ protected void fillFields(RecordInputStream in) { - field_1_option_flag = in.readShort(); - field_2_keyboard_shortcut = in.readByte(); - field_3_length_name_text = in.readByte(); - field_4_length_name_definition = in.readShort(); - field_5_index_to_sheet = in.readShort(); - field_6_sheetNumber = in.readUShort(); - field_7_length_custom_menu = in.readByte(); - field_8_length_description_text = in.readByte(); - field_9_length_help_topic_text = in.readByte(); - field_10_length_status_bar_text = in.readByte(); - - //store the name in byte form if it's a builtin name - field_11_compressed_unicode_flag= in.readByte(); - if (this.isBuiltInName()) { - field_12_builtIn_name = in.readByte(); - } else { - if (field_11_compressed_unicode_flag == 1) { - field_12_name_text = in.readUnicodeLEString(field_3_length_name_text); - } else { - field_12_name_text = in.readCompressedUnicode(field_3_length_name_text); - } + field_1_option_flag = in.readShort(); + field_2_keyboard_shortcut = in.readByte(); + int field_3_length_name_text = in.readByte(); + int field_4_length_name_definition = in.readShort(); + field_5_index_to_sheet = in.readShort(); + field_6_sheetNumber = in.readUShort(); + int field_7_length_custom_menu = in.readUByte(); + int field_8_length_description_text = in.readUByte(); + int field_9_length_help_topic_text = in.readUByte(); + int field_10_length_status_bar_text = in.readUByte(); + + //store the name in byte form if it's a built-in name + field_11_nameIsMultibyte = (in.readByte() != 0); + if (isBuiltInName()) { + field_12_built_in_code = in.readByte(); + } else { + if (field_11_nameIsMultibyte) { + field_12_name_text = in.readUnicodeLEString(field_3_length_name_text); + } else { + field_12_name_text = in.readCompressedUnicode(field_3_length_name_text); + } } - - field_13_name_definition = Ptg.createParsedExpressionTokens(field_4_length_name_definition, in); - + + field_13_name_definition = Ptg.readTokens(field_4_length_name_definition, in); + //Who says that this can only ever be compressed unicode??? - field_14_custom_menu_text = in.readCompressedUnicode(LittleEndian.ubyteToInt(field_7_length_custom_menu)); - - field_15_description_text = in.readCompressedUnicode(LittleEndian.ubyteToInt(field_8_length_description_text)); - - field_16_help_topic_text = in.readCompressedUnicode(LittleEndian.ubyteToInt(field_9_length_help_topic_text)); - - field_17_status_bar_text = in.readCompressedUnicode(LittleEndian.ubyteToInt(field_10_length_status_bar_text)); - /*} */ + field_14_custom_menu_text = in.readCompressedUnicode(field_7_length_custom_menu); + field_15_description_text = in.readCompressedUnicode(field_8_length_description_text); + field_16_help_topic_text = in.readCompressedUnicode(field_9_length_help_topic_text); + field_17_status_bar_text = in.readCompressedUnicode(field_10_length_status_bar_text); } /** @@ -742,113 +592,90 @@ public final class NameRecord extends Record { return sid; } /* - 20 00 - 00 - 01 + 20 00 + 00 + 01 1A 00 // sz = 0x1A = 26 - 00 00 - 01 00 - 00 - 00 - 00 - 00 + 00 00 + 01 00 + 00 + 00 + 00 + 00 00 // unicode flag 07 // name - + 29 17 00 3B 00 00 00 00 FF FF 00 00 02 00 3B 00 //{ 26 00 07 00 07 00 00 00 FF 00 10 // } - - - - 20 00 - 00 - 01 + + + + 20 00 + 00 + 01 0B 00 // sz = 0xB = 11 - 00 00 - 01 00 - 00 - 00 - 00 - 00 + 00 00 + 01 00 + 00 + 00 + 00 + 00 00 // unicode flag 07 // name - + 3B 00 00 07 00 07 00 00 00 FF 00 // { 11 } */ /* - 18, 00, - 1B, 00, - - 20, 00, - 00, - 01, - 0B, 00, - 00, - 00, - 00, - 00, - 00, - 07, - 3B 00 00 07 00 07 00 00 00 FF 00 ] + 18, 00, + 1B, 00, + + 20, 00, + 00, + 01, + 0B, 00, + 00, + 00, + 00, + 00, + 00, + 07, + 3B 00 00 07 00 07 00 00 00 FF 00 ] */ - /** - * @see Object#toString() - */ public String toString() { - StringBuffer buffer = new StringBuffer(); + StringBuffer sb = new StringBuffer(); - buffer.append("[NAME]\n"); - buffer.append(" .option flags = ").append( HexDump.toHex( field_1_option_flag ) ) - .append("\n"); - buffer.append(" .keyboard shortcut = ").append( HexDump.toHex( field_2_keyboard_shortcut ) ) - .append("\n"); - buffer.append(" .length of the name = ").append( field_3_length_name_text ) - .append("\n"); - buffer.append(" .size of the formula data = ").append( field_4_length_name_definition ) - .append("\n"); - buffer.append(" .unused = ").append( field_5_index_to_sheet ) - .append("\n"); - buffer.append(" .index to sheet (1-based, 0=Global) = ").append( field_6_sheetNumber ) - .append("\n"); - buffer.append(" .Length of menu text (character count) = ").append( field_7_length_custom_menu ) - .append("\n"); - buffer.append(" .Length of description text (character count) = ").append( field_8_length_description_text ) - .append("\n"); - buffer.append(" .Length of help topic text (character count) = ").append( field_9_length_help_topic_text ) - .append("\n"); - buffer.append(" .Length of status bar text (character count) = ").append( field_10_length_status_bar_text ) - .append("\n"); - buffer.append(" .Name (Unicode flag) = ").append( field_11_compressed_unicode_flag ) - .append("\n"); - buffer.append(" .Name (Unicode text) = ").append( getNameText() ) - .append("\n"); - - buffer.append(" .Parts (" + field_13_name_definition.size() +"):") - .append("\n"); - Iterator it = field_13_name_definition.iterator(); - while(it.hasNext()) { - Ptg ptg = (Ptg)it.next(); - buffer.append(" " + ptg.toString()).append("\n"); + sb.append("[NAME]\n"); + sb.append(" .option flags = ").append(HexDump.shortToHex(field_1_option_flag)).append("\n"); + sb.append(" .keyboard shortcut = ").append(HexDump.byteToHex(field_2_keyboard_shortcut)).append("\n"); + sb.append(" .length of the name = ").append(getNameTextLength()).append("\n"); + sb.append(" .unused = ").append( field_5_index_to_sheet ).append("\n"); + sb.append(" .index to sheet (1-based, 0=Global) = ").append( field_6_sheetNumber ).append("\n"); + sb.append(" .Menu text length = ").append(field_14_custom_menu_text.length()).append("\n"); + sb.append(" .Description text length= ").append(field_15_description_text.length()).append("\n"); + sb.append(" .Help topic text length = ").append(field_16_help_topic_text.length()).append("\n"); + sb.append(" .Status bar text length = ").append(field_17_status_bar_text.length()).append("\n"); + sb.append(" .NameIsMultibyte = ").append(field_11_nameIsMultibyte).append("\n"); + sb.append(" .Name (Unicode text) = ").append( getNameText() ).append("\n"); + sb.append(" .Formula (nTokens=").append(field_13_name_definition.length).append("):") .append("\n"); + for (int i = 0; i < field_13_name_definition.length; i++) { + Ptg ptg = field_13_name_definition[i]; + sb.append(" " + ptg.toString()).append(ptg.getRVAType()).append("\n"); } - - buffer.append(" .Menu text (Unicode string without length field) = ").append( field_14_custom_menu_text ) - .append("\n"); - buffer.append(" .Description text (Unicode string without length field) = ").append( field_15_description_text ) - .append("\n"); - buffer.append(" .Help topic text (Unicode string without length field) = ").append( field_16_help_topic_text ) - .append("\n"); - buffer.append(" .Status bar text (Unicode string without length field) = ").append( field_17_status_bar_text ) - .append("\n"); - buffer.append("[/NAME]\n"); - - return buffer.toString(); + + sb.append(" .Menu text = ").append(field_14_custom_menu_text).append("\n"); + sb.append(" .Description text= ").append(field_15_description_text).append("\n"); + sb.append(" .Help topic text = ").append(field_16_help_topic_text).append("\n"); + sb.append(" .Status bar text = ").append(field_17_status_bar_text).append("\n"); + sb.append("[/NAME]\n"); + + return sb.toString(); } /**Creates a human readable name for built in types * @return Unknown if the built-in name cannot be translated */ - protected String translateBuiltInName(byte name) + private static String translateBuiltInName(byte name) { switch (name) { @@ -859,14 +686,15 @@ public final class NameRecord extends Record { case NameRecord.BUILTIN_CONSOLIDATE_AREA : return "Consolidate_Area"; case NameRecord.BUILTIN_CRITERIA : return "Criteria"; case NameRecord.BUILTIN_DATABASE : return "Database"; - case NameRecord.BUILTIN_DATA_FORM : return "Data_Form"; + case NameRecord.BUILTIN_DATA_FORM : return "Data_Form"; case NameRecord.BUILTIN_PRINT_AREA : return "Print_Area"; case NameRecord.BUILTIN_PRINT_TITLE : return "Print_Titles"; case NameRecord.BUILTIN_RECORDER : return "Recorder"; case NameRecord.BUILTIN_SHEET_TITLE : return "Sheet_Title"; - + case NameRecord.BUILTIN_FILTER_DB : return "_FilterDatabase"; + } - + return "Unknown"; } } diff --git a/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java b/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java index 5ea1061ba..49994c059 100644 --- a/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java @@ -94,10 +94,14 @@ public final class ArrayPtg extends Ptg { buffer.append("columns = ").append(getColumnCount()).append("\n"); buffer.append("rows = ").append(getRowCount()).append("\n"); - for (int x=0;x + * + * @author Josh Micich + */ +final class ParityFunction implements FreeRefFunction { + + public static final FreeRefFunction IS_EVEN = new ParityFunction(0); + public static final FreeRefFunction IS_ODD = new ParityFunction(1); + private final int _desiredParity; + + private ParityFunction(int desiredParity) { + _desiredParity = desiredParity; + } + + public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, Workbook workbook, + Sheet sheet) { + if (args.length != 1) { + return ErrorEval.VALUE_INVALID; + } + + int val; + try { + val = evaluateArgParity(args[0], srcCellRow, srcCellCol); + } catch (EvaluationException e) { + return e.getErrorEval(); + } + + return BoolEval.valueOf(val == _desiredParity); + } + + private static int evaluateArgParity(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException { + ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol); + + if (ve == BlankEval.INSTANCE) { + return 0; + } + double d = OperandResolver.coerceValueToDouble(ve); + if (d < 0) { + d = -d; + } + long v = (long) Math.floor(d); + return (int) (v & 0x0001); + } +} diff --git a/src/java/org/apache/poi/hssf/record/formula/atp/YearFrac.java b/src/java/org/apache/poi/hssf/record/formula/atp/YearFrac.java index 517189d40..96718ef4e 100644 --- a/src/java/org/apache/poi/hssf/record/formula/atp/YearFrac.java +++ b/src/java/org/apache/poi/hssf/record/formula/atp/YearFrac.java @@ -21,6 +21,7 @@ import java.util.Calendar; import java.util.GregorianCalendar; import java.util.regex.Pattern; +import org.apache.poi.hssf.record.formula.eval.BlankEval; import org.apache.poi.hssf.record.formula.eval.ErrorEval; import org.apache.poi.hssf.record.formula.eval.Eval; import org.apache.poi.hssf.record.formula.eval.EvaluationException; @@ -96,6 +97,9 @@ final class YearFrac implements FreeRefFunction { Calendar date = parseDate(strVal); return DateUtil.getExcelDate(date, false); } + if (ve instanceof BlankEval) { + return 0.0; + } return OperandResolver.coerceValueToDouble(ve); } @@ -120,7 +124,7 @@ final class YearFrac implements FreeRefFunction { } catch (NumberFormatException e) { throw new EvaluationException(ErrorEval.VALUE_INVALID); } - if (f0<0 || f1<0 || f2<0 || f0>12 || f1>12 || f2>12) { + if (f0<0 || f1<0 || f2<0 || (f0>12 && f1>12 && f2>12)) { // easy to see this cannot be a valid date throw new EvaluationException(ErrorEval.VALUE_INVALID); } @@ -150,6 +154,7 @@ final class YearFrac implements FreeRefFunction { if (day <1 || day>cal.getActualMaximum(Calendar.DAY_OF_MONTH)) { throw new EvaluationException(ErrorEval.VALUE_INVALID); } + cal.set(Calendar.DAY_OF_MONTH, day); return cal; } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/AddEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/AddEval.java index 6562263d5..bf1b42421 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/AddEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/AddEval.java @@ -1,27 +1,22 @@ -/* -* Licensed to the Apache Software Foundation (ASF) under one or more -* contributor license agreements. See the NOTICE file distributed with -* this work for additional information regarding copyright ownership. -* The ASF licenses this file to You under the Apache License, Version 2.0 -* (the "License"); you may not use this file except in compliance with -* the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ -/* - * Created on May 8, 2005 - * - */ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + package org.apache.poi.hssf.record.formula.eval; -import org.apache.poi.hssf.record.formula.AddPtg; -import org.apache.poi.hssf.record.formula.Ptg; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > @@ -37,58 +32,14 @@ import org.apache.poi.hssf.record.formula.Ptg; *
  • 1+A1 = 2 if A1 contains TRUE or =TRUE *
  • 1+A1 = #VALUE! if A1 contains "TRUE" or ="TRUE" */ -public class AddEval extends NumericOperationEval { +public final class AddEval extends TwoOperandNumericOperation { - private AddPtg delegate; - private static final ValueEvalToNumericXlator NUM_XLATOR = - new ValueEvalToNumericXlator((short) - ( ValueEvalToNumericXlator.BOOL_IS_PARSED - | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - | ValueEvalToNumericXlator.REF_STRING_IS_PARSED - )); + public static final OperationEval instance = new AddEval(); - public AddEval(Ptg ptg) { - delegate = (AddPtg) ptg; - } + private AddEval() { + } - public ValueEvalToNumericXlator getXlator() { - return NUM_XLATOR; - } - - - public Eval evaluate(Eval[] args, int srcRow, short srcCol) { - if(args.length != 2) { - return ErrorEval.VALUE_INVALID; - } - - double d = 0; - for (int i = 0; i < 2; i++) { - ValueEval ve = singleOperandEvaluate(args[i], srcRow, srcCol); - if(ve instanceof ErrorEval) { - return ve; - } - if (ve instanceof NumericValueEval) { - d += ((NumericValueEval) ve).getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - return ErrorEval.VALUE_INVALID; - } - } - if(Double.isNaN(d) || Double.isInfinite(d)) { - return ErrorEval.NUM_ERROR; - } - return new NumberEval(d); - } - - public int getNumberOfOperands() { - return delegate.getNumberOfOperands(); - } - - public int getType() { - return delegate.getType(); - } + protected double evaluate(double d0, double d1) { + return d0 + d1; + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/DivideEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/DivideEval.java index 021168ad7..ba0b07e7e 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/DivideEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/DivideEval.java @@ -1,95 +1,36 @@ -/* -* 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; -import org.apache.poi.hssf.record.formula.Ptg; -import org.apache.poi.hssf.record.formula.DividePtg; - /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * */ -public final class DivideEval extends NumericOperationEval { +public final class DivideEval extends TwoOperandNumericOperation { - private DividePtg delegate; + public static final OperationEval instance = new DivideEval(); - private static final ValueEvalToNumericXlator NUM_XLATOR = - new ValueEvalToNumericXlator((short) - ( ValueEvalToNumericXlator.BOOL_IS_PARSED - | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - | ValueEvalToNumericXlator.REF_STRING_IS_PARSED - )); + private DivideEval() { + } - public DivideEval(Ptg ptg) { - delegate = (DividePtg) ptg; - } - - protected ValueEvalToNumericXlator getXlator() { - return NUM_XLATOR; - } - - public Eval evaluate(Eval[] args, int srcRow, short srcCol) { - if(args.length != 2) { - return ErrorEval.VALUE_INVALID; - } - Eval retval = null; - double d0 = 0; - double d1 = 0; - ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - d0 = ((NumericValueEval) ve).getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - retval = ErrorEval.VALUE_INVALID; - } - - if (retval == null) { // no error yet - ve = singleOperandEvaluate(args[1], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - d1 = ((NumericValueEval) ve).getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - retval = ErrorEval.VALUE_INVALID; - } - } - - if (retval == null) { - retval = (d1 == 0) - ? ErrorEval.DIV_ZERO - : (Double.isNaN(d0) || Double.isNaN(d1)) - ? (ValueEval) ErrorEval.VALUE_INVALID - : new NumberEval(d0 / d1); - } - return retval; - } - - public int getNumberOfOperands() { - return delegate.getNumberOfOperands(); - } - - public int getType() { - return delegate.getType(); - } + protected double evaluate(double d0, double d1) throws EvaluationException { + if (d1 == 0.0) { + throw new EvaluationException(ErrorEval.DIV_ZERO); + } + return d0 / d1; + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/EqualEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/EqualEval.java index e7b169294..8dca60906 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/EqualEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/EqualEval.java @@ -1,67 +1,34 @@ -/* -* Licensed to the Apache Software Foundation (ASF) under one or more -* contributor license agreements. See the NOTICE file distributed with -* this work for additional information regarding copyright ownership. -* The ASF licenses this file to You under the Apache License, Version 2.0 -* (the "License"); you may not use this file except in compliance with -* the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ -/* - * Created on May 8, 2005 - * - */ -package org.apache.poi.hssf.record.formula.eval; +/* ==================================================================== + 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 -import org.apache.poi.hssf.record.formula.EqualPtg; -import org.apache.poi.hssf.record.formula.Ptg; + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hssf.record.formula.eval; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > * */ -public class EqualEval extends RelationalOperationEval { +public final class EqualEval extends RelationalOperationEval { - private EqualPtg delegate; + public static final OperationEval instance = new EqualEval(); + + private EqualEval() { + } - public EqualEval(Ptg ptg) { - this.delegate = (EqualPtg) ptg; - } - - - public Eval evaluate(Eval[] operands, int srcRow, short srcCol) { - ValueEval retval = null; - - RelationalValues rvs = super.doEvaluate(operands, srcRow, srcCol); - retval = rvs.ee; - int result = 0; - if (retval == null) { - result = doComparison(rvs.bs); - if (result == 0) { - result = doComparison(rvs.ss); - } - if (result == 0) { - result = doComparison(rvs.ds); - } - - retval = (result == 0) ? BoolEval.TRUE : BoolEval.FALSE; - } - - return retval; - } - - public int getNumberOfOperands() { - return delegate.getNumberOfOperands(); - } - - public int getType() { - return delegate.getType(); - } + protected boolean convertComparisonResult(int cmpResult) { + return cmpResult == 0; + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/ExternalFunction.java b/src/java/org/apache/poi/hssf/record/formula/eval/ExternalFunction.java index ffaf780ab..95dd0b545 100755 --- a/src/java/org/apache/poi/hssf/record/formula/eval/ExternalFunction.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/ExternalFunction.java @@ -42,7 +42,7 @@ final class ExternalFunction implements FreeRefFunction { FreeRefFunction targetFunc; try { if (nameArg instanceof NameEval) { - targetFunc = findInternalUserDefinedFunction(workbook, (NameEval) nameArg); + targetFunc = findInternalUserDefinedFunction((NameEval) nameArg); } else if (nameArg instanceof NameXEval) { targetFunc = findExternalUserDefinedFunction(workbook, (NameXEval) nameArg); } else { @@ -65,7 +65,7 @@ final class ExternalFunction implements FreeRefFunction { if(false) { System.out.println("received call to external user defined function (" + functionName + ")"); } - // currently only looking for functions from the 'Analysis TookPak' + // currently only looking for functions from the 'Analysis TookPak' e.g. "YEARFRAC" or "ISEVEN" // not sure how much this logic would need to change to support other or multiple add-ins. FreeRefFunction result = AnalysisToolPak.findFunction(functionName); if (result != null) { @@ -74,24 +74,12 @@ final class ExternalFunction implements FreeRefFunction { throw new EvaluationException(ErrorEval.FUNCTION_NOT_IMPLEMENTED); } - private FreeRefFunction findInternalUserDefinedFunction(Workbook workbook, NameEval functionNameEval) throws EvaluationException { - - int numberOfNames = workbook.getNumberOfNames(); - - int nameIndex = functionNameEval.getIndex(); - if(nameIndex < 0 || nameIndex >= numberOfNames) { - throw new RuntimeException("Bad name index (" + nameIndex - + "). Allowed range is (0.." + (numberOfNames-1) + ")"); - } - - String functionName = workbook.getNameName(nameIndex); + private FreeRefFunction findInternalUserDefinedFunction(NameEval functionNameEval) throws EvaluationException { + String functionName = functionNameEval.getFunctionName(); if(false) { System.out.println("received call to internal user defined function (" + functionName + ")"); } - // TODO - detect if the NameRecord corresponds to a named range, function, or something undefined - // throw the right errors in these cases - - // TODO find the implementation for the external function e.g. "YEARFRAC" or "ISEVEN" + // TODO find the implementation for the user defined function throw new EvaluationException(ErrorEval.FUNCTION_NOT_IMPLEMENTED); } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/GreaterEqualEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/GreaterEqualEval.java index 6a9a23217..16dd48846 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/GreaterEqualEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/GreaterEqualEval.java @@ -1,67 +1,34 @@ -/* -* Licensed to the Apache Software Foundation (ASF) under one or more -* contributor license agreements. See the NOTICE file distributed with -* this work for additional information regarding copyright ownership. -* The ASF licenses this file to You under the Apache License, Version 2.0 -* (the "License"); you may not use this file except in compliance with -* the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ -/* - * Created on May 8, 2005 - * - */ -package org.apache.poi.hssf.record.formula.eval; +/* ==================================================================== + 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 -import org.apache.poi.hssf.record.formula.GreaterEqualPtg; -import org.apache.poi.hssf.record.formula.Ptg; + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hssf.record.formula.eval; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > * */ -public class GreaterEqualEval extends RelationalOperationEval { +public final class GreaterEqualEval extends RelationalOperationEval { - private GreaterEqualPtg delegate; - - public GreaterEqualEval(Ptg ptg) { - this.delegate = (GreaterEqualPtg) ptg; - } - - public Eval evaluate(Eval[] operands, int srcRow, short srcCol) { - ValueEval retval = null; - - RelationalValues rvs = super.doEvaluate(operands, srcRow, srcCol); - retval = rvs.ee; - int result = 0; - if (retval == null) { - result = doComparison(rvs.bs); - if (result == 0) { - result = doComparison(rvs.ss); - } - if (result == 0) { - result = doComparison(rvs.ds); - } - - retval = (result >= 0) ? BoolEval.TRUE : BoolEval.FALSE; - } - - return retval; - } - - public int getNumberOfOperands() { - return delegate.getNumberOfOperands(); - } - - public int getType() { - return delegate.getType(); - } + public static final OperationEval instance = new GreaterEqualEval(); + + private GreaterEqualEval() { + } + protected boolean convertComparisonResult(int cmpResult) { + return cmpResult >= 0; + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/GreaterThanEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/GreaterThanEval.java index 27da54a04..45605a41c 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/GreaterThanEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/GreaterThanEval.java @@ -1,67 +1,34 @@ -/* -* Licensed to the Apache Software Foundation (ASF) under one or more -* contributor license agreements. See the NOTICE file distributed with -* this work for additional information regarding copyright ownership. -* The ASF licenses this file to You under the Apache License, Version 2.0 -* (the "License"); you may not use this file except in compliance with -* the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ -/* - * Created on May 8, 2005 - * - */ -package org.apache.poi.hssf.record.formula.eval; +/* ==================================================================== + 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 -import org.apache.poi.hssf.record.formula.GreaterThanPtg; -import org.apache.poi.hssf.record.formula.Ptg; + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hssf.record.formula.eval; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > * */ -public class GreaterThanEval extends RelationalOperationEval { +public final class GreaterThanEval extends RelationalOperationEval { - private GreaterThanPtg delegate; - - public GreaterThanEval(Ptg ptg) { - this.delegate = (GreaterThanPtg) ptg; - } - - public Eval evaluate(Eval[] operands, int srcRow, short srcCol) { - ValueEval retval = null; - - RelationalValues rvs = super.doEvaluate(operands, srcRow, srcCol); - retval = rvs.ee; - int result = 0; - if (retval == null) { - result = doComparison(rvs.bs); - if (result == 0) { - result = doComparison(rvs.ss); - } - if (result == 0) { - result = doComparison(rvs.ds); - } - - retval = (result > 0) ? BoolEval.TRUE : BoolEval.FALSE;; - } - - return retval; - } - - public int getNumberOfOperands() { - return delegate.getNumberOfOperands(); - } - - public int getType() { - return delegate.getType(); - } + public static final OperationEval instance = new GreaterThanEval(); + + private GreaterThanEval() { + } + protected boolean convertComparisonResult(int cmpResult) { + return cmpResult > 0; + } } 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 index b5c633617..9f3830554 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/LazyAreaEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/LazyAreaEval.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; @@ -24,20 +24,21 @@ import org.apache.poi.ss.usermodel.FormulaEvaluator; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.util.CellReference; /** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * + * + * @author Josh Micich */ public final class LazyAreaEval extends AreaEvalBase { private final Sheet _sheet; - private Workbook _workbook; + private FormulaEvaluator _evaluator; - public LazyAreaEval(AreaI ptg, Sheet sheet, Workbook workbook) { + public LazyAreaEval(AreaI ptg, Sheet sheet, FormulaEvaluator evaluator) { super(ptg); _sheet = sheet; - _workbook = workbook; + _evaluator = evaluator; } public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) { @@ -53,13 +54,27 @@ public final class LazyAreaEval extends AreaEvalBase { if (cell == null) { return BlankEval.INSTANCE; } - return FormulaEvaluator.getEvalForCell(cell, _sheet, _workbook); + return _evaluator.getEvalForCell(cell, _sheet); } public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) { AreaI area = new OffsetArea(getFirstRow(), getFirstColumn(), relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx); - return new LazyAreaEval(area, _sheet, _workbook); + return new LazyAreaEval(area, _sheet, _evaluator); + } + public String toString() { + CellReference crA = new CellReference(getFirstRow(), getFirstColumn()); + CellReference crB = new CellReference(getLastRow(), getLastColumn()); + StringBuffer sb = new StringBuffer(); + sb.append(getClass().getName()).append("["); + String sheetName = _evaluator.getSheetName(_sheet); + sb.append(sheetName); + sb.append('!'); + sb.append(crA.formatAsString()); + sb.append(':'); + sb.append(crB.formatAsString()); + sb.append("]"); + return sb.toString(); } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/LazyRefEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/LazyRefEval.java index 7daf96424..b45801197 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/LazyRefEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/LazyRefEval.java @@ -1,30 +1,51 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + package org.apache.poi.hssf.record.formula.eval; import org.apache.poi.hssf.record.formula.AreaI; -import org.apache.poi.hssf.record.formula.AreaI.OffsetArea; import org.apache.poi.hssf.record.formula.Ref3DPtg; import org.apache.poi.hssf.record.formula.RefPtg; +import org.apache.poi.hssf.record.formula.AreaI.OffsetArea; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.FormulaEvaluator; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.util.CellReference; +/** +* +* @author Josh Micich +*/ public final class LazyRefEval extends RefEvalBase { - private final Sheet _sheet; - private final Workbook _workbook; + private final FormulaEvaluator _evaluator; - public LazyRefEval(RefPtg ptg, Sheet sheet, Workbook workbook) { + public LazyRefEval(RefPtg ptg, Sheet sheet, FormulaEvaluator evaluator) { super(ptg.getRow(), ptg.getColumn()); _sheet = sheet; - _workbook = workbook; + _evaluator = evaluator; } - public LazyRefEval(Ref3DPtg ptg, Sheet sheet, Workbook workbook) { + public LazyRefEval(Ref3DPtg ptg, Sheet sheet, FormulaEvaluator evaluator) { super(ptg.getRow(), ptg.getColumn()); _sheet = sheet; - _workbook = workbook; + _evaluator = evaluator; } public ValueEval getInnerValueEval() { @@ -39,7 +60,7 @@ public final class LazyRefEval extends RefEvalBase { if (cell == null) { return BlankEval.INSTANCE; } - return FormulaEvaluator.getEvalForCell(cell, _sheet, _workbook); + return _evaluator.getEvalForCell(cell, _sheet); } public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) { @@ -47,6 +68,18 @@ public final class LazyRefEval extends RefEvalBase { AreaI area = new OffsetArea(getRow(), getColumn(), relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx); - return new LazyAreaEval(area, _sheet, _workbook); + return new LazyAreaEval(area, _sheet, _evaluator); + } + + public String toString() { + CellReference cr = new CellReference(getRow(), getColumn()); + StringBuffer sb = new StringBuffer(); + sb.append(getClass().getName()).append("["); + String sheetName = _evaluator.getSheetName(_sheet); + sb.append(sheetName); + sb.append('!'); + sb.append(cr.formatAsString()); + sb.append("]"); + return sb.toString(); } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/LessEqualEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/LessEqualEval.java index e45bf9e6b..e62be04d0 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/LessEqualEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/LessEqualEval.java @@ -1,67 +1,34 @@ -/* -* Licensed to the Apache Software Foundation (ASF) under one or more -* contributor license agreements. See the NOTICE file distributed with -* this work for additional information regarding copyright ownership. -* The ASF licenses this file to You under the Apache License, Version 2.0 -* (the "License"); you may not use this file except in compliance with -* the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ -/* - * Created on May 8, 2005 - * - */ -package org.apache.poi.hssf.record.formula.eval; +/* ==================================================================== + 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 -import org.apache.poi.hssf.record.formula.LessEqualPtg; -import org.apache.poi.hssf.record.formula.Ptg; + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hssf.record.formula.eval; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > * */ -public class LessEqualEval extends RelationalOperationEval { +public final class LessEqualEval extends RelationalOperationEval { - private LessEqualPtg delegate; - - public LessEqualEval(Ptg ptg) { - this.delegate = (LessEqualPtg) ptg; - } - - public Eval evaluate(Eval[] operands, int srcRow, short srcCol) { - ValueEval retval = null; - - RelationalValues rvs = super.doEvaluate(operands, srcRow, srcCol); - retval = rvs.ee; - int result = 0; - if (retval == null) { - result = doComparison(rvs.bs); - if (result == 0) { - result = doComparison(rvs.ss); - } - if (result == 0) { - result = doComparison(rvs.ds); - } - - retval = (result <= 0) ? BoolEval.TRUE : BoolEval.FALSE;; - } - - return retval; - } - - public int getNumberOfOperands() { - return delegate.getNumberOfOperands(); - } - - public int getType() { - return delegate.getType(); - } + public static final OperationEval instance = new LessEqualEval(); + + private LessEqualEval() { + } + protected boolean convertComparisonResult(int cmpResult) { + return cmpResult <= 0; + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/LessThanEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/LessThanEval.java index 1aac6f73b..cf1fc1bef 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/LessThanEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/LessThanEval.java @@ -1,68 +1,34 @@ -/* -* Licensed to the Apache Software Foundation (ASF) under one or more -* contributor license agreements. See the NOTICE file distributed with -* this work for additional information regarding copyright ownership. -* The ASF licenses this file to You under the Apache License, Version 2.0 -* (the "License"); you may not use this file except in compliance with -* the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ -/* - * Created on May 8, 2005 - * - */ -package org.apache.poi.hssf.record.formula.eval; +/* ==================================================================== + 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 -import org.apache.poi.hssf.record.formula.LessThanPtg; -import org.apache.poi.hssf.record.formula.Ptg; + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hssf.record.formula.eval; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > * */ -public class LessThanEval extends RelationalOperationEval { +public final class LessThanEval extends RelationalOperationEval { - private LessThanPtg delegate; - - public LessThanEval(Ptg ptg) { - this.delegate = (LessThanPtg) ptg; - } - - - public Eval evaluate(Eval[] operands, int srcRow, short srcCol) { - ValueEval retval = null; - - RelationalValues rvs = super.doEvaluate(operands, srcRow, srcCol); - retval = rvs.ee; - int result = 0; - if (retval == null) { - result = doComparison(rvs.bs); - if (result == 0) { - result = doComparison(rvs.ss); - } - if (result == 0) { - result = doComparison(rvs.ds); - } - - retval = (result < 0) ? BoolEval.TRUE : BoolEval.FALSE;; - } - - return retval; - } - - public int getNumberOfOperands() { - return delegate.getNumberOfOperands(); - } - - public int getType() { - return delegate.getType(); - } + public static final OperationEval instance = new LessThanEval(); + + private LessThanEval() { + } + protected boolean convertComparisonResult(int cmpResult) { + return cmpResult < 0; + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/MultiplyEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/MultiplyEval.java index 22d87b7e4..83b829c71 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/MultiplyEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/MultiplyEval.java @@ -1,89 +1,33 @@ -/* -* Licensed to the Apache Software Foundation (ASF) under one or more -* contributor license agreements. See the NOTICE file distributed with -* this work for additional information regarding copyright ownership. -* The ASF licenses this file to You under the Apache License, Version 2.0 -* (the "License"); you may not use this file except in compliance with -* the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ package org.apache.poi.hssf.record.formula.eval; -import org.apache.poi.hssf.record.formula.Ptg; -import org.apache.poi.hssf.record.formula.MultiplyPtg; - /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * */ -public final class MultiplyEval extends NumericOperationEval { +public final class MultiplyEval extends TwoOperandNumericOperation { - private MultiplyPtg delegate; + public static final OperationEval instance = new MultiplyEval(); - private static final ValueEvalToNumericXlator NUM_XLATOR = - new ValueEvalToNumericXlator((short) - ( ValueEvalToNumericXlator.BOOL_IS_PARSED - | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - | ValueEvalToNumericXlator.REF_STRING_IS_PARSED - )); + private MultiplyEval() { + } - public MultiplyEval(Ptg ptg) { - delegate = (MultiplyPtg) ptg; - } - - protected ValueEvalToNumericXlator getXlator() { - return NUM_XLATOR; - } - - public Eval evaluate(Eval[] args, int srcRow, short srcCol) { - if(args.length != 2) { - return ErrorEval.VALUE_INVALID; - } - - double d0 = 0; - double d1 = 0; - ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - d0 = ((NumericValueEval) ve).getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - return ErrorEval.VALUE_INVALID; - } - - ve = singleOperandEvaluate(args[1], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - d1 = ((NumericValueEval) ve).getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - return ErrorEval.VALUE_INVALID; - } - - if (Double.isNaN(d0) || Double.isNaN(d1)) { - return ErrorEval.NUM_ERROR; - } - return new NumberEval(d0 * d1); - } - - public int getNumberOfOperands() { - return delegate.getNumberOfOperands(); - } - - public int getType() { - return delegate.getType(); - } + protected double evaluate(double d0, double d1) { + return d0 * d1; + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/NameEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/NameEval.java index 682394b3c..1f77deec2 100755 --- a/src/java/org/apache/poi/hssf/record/formula/eval/NameEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/NameEval.java @@ -22,26 +22,24 @@ package org.apache.poi.hssf.record.formula.eval; */ public final class NameEval implements Eval { - private final int _index; + private final String _functionName; /** - * @param index zero based index to a defined name record + * Creates a NameEval representing a function name */ - public NameEval(int index) { - _index = index; + public NameEval(String functionName) { + _functionName = functionName; } - /** - * @return zero based index to a defined name record - */ - public int getIndex() { - return _index; + + public String getFunctionName() { + return _functionName; } public String toString() { StringBuffer sb = new StringBuffer(64); sb.append(getClass().getName()).append(" ["); - sb.append(_index); + sb.append(_functionName); sb.append("]"); return sb.toString(); } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/NotEqualEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/NotEqualEval.java index c5388f520..da8f31549 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/NotEqualEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/NotEqualEval.java @@ -1,68 +1,34 @@ -/* -* Licensed to the Apache Software Foundation (ASF) under one or more -* contributor license agreements. See the NOTICE file distributed with -* this work for additional information regarding copyright ownership. -* The ASF licenses this file to You under the Apache License, Version 2.0 -* (the "License"); you may not use this file except in compliance with -* the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ -/* - * Created on May 8, 2005 - * - */ -package org.apache.poi.hssf.record.formula.eval; +/* ==================================================================== + 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 -import org.apache.poi.hssf.record.formula.NotEqualPtg; -import org.apache.poi.hssf.record.formula.Ptg; + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hssf.record.formula.eval; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > * */ -public class NotEqualEval extends RelationalOperationEval { +public final class NotEqualEval extends RelationalOperationEval { - private NotEqualPtg delegate; - - public NotEqualEval(Ptg ptg) { - this.delegate = (NotEqualPtg) ptg; - } - - - public Eval evaluate(Eval[] operands, int srcRow, short srcCol) { - ValueEval retval = null; - - RelationalValues rvs = super.doEvaluate(operands, srcRow, srcCol); - retval = rvs.ee; - int result = 0; - if (retval == null) { - result = doComparison(rvs.bs); - if (result == 0) { - result = doComparison(rvs.ss); - } - if (result == 0) { - result = doComparison(rvs.ds); - } - - retval = (result != 0) ? BoolEval.TRUE : BoolEval.FALSE; - } - - return retval; - } - - public int getNumberOfOperands() { - return delegate.getNumberOfOperands(); - } - - public int getType() { - return delegate.getType(); - } + public static final OperationEval instance = new NotEqualEval(); + + private NotEqualEval() { + } + protected boolean convertComparisonResult(int cmpResult) { + return cmpResult != 0; + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/NumericOperationEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/NumericOperationEval.java deleted file mode 100644 index 6bc1c95dc..000000000 --- a/src/java/org/apache/poi/hssf/record/formula/eval/NumericOperationEval.java +++ /dev/null @@ -1,66 +0,0 @@ -/* -* Licensed to the Apache Software Foundation (ASF) under one or more -* contributor license agreements. See the NOTICE file distributed with -* this work for additional information regarding copyright ownership. -* The ASF licenses this file to You under the Apache License, Version 2.0 -* (the "License"); you may not use this file except in compliance with -* the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ -/* - * Created on May 14, 2005 - * - */ -package org.apache.poi.hssf.record.formula.eval; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public abstract class NumericOperationEval implements OperationEval { - - protected abstract ValueEvalToNumericXlator getXlator(); - - protected ValueEval singleOperandEvaluate(Eval eval, int srcRow, short srcCol) { - ValueEval retval; - if (eval instanceof AreaEval) { - AreaEval ae = (AreaEval) eval; - if (ae.contains(srcRow, srcCol)) { // circular ref! - retval = ErrorEval.CIRCULAR_REF_ERROR; - } - else if (ae.isRow()) { - if (ae.containsColumn(srcCol)) { - ValueEval ve = ae.getValueAt(ae.getFirstRow(), srcCol); - ve = getXlator().attemptXlateToNumeric(ve); - retval = getXlator().attemptXlateToNumeric(ve); - } - else { - retval = ErrorEval.VALUE_INVALID; - } - } - else if (ae.isColumn()) { - if (ae.containsRow(srcRow)) { - ValueEval ve = ae.getValueAt(srcRow, ae.getFirstColumn()); - retval = getXlator().attemptXlateToNumeric(ve); - } - else { - retval = ErrorEval.VALUE_INVALID; - } - } - else { - retval = ErrorEval.VALUE_INVALID; - } - } - else { - retval = getXlator().attemptXlateToNumeric((ValueEval) eval); - } - return retval; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/PercentEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/PercentEval.java index c698a4e50..d03e44745 100755 --- a/src/java/org/apache/poi/hssf/record/formula/eval/PercentEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/PercentEval.java @@ -17,55 +17,40 @@ package org.apache.poi.hssf.record.formula.eval; -import org.apache.poi.hssf.record.formula.PercentPtg; -import org.apache.poi.hssf.record.formula.Ptg; /** * Implementation of Excel formula token '%'.

    * @author Josh Micich */ -public final class PercentEval extends NumericOperationEval { +public final class PercentEval implements OperationEval { - private PercentPtg _delegate; + public static final OperationEval instance = new PercentEval(); - private static final ValueEvalToNumericXlator NUM_XLATOR = new ValueEvalToNumericXlator( - (short) (ValueEvalToNumericXlator.BOOL_IS_PARSED - | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED | ValueEvalToNumericXlator.REF_STRING_IS_PARSED)); - - public PercentEval(Ptg ptg) { - _delegate = (PercentPtg) ptg; - } - - protected ValueEvalToNumericXlator getXlator() { - return NUM_XLATOR; + private PercentEval() { } public Eval evaluate(Eval[] args, int srcRow, short srcCol) { if (args.length != 1) { return ErrorEval.VALUE_INVALID; } - - ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - double d0 = ((NumericValueEval) ve).getNumberValue(); - return new NumberEval(d0 / 100); + double d0; + try { + ValueEval ve = OperandResolver.getSingleValue(args[0], srcRow, srcCol); + if (ve instanceof BlankEval) { + return NumberEval.ZERO; + } + d0 = OperandResolver.coerceValueToDouble(ve); + } catch (EvaluationException e) { + return e.getErrorEval(); } - - if (ve instanceof BlankEval) { - return NumberEval.ZERO; - } - if (ve instanceof ErrorEval) { - return ve; - } - return ErrorEval.VALUE_INVALID; + return new NumberEval(d0 / 100); } public int getNumberOfOperands() { - return _delegate.getNumberOfOperands(); - } - - public int getType() { - return _delegate.getType(); + return 1; } + public final int getType() { + // TODO - remove + throw new RuntimeException("obsolete code should not be called"); + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/PowerEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/PowerEval.java index 651c5d2aa..685332a05 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/PowerEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/PowerEval.java @@ -1,90 +1,33 @@ -/* -* Licensed to the Apache Software Foundation (ASF) under one or more -* contributor license agreements. See the NOTICE file distributed with -* this work for additional information regarding copyright ownership. -* The ASF licenses this file to You under the Apache License, Version 2.0 -* (the "License"); you may not use this file except in compliance with -* the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ package org.apache.poi.hssf.record.formula.eval; -import org.apache.poi.hssf.record.formula.Ptg; -import org.apache.poi.hssf.record.formula.PowerPtg; - /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * */ -public final class PowerEval extends NumericOperationEval { +public final class PowerEval extends TwoOperandNumericOperation { - private PowerPtg delegate; + public static final OperationEval instance = new PowerEval(); - private static final ValueEvalToNumericXlator NUM_XLATOR = - new ValueEvalToNumericXlator((short) - ( ValueEvalToNumericXlator.BOOL_IS_PARSED - | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - | ValueEvalToNumericXlator.REF_STRING_IS_PARSED - )); + private PowerEval() { + } - public PowerEval(Ptg ptg) { - delegate = (PowerPtg) ptg; - } - - protected ValueEvalToNumericXlator getXlator() { - return NUM_XLATOR; - } - - public Eval evaluate(Eval[] args, int srcRow, short srcCol) { - if(args.length != 2) { - return ErrorEval.VALUE_INVALID; - } - double d0 = 0; - double d1 = 0; - - ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - d0 = ((NumericValueEval) ve).getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - return ErrorEval.VALUE_INVALID; - } - - ve = singleOperandEvaluate(args[1], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - d1 = ((NumericValueEval) ve).getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - return ErrorEval.VALUE_INVALID; - } - - double p = Math.pow(d0, d1); - if (Double.isNaN(p)) { - return ErrorEval.VALUE_INVALID; - } - return new NumberEval(p); - } - - public int getNumberOfOperands() { - return delegate.getNumberOfOperands(); - } - - public int getType() { - return delegate.getType(); - } + protected double evaluate(double d0, double d1) { + return Math.pow(d0, d1); + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/RelationalOperationEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/RelationalOperationEval.java index 9b1a2ece4..058b00e1a 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/RelationalOperationEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/RelationalOperationEval.java @@ -1,216 +1,145 @@ -/* -* Licensed to the Apache Software Foundation (ASF) under one or more -* contributor license agreements. See the NOTICE file distributed with -* this work for additional information regarding copyright ownership. -* The ASF licenses this file to You under the Apache License, Version 2.0 -* (the "License"); you may not use this file except in compliance with -* the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ -/* - * Created on May 10, 2005 - * - */ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + package org.apache.poi.hssf.record.formula.eval; /** + * Base class for all comparison operator evaluators + * * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * */ public abstract class RelationalOperationEval implements OperationEval { - protected class RelationalValues { - public Double[] ds = new Double[2]; - public Boolean[] bs = new Boolean[2]; - public String[] ss = new String[3]; - public ErrorEval ee = null; - } + /** + * Converts a standard compare result (-1, 0, 1) to true or false + * according to subclass' comparison type. + */ + protected abstract boolean convertComparisonResult(int cmpResult); - - /* - * This is a description of how the relational operators apply in MS Excel. - * Use this as a guideline when testing/implementing the evaluate methods - * for the relational operators Evals. - * - * Bool > any number. ALWAYS - * Bool > any string. ALWAYS - * Bool.TRUE > Bool.FALSE - * - * String > any number. ALWAYS - * String > Blank. ALWAYS - * String are sorted dictionary wise - * - * Blank == 0 (numeric) - */ - public RelationalValues doEvaluate(Eval[] operands, int srcRow, short srcCol) { - RelationalValues retval = new RelationalValues(); - - switch (operands.length) { - default: - retval.ee = ErrorEval.VALUE_INVALID; - break; - case 2: - internalDoEvaluate(operands, srcRow, srcCol, retval, 0); - internalDoEvaluate(operands, srcRow, srcCol, retval, 1); - } // end switch - return retval; - } - - /** - * convenience method to avoid code duplication for multiple operands - * @param operands - * @param srcRow - * @param srcCol - * @param retval - * @param index - */ - private void internalDoEvaluate(Eval[] operands, int srcRow, short srcCol, RelationalValues retval, int index) { - if (operands[index] instanceof BoolEval) { - BoolEval be = (BoolEval) operands[index]; - retval.bs[index] = Boolean.valueOf(be.getBooleanValue()); - } - else if (operands[index] instanceof NumericValueEval) { - NumericValueEval ne = (NumericValueEval) operands[index]; - retval.ds[index] = new Double(ne.getNumberValue()); - } - else if (operands[index] instanceof StringValueEval) { - StringValueEval se = (StringValueEval) operands[index]; - retval.ss[index] = se.getStringValue(); - } - else if (operands[index] instanceof RefEval) { - RefEval re = (RefEval) operands[index]; - ValueEval ve = re.getInnerValueEval(); - if (ve instanceof BoolEval) { - BoolEval be = (BoolEval) ve; - retval.bs[index] = Boolean.valueOf(be.getBooleanValue()); - } - else if (ve instanceof BlankEval) { - retval.ds[index] = new Double(0); - } - else if (ve instanceof NumericValueEval) { - NumericValueEval ne = (NumericValueEval) ve; - retval.ds[index] = new Double(ne.getNumberValue()); - } - else if (ve instanceof StringValueEval) { - StringValueEval se = (StringValueEval) ve; - retval.ss[index] = se.getStringValue(); - } - } - else if (operands[index] instanceof AreaEval) { - AreaEval ae = (AreaEval) operands[index]; - if (ae.isRow()) { - if (ae.containsColumn(srcCol)) { - ValueEval ve = ae.getValueAt(ae.getFirstRow(), srcCol); - if (ve instanceof BoolEval) { - BoolEval be = (BoolEval) ve; - retval.bs[index] = Boolean.valueOf(be.getBooleanValue()); - } - else if (ve instanceof BlankEval) { - retval.ds[index] = new Double(0); - } - else if (ve instanceof NumericValueEval) { - NumericValueEval ne = (NumericValueEval) ve; - retval.ds[index] = new Double(ne.getNumberValue()); - } - else if (ve instanceof StringValueEval) { - StringValueEval se = (StringValueEval) ve; - retval.ss[index] = se.getStringValue(); - } - else { - retval.ee = ErrorEval.VALUE_INVALID; - } - } - else { - retval.ee = ErrorEval.VALUE_INVALID; - } - } - else if (ae.isColumn()) { - if (ae.containsRow(srcRow)) { - ValueEval ve = ae.getValueAt(srcRow, ae.getFirstColumn()); - if (ve instanceof BoolEval) { - BoolEval be = (BoolEval) ve; - retval.bs[index] = Boolean.valueOf(be.getBooleanValue()); - } - else if (ve instanceof BlankEval) { - retval.ds[index] = new Double(0); - } - else if (ve instanceof NumericValueEval) { - NumericValueEval ne = (NumericValueEval) ve; - retval.ds[index] = new Double(ne.getNumberValue()); - } - else if (ve instanceof StringValueEval) { - StringValueEval se = (StringValueEval) ve; - retval.ss[index] = se.getStringValue(); - } - else { - retval.ee = ErrorEval.VALUE_INVALID; - } - } - else { - retval.ee = ErrorEval.VALUE_INVALID; - } - } - else { - retval.ee = ErrorEval.VALUE_INVALID; - } - } - } - - // if both null return 0, else non null wins, else TRUE wins - protected int doComparison(Boolean[] bs) { - int retval = 0; - if (bs[0] != null || bs[1] != null) { - retval = bs[0] != null - ? bs[1] != null - ? bs[0].booleanValue() - ? bs[1].booleanValue() - ? 0 - : 1 - : bs[1].booleanValue() - ? -1 - : 0 - : 1 - : bs[1] != null - ? -1 - : 0; - } - return retval; - } + /** + * This is a description of how the relational operators apply in MS Excel. + * Use this as a guideline when testing/implementing the evaluate methods + * for the relational operators Evals. + * + *

    +	 * Bool.TRUE > any number.
    +	 * Bool > any string. ALWAYS
    +	 * Bool.TRUE > Bool.FALSE
    +	 * Bool.FALSE == Blank
    +	 *
    +	 * Strings are never converted to numbers or booleans
    +	 * String > any number. ALWAYS
    +	 * Non-empty String > Blank
    +	 * Empty String == Blank
    +	 * String are sorted dictionary wise
    +	 *
    +	 * Blank > Negative numbers
    +	 * Blank == 0
    +	 * Blank < Positive numbers
    +	 * 
    + */ + public final Eval evaluate(Eval[] operands, int srcRow, short srcCol) { + if (operands.length != 2) { + return ErrorEval.VALUE_INVALID; + } - // if both null return 0, else non null wins, else string compare - protected int doComparison(String[] ss) { - int retval = 0; - if (ss[0] != null || ss[1] != null) { - retval = ss[0] != null - ? ss[1] != null - ? ss[0].compareTo(ss[1]) - : 1 - : ss[1] != null - ? -1 - : 0; - } - return retval; - } + ValueEval vA; + ValueEval vB; + try { + vA = OperandResolver.getSingleValue(operands[0], srcRow, srcCol); + vB = OperandResolver.getSingleValue(operands[1], srcRow, srcCol); + } catch (EvaluationException e) { + return e.getErrorEval(); + } + int cmpResult = doCompare(vA, vB); + boolean result = convertComparisonResult(cmpResult); + return BoolEval.valueOf(result); + } - // if both null return 0, else non null wins, else doublevalue compare - protected int doComparison(Double[] ds) { - int retval = 0; - if (ds[0] != null || ds[1] != null) { - retval = ds[0] != null - ? ds[1] != null - ? ds[0].compareTo(ds[1]) - : 1 - : ds[1] != null - ? -1 - : 0; - } - return retval; - } + private static int doCompare(ValueEval va, ValueEval vb) { + // special cases when one operand is blank + if (va == BlankEval.INSTANCE) { + return compareBlank(vb); + } + if (vb == BlankEval.INSTANCE) { + return -compareBlank(va); + } + + if (va instanceof BoolEval) { + if (vb instanceof BoolEval) { + BoolEval bA = (BoolEval) va; + BoolEval bB = (BoolEval) vb; + if (bA.getBooleanValue() == bB.getBooleanValue()) { + return 0; + } + return bA.getBooleanValue() ? 1 : -1; + } + return 1; + } + if (vb instanceof BoolEval) { + return -1; + } + if (va instanceof StringEval) { + if (vb instanceof StringEval) { + StringEval sA = (StringEval) va; + StringEval sB = (StringEval) vb; + return sA.getStringValue().compareTo(sB.getStringValue()); + } + return 1; + } + if (vb instanceof StringEval) { + return -1; + } + if (va instanceof NumberEval) { + if (vb instanceof NumberEval) { + NumberEval nA = (NumberEval) va; + NumberEval nB = (NumberEval) vb; + return Double.compare(nA.getNumberValue(), nB.getNumberValue()); + } + } + throw new IllegalArgumentException("Bad operand types (" + va.getClass().getName() + "), (" + + vb.getClass().getName() + ")"); + } + + private static int compareBlank(ValueEval v) { + if (v == BlankEval.INSTANCE) { + return 0; + } + if (v instanceof BoolEval) { + BoolEval boolEval = (BoolEval) v; + return boolEval.getBooleanValue() ? -1 : 0; + } + if (v instanceof NumberEval) { + NumberEval ne = (NumberEval) v; + return Double.compare(0, ne.getNumberValue()); + } + if (v instanceof StringEval) { + StringEval se = (StringEval) v; + return se.getStringValue().length() < 1 ? 0 : -1; + } + throw new IllegalArgumentException("bad value class (" + v.getClass().getName() + ")"); + } + + public final int getNumberOfOperands() { + return 2; + } + + public final int getType() { + // TODO - get rid of this method + throw new RuntimeException("Obsolete code - should not be called"); + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/SubtractEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/SubtractEval.java index 85a384529..cbd6dc5ee 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/SubtractEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/SubtractEval.java @@ -1,93 +1,33 @@ -/* -* Licensed to the Apache Software Foundation (ASF) under one or more -* contributor license agreements. See the NOTICE file distributed with -* this work for additional information regarding copyright ownership. -* The ASF licenses this file to You under the Apache License, Version 2.0 -* (the "License"); you may not use this file except in compliance with -* the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ package org.apache.poi.hssf.record.formula.eval; -import org.apache.poi.hssf.record.formula.Ptg; -import org.apache.poi.hssf.record.formula.SubtractPtg; - /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * */ -public final class SubtractEval extends NumericOperationEval { +public final class SubtractEval extends TwoOperandNumericOperation { - private SubtractPtg delegate; + public static final OperationEval instance = new SubtractEval(); - private static final ValueEvalToNumericXlator NUM_XLATOR = - new ValueEvalToNumericXlator((short) - ( ValueEvalToNumericXlator.BOOL_IS_PARSED - | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - | ValueEvalToNumericXlator.REF_STRING_IS_PARSED - )); + private SubtractEval() { + } - public SubtractEval(Ptg ptg) { - delegate = (SubtractPtg) ptg; - } - - protected ValueEvalToNumericXlator getXlator() { - return NUM_XLATOR; - } - - public Eval evaluate(Eval[] args, int srcRow, short srcCol) { - if(args.length != 2) { - return ErrorEval.VALUE_INVALID; - } - Eval retval = null; - double d0 = 0; - double d1 = 0; - ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - d0 = ((NumericValueEval) ve).getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - retval = ErrorEval.VALUE_INVALID; - } - - if (retval == null) { // no error yet - ve = singleOperandEvaluate(args[1], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - d1 = ((NumericValueEval) ve).getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - retval = ErrorEval.VALUE_INVALID; - } - } - - if (retval == null) { - retval = (Double.isNaN(d0) || Double.isNaN(d1)) - ? (ValueEval) ErrorEval.VALUE_INVALID - : new NumberEval(d0 - d1); - } - return retval; - } - - public int getNumberOfOperands() { - return delegate.getNumberOfOperands(); - } - - public int getType() { - return delegate.getType(); - } + protected double evaluate(double d0, double d1) { + return d0 - d1; + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/TwoOperandNumericOperation.java b/src/java/org/apache/poi/hssf/record/formula/eval/TwoOperandNumericOperation.java new file mode 100644 index 000000000..665ba4b46 --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/formula/eval/TwoOperandNumericOperation.java @@ -0,0 +1,55 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hssf.record.formula.eval; + +/** + * @author Josh Micich + */ +abstract class TwoOperandNumericOperation implements OperationEval { + + public final int getType() { + // TODO - remove + throw new RuntimeException("obsolete code should not be called"); + } + protected final double singleOperandEvaluate(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException { + ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol); + if (ve instanceof BlankEval) { + return 0.0; + } + return OperandResolver.coerceValueToDouble(ve); + } + + public final Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) { + double result; + try { + double d0 = singleOperandEvaluate(args[0], srcCellRow, srcCellCol); + double d1 = singleOperandEvaluate(args[1], srcCellRow, srcCellCol); + result = evaluate(d0, d1); + if (Double.isNaN(result) || Double.isInfinite(result)) { + return ErrorEval.NUM_ERROR; + } + } catch (EvaluationException e) { + return e.getErrorEval(); + } + return new NumberEval(result); + } + protected abstract double evaluate(double d0, double d1) throws EvaluationException; + public final int getNumberOfOperands() { + return 2; + } +} diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/UnaryMinusEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/UnaryMinusEval.java index ef6f533ea..8174429e0 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/UnaryMinusEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/UnaryMinusEval.java @@ -1,75 +1,56 @@ -/* -* 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; -import org.apache.poi.hssf.record.formula.Ptg; -import org.apache.poi.hssf.record.formula.UnaryMinusPtg; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > * */ -public final class UnaryMinusEval extends NumericOperationEval { +public final class UnaryMinusEval implements OperationEval { - private UnaryMinusPtg delegate; - private static final ValueEvalToNumericXlator NUM_XLATOR = - new ValueEvalToNumericXlator((short) - ( ValueEvalToNumericXlator.BOOL_IS_PARSED - | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - | ValueEvalToNumericXlator.REF_STRING_IS_PARSED - )); + public static final OperationEval instance = new UnaryMinusEval(); + private UnaryMinusEval() { + } - public UnaryMinusEval(Ptg ptg) { - this.delegate = (UnaryMinusPtg) ptg; + public Eval evaluate(Eval[] args, int srcRow, short srcCol) { + if (args.length != 1) { + return ErrorEval.VALUE_INVALID; + } + double d; + try { + ValueEval ve = OperandResolver.getSingleValue(args[0], srcRow, srcCol); + if (ve instanceof BlankEval) { + return NumberEval.ZERO; + } + d = OperandResolver.coerceValueToDouble(ve); + } catch (EvaluationException e) { + return e.getErrorEval(); + } + return new NumberEval(-d); + } + + public int getNumberOfOperands() { + return 1; + } + public final int getType() { + // TODO - remove + throw new RuntimeException("obsolete code should not be called"); } - - protected ValueEvalToNumericXlator getXlator() { - return NUM_XLATOR; - } - - public Eval evaluate(Eval[] args, int srcRow, short srcCol) { - if(args.length != 1) { - return ErrorEval.VALUE_INVALID; - } - double d = 0; - - ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - d = ((NumericValueEval) ve).getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else if (ve instanceof ErrorEval) { - return ve; - } - - return new NumberEval(-d); - } - - public int getNumberOfOperands() { - return delegate.getNumberOfOperands(); - } - - public int getType() { - return delegate.getType(); - } - } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/UnaryPlusEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/UnaryPlusEval.java index edcc7bee7..66c5f6801 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/UnaryPlusEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/UnaryPlusEval.java @@ -17,8 +17,6 @@ package org.apache.poi.hssf.record.formula.eval; -import org.apache.poi.hssf.record.formula.Ptg; -import org.apache.poi.hssf.record.formula.UnaryPlusPtg; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > @@ -26,13 +24,9 @@ import org.apache.poi.hssf.record.formula.UnaryPlusPtg; */ public final class UnaryPlusEval implements OperationEval { - private UnaryPlusPtg delegate; + public static final OperationEval instance = new UnaryPlusEval(); - /** - * called by reflection - */ - public UnaryPlusEval(Ptg ptg) { - this.delegate = (UnaryPlusPtg) ptg; + private UnaryPlusEval() { } public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) { @@ -59,10 +53,10 @@ public final class UnaryPlusEval implements OperationEval { } public int getNumberOfOperands() { - return delegate.getNumberOfOperands(); + return 1; } public int getType() { - return delegate.getType(); + throw new RuntimeException("obsolete code should not be called"); } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/ValueEvalToNumericXlator.java b/src/java/org/apache/poi/hssf/record/formula/eval/ValueEvalToNumericXlator.java index 1abcf34d2..46aa0ddf6 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/ValueEvalToNumericXlator.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/ValueEvalToNumericXlator.java @@ -55,49 +55,40 @@ public final class ValueEvalToNumericXlator { * @param eval */ public ValueEval attemptXlateToNumeric(ValueEval eval) { - ValueEval retval = null; if (eval == null) { - retval = BlankEval.INSTANCE; + throw new IllegalArgumentException("eval must not be null"); } // most common case - least worries :) - else if (eval instanceof NumberEval) { - retval = eval; + if (eval instanceof NumberEval) { + return eval; } - // booleval - else if (eval instanceof BoolEval) { - retval = ((flags & BOOL_IS_PARSED) > 0) + if (eval instanceof BoolEval) { + return ((flags & BOOL_IS_PARSED) > 0) ? (NumericValueEval) eval : xlateBlankEval(BLANK_IS_PARSED); } - // stringeval - else if (eval instanceof StringEval) { - retval = xlateStringEval((StringEval) eval); // TODO: recursive call needed + if (eval instanceof StringEval) { + return xlateStringEval((StringEval) eval); // TODO: recursive call needed } - // refeval - else if (eval instanceof RefEval) { - retval = xlateRefEval((RefEval) eval); + if (eval instanceof RefEval) { + return xlateRefEval((RefEval) eval); } - // erroreval - else if (eval instanceof ErrorEval) { - retval = eval; + if (eval instanceof ErrorEval) { + return eval; } - else if (eval instanceof BlankEval) { - retval = xlateBlankEval(BLANK_IS_PARSED); + if (eval instanceof BlankEval) { + return xlateBlankEval(BLANK_IS_PARSED); } // probably AreaEval? then not acceptable. - else { - throw new RuntimeException("Invalid ValueEval type passed for conversion: " + eval.getClass()); - } - - return retval; + throw new RuntimeException("Invalid ValueEval type passed for conversion: " + eval.getClass()); } /** diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Hlookup.java b/src/java/org/apache/poi/hssf/record/formula/functions/Hlookup.java index 8604eadc3..2bbf6f5cd 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Hlookup.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Hlookup.java @@ -60,8 +60,7 @@ public final class Hlookup implements Function { AreaEval tableArray = LookupUtils.resolveTableArrayArg(args[1]); boolean isRangeLookup = LookupUtils.resolveRangeLookupArg(arg3, srcCellRow, srcCellCol); int colIndex = LookupUtils.lookupIndexOfValue(lookupValue, LookupUtils.createRowVector(tableArray, 0), isRangeLookup); - ValueEval veColIndex = OperandResolver.getSingleValue(args[2], srcCellRow, srcCellCol); - int rowIndex = LookupUtils.resolveRowOrColIndexArg(veColIndex); + int rowIndex = LookupUtils.resolveRowOrColIndexArg(args[2], srcCellRow, srcCellCol); ValueVector resultCol = createResultColumnVector(tableArray, rowIndex); return resultCol.getItem(colIndex); } catch (EvaluationException e) { @@ -73,15 +72,14 @@ public final class Hlookup implements Function { /** * Returns one column from an AreaEval * - * @throws EvaluationException (#VALUE!) if colIndex is negative, (#REF!) if colIndex is too high + * @param rowIndex assumed to be non-negative + * + * @throws EvaluationException (#REF!) if colIndex is too high */ - private ValueVector createResultColumnVector(AreaEval tableArray, int colIndex) throws EvaluationException { - if(colIndex < 0) { - throw EvaluationException.invalidValue(); - } - if(colIndex >= tableArray.getWidth()) { + private ValueVector createResultColumnVector(AreaEval tableArray, int rowIndex) throws EvaluationException { + if(rowIndex >= tableArray.getHeight()) { throw EvaluationException.invalidRef(); } - return LookupUtils.createRowVector(tableArray, colIndex); + return LookupUtils.createRowVector(tableArray, rowIndex); } } 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 e6a3ec81c..ee67ef8ec 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 @@ -322,30 +322,45 @@ final class LookupUtils { * <blank> #VALUE! *
    * - * * Note - out of range errors (both too high and too low) are handled by the caller. - * @return column or row index as a zero-based value - * + * Note - out of range errors (result index too high) are handled by the caller. + * @return column or row index as a zero-based value, never negative. + * @throws EvaluationException when the specified arg cannot be coerced to a non-negative integer */ - public static int resolveRowOrColIndexArg(ValueEval veRowColIndexArg) throws EvaluationException { - if(veRowColIndexArg == null) { + public static int resolveRowOrColIndexArg(Eval rowColIndexArg, int srcCellRow, int srcCellCol) throws EvaluationException { + if(rowColIndexArg == null) { throw new IllegalArgumentException("argument must not be null"); } + + ValueEval veRowColIndexArg; + try { + veRowColIndexArg = OperandResolver.getSingleValue(rowColIndexArg, srcCellRow, (short)srcCellCol); + } catch (EvaluationException e) { + // All errors get translated to #REF! + throw EvaluationException.invalidRef(); + } + int oneBasedIndex; if(veRowColIndexArg instanceof BlankEval) { + oneBasedIndex = 0; + } else { + if(veRowColIndexArg instanceof StringEval) { + StringEval se = (StringEval) veRowColIndexArg; + String strVal = se.getStringValue(); + Double dVal = OperandResolver.parseDouble(strVal); + if(dVal == null) { + // String does not resolve to a number. Raise #REF! error. + throw EvaluationException.invalidRef(); + // This includes text booleans "TRUE" and "FALSE". They are not valid. + } + // else - numeric value parses OK + } + // actual BoolEval values get interpreted as FALSE->0 and TRUE->1 + oneBasedIndex = OperandResolver.coerceValueToInt(veRowColIndexArg); + } + if (oneBasedIndex < 1) { + // note this is asymmetric with the errors when the index is too large (#REF!) throw EvaluationException.invalidValue(); } - if(veRowColIndexArg instanceof StringEval) { - StringEval se = (StringEval) veRowColIndexArg; - String strVal = se.getStringValue(); - Double dVal = OperandResolver.parseDouble(strVal); - if(dVal == null) { - // String does not resolve to a number. Raise #VALUE! error. - throw EvaluationException.invalidRef(); - // This includes text booleans "TRUE" and "FALSE". They are not valid. - } - // else - numeric value parses OK - } - // actual BoolEval values get interpreted as FALSE->0 and TRUE->1 - return OperandResolver.coerceValueToInt(veRowColIndexArg) - 1; + return oneBasedIndex - 1; // convert to zero based } @@ -583,11 +598,13 @@ final class LookupUtils { return maxIx - 1; } - public static LookupValueComparer createLookupComparer(ValueEval lookupValue) throws EvaluationException { + public static LookupValueComparer createLookupComparer(ValueEval lookupValue) { - if (lookupValue instanceof BlankEval) { - // blank eval can never be found in a lookup array - throw new EvaluationException(ErrorEval.NA); + if (lookupValue == BlankEval.INSTANCE) { + // blank eval translates to zero + // Note - a blank eval in the lookup column/row never matches anything + // empty string in the lookup column/row can only be matched by explicit emtpty string + return new NumberLookupComparer(NumberEval.ZERO); } if (lookupValue instanceof StringEval) { return new StringLookupComparer((StringEval) lookupValue); diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Vlookup.java b/src/java/org/apache/poi/hssf/record/formula/functions/Vlookup.java index 54f7d465e..28923c0f3 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Vlookup.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Vlookup.java @@ -60,8 +60,7 @@ public final class Vlookup implements Function { AreaEval tableArray = LookupUtils.resolveTableArrayArg(args[1]); boolean isRangeLookup = LookupUtils.resolveRangeLookupArg(arg3, srcCellRow, srcCellCol); int rowIndex = LookupUtils.lookupIndexOfValue(lookupValue, LookupUtils.createColumnVector(tableArray, 0), isRangeLookup); - ValueEval veColIndex = OperandResolver.getSingleValue(args[2], srcCellRow, srcCellCol); - int colIndex = LookupUtils.resolveRowOrColIndexArg(veColIndex); + int colIndex = LookupUtils.resolveRowOrColIndexArg(args[2], srcCellRow, srcCellCol); ValueVector resultCol = createResultColumnVector(tableArray, colIndex); return resultCol.getItem(rowIndex); } catch (EvaluationException e) { @@ -73,12 +72,11 @@ public final class Vlookup implements Function { /** * Returns one column from an AreaEval * - * @throws EvaluationException (#VALUE!) if colIndex is negative, (#REF!) if colIndex is too high + * @param colIndex assumed to be non-negative + * + * @throws EvaluationException (#REF!) if colIndex is too high */ private ValueVector createResultColumnVector(AreaEval tableArray, int colIndex) throws EvaluationException { - if(colIndex < 0) { - throw EvaluationException.invalidValue(); - } if(colIndex >= tableArray.getWidth()) { throw EvaluationException.invalidRef(); } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFName.java b/src/java/org/apache/poi/hssf/usermodel/HSSFName.java index 8d7c0ada8..4fde9e6cc 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFName.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFName.java @@ -23,15 +23,15 @@ import org.apache.poi.hssf.util.RangeAddress; import org.apache.poi.ss.usermodel.Name; /** - * High Level Representation of a 'defined name' which could be a 'built-in' name, + * High Level Representation of a 'defined name' which could be a 'built-in' name, * 'named range' or name of a user defined function. - * + * * @author Libin Roman (Vista Portal LDT. Developer) */ public class HSSFName implements Name { private HSSFWorkbook _book; private NameRecord _definedNameRec; - + /** Creates new HSSFName - called by HSSFWorkbook to create a sheet from * scratch. * @@ -43,58 +43,57 @@ public class HSSFName implements Name { _book = book; _definedNameRec = name; } - + /** Get the sheets name which this named range is referenced to * @return sheet name, which this named range referred to - */ + */ public String getSheetName() { short indexToExternSheet = _definedNameRec.getExternSheetNumber(); - + return _book.getWorkbook().findSheetNameFromExternSheet(indexToExternSheet); } - - /** + + /** * @return text name of this defined name - */ + */ public String getNameName(){ return _definedNameRec.getNameText(); } - - /** + + /** * sets the name of the named range * @param nameName named range name to set - */ + */ public void setNameName(String nameName){ _definedNameRec.setNameText(nameName); - _definedNameRec.setNameTextLength((byte)nameName.length()); Workbook wb = _book.getWorkbook(); - + //Check to ensure no other names have the same case-insensitive name for ( int i = wb.getNumNames()-1; i >=0; i-- ) { - NameRecord rec = wb.getNameRecord(i); - if (rec != _definedNameRec) { - if (rec.getNameText().equalsIgnoreCase(getNameName())) - throw new IllegalArgumentException("The workbook already contains this name (case-insensitive)"); - } + NameRecord rec = wb.getNameRecord(i); + if (rec != _definedNameRec) { + if (rec.getNameText().equalsIgnoreCase(getNameName())) + throw new IllegalArgumentException("The workbook already contains this name (case-insensitive)"); + } } } - /** + /** * Note - this method only applies to named ranges * @return the formula text defining the named range - */ + */ public String getReference() { - if (_definedNameRec.isFunctionName()) { - throw new IllegalStateException("Only applicable to named ranges"); - } + if (_definedNameRec.isFunctionName()) { + throw new IllegalStateException("Only applicable to named ranges"); + } return _definedNameRec.getAreaReference(_book); } - /** + /** * sets the sheet name which this named range referenced to * @param sheetName the sheet name of the reference - */ + */ private void setSheetName(String sheetName){ int sheetNumber = _book.getSheetIndex(sheetName); short externSheetNumber = (short) @@ -102,11 +101,11 @@ public class HSSFName implements Name { _definedNameRec.setExternSheetNumber(externSheetNumber); } - - /** + + /** * sets the reference of this named range * @param ref the reference to set - */ + */ public void setReference(String ref){ RangeAddress ra = new RangeAddress(ref); @@ -117,7 +116,7 @@ public class HSSFName implements Name { setSheetName(sheetName); } - //allow the poi utilities to parse it out + //allow the poi utilities to parse it out _definedNameRec.setAreaReference(ref); } @@ -131,7 +130,7 @@ public class HSSFName implements Name { return "#REF!".endsWith(ref); } public boolean isFunctionName() { - return _definedNameRec.isFunctionName(); + return _definedNameRec.isFunctionName(); } public String toString() { StringBuffer sb = new StringBuffer(64); diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java index 801b29878..dd6d5b383 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java @@ -27,7 +27,6 @@ import java.util.ArrayList; import java.util.Hashtable; import java.util.Iterator; import java.util.List; -import java.util.Stack; import org.apache.poi.POIDocument; import org.apache.poi.ddf.EscherBSERecord; @@ -36,6 +35,7 @@ import org.apache.poi.ddf.EscherBlipRecord; import org.apache.poi.ddf.EscherRecord; import org.apache.poi.hssf.model.Sheet; import org.apache.poi.hssf.model.Workbook; +import org.apache.poi.hssf.model.DrawingManager2; import org.apache.poi.hssf.record.AbstractEscherHolderRecord; import org.apache.poi.hssf.record.BackupRecord; import org.apache.poi.hssf.record.DrawingGroupRecord; @@ -55,6 +55,8 @@ import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor; import org.apache.poi.hssf.record.formula.Area3DPtg; import org.apache.poi.hssf.record.formula.MemFuncPtg; import org.apache.poi.hssf.record.formula.NameXPtg; +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.util.CellReference; import org.apache.poi.hssf.util.SheetReferences; @@ -667,33 +669,81 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm * @return HSSFSheet representing the cloned sheet. */ - public HSSFSheet cloneSheet(int sheetNum) { - validateSheetIndex(sheetNum); - HSSFSheet srcSheet = (HSSFSheet) _sheets.get(sheetNum); - String srcName = workbook.getSheetName(sheetNum); + public HSSFSheet cloneSheet(int sheetIndex) { + validateSheetIndex(sheetIndex); + HSSFSheet srcSheet = (HSSFSheet) _sheets.get(sheetIndex); + String srcName = workbook.getSheetName(sheetIndex); HSSFSheet clonedSheet = srcSheet.cloneSheet(this); clonedSheet.setSelected(false); clonedSheet.setActive(false); + String name = getUniqueSheetName(srcName); + int newSheetIndex = _sheets.size(); _sheets.add(clonedSheet); - int i = 1; + workbook.setSheetName(newSheetIndex, name); + + // Check this sheet has an autofilter, (which has a built-in NameRecord at workbook level) + int filterDbNameIndex = findExistingBuiltinNameRecordIdx(sheetIndex, NameRecord.BUILTIN_FILTER_DB); + if (filterDbNameIndex >=0) { + NameRecord origNameRecord = workbook.getNameRecord(filterDbNameIndex); + // copy original formula but adjust 3D refs to the new external sheet index + int newExtSheetIx = getExternalSheetIndex(newSheetIndex); + Ptg[] ptgs = origNameRecord.getNameDefinition(); + for (int i=0; i< ptgs.length; i++) { + Ptg ptg = ptgs[i]; + ptg = ptg.copy(); + + if (ptg instanceof Area3DPtg) { + Area3DPtg a3p = (Area3DPtg) ptg; + a3p.setExternSheetIndex(newExtSheetIx); + } else if (ptg instanceof Ref3DPtg) { + Ref3DPtg r3p = (Ref3DPtg) ptg; + r3p.setExternSheetIndex(newExtSheetIx); + } + ptgs[i] = ptg; + } + NameRecord newNameRecord = workbook.createBuiltInName(NameRecord.BUILTIN_FILTER_DB, newSheetIndex+1); + newNameRecord.setNameDefinition(ptgs); + newNameRecord.setHidden(true); + HSSFName newName = new HSSFName(this, newNameRecord); + names.add(newName); + + workbook.cloneDrawings(clonedSheet.getSheet()); + } + // TODO - maybe same logic required for other/all built-in name records + + return clonedSheet; + } + + private String getUniqueSheetName(String srcName) { + int uniqueIndex = 2; + String baseName = srcName; + int bracketPos = srcName.lastIndexOf('('); + if (bracketPos > 0 && srcName.endsWith(")")) { + String suffix = srcName.substring(bracketPos + 1, srcName.length() - ")".length()); + try { + uniqueIndex = Integer.parseInt(suffix.trim()); + uniqueIndex++; + baseName=srcName.substring(0, bracketPos).trim(); + } catch (NumberFormatException e) { + // contents of brackets not numeric + } + } while (true) { // Try and find the next sheet name that is unique - String name = srcName; - String index = Integer.toString(i++); - if (name.length() + index.length() + 2 < 31) { - name = name + "(" + index + ")"; + String index = Integer.toString(uniqueIndex++); + String name; + if (baseName.length() + index.length() + 2 < 31) { + name = baseName + " (" + index + ")"; } else { - name = name.substring(0, 31 - index.length() - 2) + "(" + index + ")"; + name = baseName.substring(0, 31 - index.length() - 2) + "(" + index + ")"; } //If the sheet name is unique, then set it otherwise move on to the next number. if (workbook.getSheetIndex(name) == -1) { - workbook.setSheetName(_sheets.size()-1, name); - break; + return name; } } - return clonedSheet; } /** @@ -907,7 +957,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm boolean removingRange = startColumn == -1 && endColumn == -1 && startRow == -1 && endRow == -1; - int rowColHeaderNameIndex = findExistingRowColHeaderNameRecordIdx(sheetIndex); + int rowColHeaderNameIndex = findExistingBuiltinNameRecordIdx(sheetIndex, NameRecord.BUILTIN_PRINT_TITLE); if (removingRange) { if (rowColHeaderNameIndex >= 0) { workbook.removeName(rowColHeaderNameIndex); @@ -925,29 +975,27 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm isNewRecord = false; } - short definitionTextLength = settingRowAndColumn ? (short)0x001a : (short)0x000b; - nameRecord.setDefinitionTextLength(definitionTextLength); // TODO - remove - - Stack ptgs = new Stack(); + List temp = new ArrayList(); if (settingRowAndColumn) { final int exprsSize = 2 * 11 + 1; // 2 * Area3DPtg.SIZE + UnionPtg.SIZE - ptgs.add(new MemFuncPtg(exprsSize)); + temp.add(new MemFuncPtg(exprsSize)); } if (startColumn >= 0) { Area3DPtg colArea = new Area3DPtg(0, MAX_ROW, startColumn, endColumn, false, false, false, false, externSheetIndex); - ptgs.add(colArea); + temp.add(colArea); } if (startRow >= 0) { Area3DPtg rowArea = new Area3DPtg(startRow, endRow, 0, MAX_COLUMN, false, false, false, false, externSheetIndex); - ptgs.add(rowArea); + temp.add(rowArea); } - if (settingRowAndColumn) - { - ptgs.add(UnionPtg.instance); + if (settingRowAndColumn) { + temp.add(UnionPtg.instance); } + Ptg[] ptgs = new Ptg[temp.size()]; + temp.toArray(ptgs); nameRecord.setNameDefinition(ptgs); if (isNewRecord) @@ -963,13 +1011,13 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm } - private int findExistingRowColHeaderNameRecordIdx(int sheetIndex) { + private int findExistingBuiltinNameRecordIdx(int sheetIndex, byte builtinCode) { for(int defNameIndex =0; defNameIndexOperationEval instances to help evaluate OperationPtg + * formula tokens. + * + * @author Josh Micich + */ +final class OperationEvaluatorFactory { + private static final Class[] OPERATION_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class }; + // TODO - use singleton instances directly instead of reflection + private static final Map _constructorsByPtgClass = initialiseConstructorsMap(); + private static final Map _instancesByPtgClass = initialiseInstancesMap(); + + private OperationEvaluatorFactory() { + // no instances of this class + } + + private static Map initialiseConstructorsMap() { + Map m = new HashMap(32); + add(m, ConcatPtg.class, ConcatEval.class); + add(m, FuncPtg.class, FuncVarEval.class); + add(m, FuncVarPtg.class, FuncVarEval.class); + return m; + } + private static Map initialiseInstancesMap() { + Map m = new HashMap(32); + add(m, EqualPtg.class, EqualEval.instance); + add(m, GreaterEqualPtg.class, GreaterEqualEval.instance); + add(m, GreaterThanPtg.class, GreaterThanEval.instance); + add(m, LessEqualPtg.class, LessEqualEval.instance); + add(m, LessThanPtg.class, LessThanEval.instance); + add(m, NotEqualPtg.class, NotEqualEval.instance); + + add(m, AddPtg.class, AddEval.instance); + add(m, DividePtg.class, DivideEval.instance); + add(m, MultiplyPtg.class, MultiplyEval.instance); + add(m, PercentPtg.class, PercentEval.instance); + add(m, PowerPtg.class, PowerEval.instance); + add(m, SubtractPtg.class, SubtractEval.instance); + add(m, UnaryMinusPtg.class, UnaryMinusEval.instance); + add(m, UnaryPlusPtg.class, UnaryPlusEval.instance); + return m; + } + + private static void add(Map m, Class ptgClass, OperationEval evalInstance) { + if(!Ptg.class.isAssignableFrom(ptgClass)) { + throw new IllegalArgumentException("Expected Ptg subclass"); + } + m.put(ptgClass, evalInstance); + } + + private static void add(Map m, Class ptgClass, Class evalClass) { + // perform some validation now, to keep later exception handlers simple + if(!Ptg.class.isAssignableFrom(ptgClass)) { + throw new IllegalArgumentException("Expected Ptg subclass"); + } + + if(!OperationEval.class.isAssignableFrom(evalClass)) { + throw new IllegalArgumentException("Expected OperationEval subclass"); + } + if (!Modifier.isPublic(evalClass.getModifiers())) { + throw new RuntimeException("Eval class must be public"); + } + if (Modifier.isAbstract(evalClass.getModifiers())) { + throw new RuntimeException("Eval class must not be abstract"); + } + + Constructor constructor; + try { + constructor = evalClass.getDeclaredConstructor(OPERATION_CONSTRUCTOR_CLASS_ARRAY); + } catch (NoSuchMethodException e) { + throw new RuntimeException("Missing constructor"); + } + if (!Modifier.isPublic(constructor.getModifiers())) { + throw new RuntimeException("Eval constructor must be public"); + } + m.put(ptgClass, constructor); + } + + /** + * returns the OperationEval concrete impl instance corresponding + * to the supplied operationPtg + */ + public static OperationEval create(OperationPtg ptg) { + if(ptg == null) { + throw new IllegalArgumentException("ptg must not be null"); + } + Object result; + + Class ptgClass = ptg.getClass(); + + result = _instancesByPtgClass.get(ptgClass); + if (result != null) { + return (OperationEval) result; + } + + + Constructor constructor = (Constructor) _constructorsByPtgClass.get(ptgClass); + if(constructor == null) { + if(ptgClass == ExpPtg.class) { + // ExpPtg is used for array formulas and shared formulas. + // it is currently unsupported, and may not even get implemented here + throw new RuntimeException("ExpPtg currently not supported"); + } + throw new RuntimeException("Unexpected operation ptg class (" + ptgClass.getName() + ")"); + } + + Object[] initargs = { ptg }; + try { + result = constructor.newInstance(initargs); + } catch (IllegalArgumentException e) { + throw new RuntimeException(e); + } catch (InstantiationException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + return (OperationEval) result; + } +} diff --git a/src/java/org/apache/poi/ss/usermodel/EvaluationCache.java b/src/java/org/apache/poi/ss/usermodel/EvaluationCache.java new file mode 100644 index 000000000..75eda02fb --- /dev/null +++ b/src/java/org/apache/poi/ss/usermodel/EvaluationCache.java @@ -0,0 +1,95 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.ss.usermodel; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.poi.hssf.record.formula.eval.ValueEval; + +/** + * Performance optimisation for {@link HSSFFormulaEvaluator}. This class stores previously + * calculated values of already visited cells, to avoid unnecessary re-calculation when the + * same cells are referenced multiple times + * + * + * @author Josh Micich + */ +final class EvaluationCache { + private static final class Key { + + private final int _sheetIndex; + private final int _srcRowNum; + private final int _srcColNum; + private final int _hashCode; + + public Key(int sheetIndex, int srcRowNum, int srcColNum) { + _sheetIndex = sheetIndex; + _srcRowNum = srcRowNum; + _srcColNum = srcColNum; + _hashCode = sheetIndex + srcRowNum + srcColNum; + } + + public int hashCode() { + return _hashCode; + } + + public boolean equals(Object obj) { + Key other = (Key) obj; + if (_hashCode != other._hashCode) { + return false; + } + if (_sheetIndex != other._sheetIndex) { + return false; + } + if (_srcRowNum != other._srcRowNum) { + return false; + } + if (_srcColNum != other._srcColNum) { + return false; + } + return true; + } + } + + private final Map _valuesByKey; + + /* package */EvaluationCache() { + _valuesByKey = new HashMap(); + } + + public ValueEval getValue(int sheetIndex, int srcRowNum, int srcColNum) { + Key key = new Key(sheetIndex, srcRowNum, srcColNum); + return (ValueEval) _valuesByKey.get(key); + } + + public void setValue(int sheetIndex, int srcRowNum, int srcColNum, ValueEval value) { + Key key = new Key(sheetIndex, srcRowNum, srcColNum); + if (_valuesByKey.containsKey(key)) { + throw new RuntimeException("Already have cached value for this cell"); + } + _valuesByKey.put(key, value); + } + + /** + * Should be called whenever there are changes to input cells in the evaluated workbook. + */ + public void clear() { + _valuesByKey.clear(); + } +} diff --git a/src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java b/src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java index aa71c68c5..30e78c33e 100644 --- a/src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java +++ b/src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java @@ -20,7 +20,11 @@ package org.apache.poi.ss.usermodel; import java.util.Iterator; import java.util.Stack; +import org.apache.poi.ss.util.AreaReference; +import org.apache.poi.ss.util.CellReference; + import org.apache.poi.hssf.model.FormulaParser; +import org.apache.poi.hssf.record.NameRecord; import org.apache.poi.hssf.record.formula.Area3DPtg; import org.apache.poi.hssf.record.formula.AreaPtg; import org.apache.poi.hssf.record.formula.BoolPtg; @@ -56,17 +60,64 @@ import org.apache.poi.hssf.record.formula.eval.StringEval; import org.apache.poi.hssf.record.formula.eval.ValueEval; /** + * Evaluates formula cells.

    + * + * For performance reasons, this class keeps a cache of all previously calculated intermediate + * cell values. Be sure to call {@link #clearCache()} if any workbook cells are changed between + * calls to evaluate~ methods on this class. + * * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * + * @author Josh Micich */ public class FormulaEvaluator { + /** + * used to track the number of evaluations + */ + private static final class Counter { + public int value; + public Counter() { + value = 0; + } + } protected Sheet _sheet; protected Workbook _workbook; + private final EvaluationCache _cache; + + private Counter _evaluationCounter; + public FormulaEvaluator(Sheet sheet, Workbook workbook) { + this(sheet, workbook, new EvaluationCache(), new Counter()); + } + + private FormulaEvaluator(Sheet sheet, Workbook workbook, EvaluationCache cache, Counter evaluationCounter) { _sheet = sheet; _workbook = workbook; + _cache = cache; + _evaluationCounter = evaluationCounter; + } + + /** + * for debug use. Used in toString methods + */ + public String getSheetName(Sheet sheet) { + return _workbook.getSheetName(_workbook.getSheetIndex(sheet)); + } + /** + * for debug/test use + */ + public int getEvaluationCount() { + return _evaluationCounter.value; + } + + private static boolean isDebugLogEnabled() { + return false; + } + private static void logDebug(String s) { + if (isDebugLogEnabled()) { + System.out.println(s); + } } /** @@ -75,6 +126,18 @@ public class FormulaEvaluator { */ public void setCurrentRow(Row row) { // do nothing + if (false) { + row.getClass(); // suppress unused parameter compiler warning + } + } + + /** + * Should be called whenever there are changes to input cells in the evaluated workbook. + * Failure to call this method after changing cell values will cause incorrect behaviour + * of the evaluate~ methods of this class + */ + public void clearCache() { + _cache.clear(); } @@ -102,7 +165,7 @@ public class FormulaEvaluator { retval.setErrorValue(cell.getErrorCellValue()); break; case Cell.CELL_TYPE_FORMULA: - retval = getCellValueForEval(internalEvaluate(cell, _sheet, _workbook), _workbook.getCreationHelper()); + retval = getCellValueForEval(internalEvaluate(cell, _sheet), _workbook.getCreationHelper()); break; case Cell.CELL_TYPE_NUMERIC: retval = new CellValue(Cell.CELL_TYPE_NUMERIC, _workbook.getCreationHelper()); @@ -140,7 +203,7 @@ public class FormulaEvaluator { if (cell != null) { switch (cell.getCellType()) { case Cell.CELL_TYPE_FORMULA: - CellValue cv = getCellValueForEval(internalEvaluate(cell, _sheet, _workbook), _workbook.getCreationHelper()); + CellValue cv = getCellValueForEval(internalEvaluate(cell, _sheet), _workbook.getCreationHelper()); switch (cv.getCellType()) { case Cell.CELL_TYPE_BOOLEAN: cell.setCellValue(cv.getBooleanValue()); @@ -185,7 +248,7 @@ public class FormulaEvaluator { if (cell != null) { switch (cell.getCellType()) { case Cell.CELL_TYPE_FORMULA: - CellValue cv = getCellValueForEval(internalEvaluate(cell, _sheet, _workbook), _workbook.getCreationHelper()); + CellValue cv = getCellValueForEval(internalEvaluate(cell, _sheet), _workbook.getCreationHelper()); switch (cv.getCellType()) { case Cell.CELL_TYPE_BOOLEAN: cell.setCellType(Cell.CELL_TYPE_BOOLEAN); @@ -283,26 +346,40 @@ public class FormulaEvaluator { * else a runtime exception will be thrown somewhere inside the method. * (Hence this is a private method.) */ - private static ValueEval internalEvaluate(Cell srcCell, Sheet sheet, Workbook workbook) { + private ValueEval internalEvaluate(Cell srcCell, Sheet sheet) { int srcRowNum = srcCell.getRowIndex(); - short srcColNum = srcCell.getCellNum(); + int srcColNum = srcCell.getCellNum(); + ValueEval result; + + int sheetIndex = _workbook.getSheetIndex(sheet); + result = _cache.getValue(sheetIndex, srcRowNum, srcColNum); + if (result != null) { + return result; + } + _evaluationCounter.value++; EvaluationCycleDetector tracker = EvaluationCycleDetectorManager.getTracker(); - if(!tracker.startEvaluate(workbook, sheet, srcRowNum, srcColNum)) { + if(!tracker.startEvaluate(_workbook, sheet, srcRowNum, srcColNum)) { return ErrorEval.CIRCULAR_REF_ERROR; } try { - return evaluateCell(workbook, sheet, srcRowNum, srcColNum, srcCell.getCellFormula()); + result = evaluateCell(srcRowNum, (short)srcColNum, srcCell.getCellFormula()); } finally { - tracker.endEvaluate(workbook, sheet, srcRowNum, srcColNum); + tracker.endEvaluate(_workbook, sheet, srcRowNum, srcColNum); + _cache.setValue(sheetIndex, srcRowNum, srcColNum, result); } + if (isDebugLogEnabled()) { + String sheetName = _workbook.getSheetName(sheetIndex); + CellReference cr = new CellReference(srcRowNum, srcColNum); + logDebug("Evaluated " + sheetName + "!" + cr.formatAsString() + " to " + result.toString()); + } + return result; } - private static ValueEval evaluateCell(Workbook workbook, Sheet sheet, - int srcRowNum, short srcColNum, String cellFormulaText) { + private ValueEval evaluateCell(int srcRowNum, short srcColNum, String cellFormulaText) { - Ptg[] ptgs = FormulaParser.parse(cellFormulaText, workbook); + Ptg[] ptgs = FormulaParser.parse(cellFormulaText, _workbook); Stack stack = new Stack(); for (int i = 0, iSize = ptgs.length; i < iSize; i++) { @@ -314,19 +391,10 @@ public class FormulaEvaluator { continue; } if (ptg instanceof MemErrPtg) { continue; } - if (ptg instanceof MissingArgPtg) { continue; } - if (ptg instanceof NamePtg) { - // named ranges, macro functions - NamePtg namePtg = (NamePtg) ptg; - stack.push(new NameEval(namePtg.getIndex())); - continue; + if (ptg instanceof MissingArgPtg) { + // TODO - might need to push BlankEval or MissingArgEval + continue; } - if (ptg instanceof NameXPtg) { - NameXPtg nameXPtg = (NameXPtg) ptg; - stack.push(new NameXEval(nameXPtg.getSheetRefIndex(), nameXPtg.getNameIndex())); - continue; - } - if (ptg instanceof UnknownPtg) { continue; } Eval opResult; if (ptg instanceof OperationPtg) { OperationPtg optg = (OperationPtg) ptg; @@ -343,10 +411,15 @@ public class FormulaEvaluator { Eval p = (Eval) stack.pop(); ops[j] = p; } - opResult = invokeOperation(operation, ops, srcRowNum, srcColNum, workbook, sheet); + logDebug("invoke " + operation + " (nAgs=" + numops + ")"); + opResult = invokeOperation(operation, ops, srcRowNum, srcColNum, _workbook, _sheet); } else { - opResult = getEvalForPtg(ptg, sheet, workbook); + opResult = getEvalForPtg(ptg, _sheet); } + if (opResult == null) { + throw new RuntimeException("Evaluation result must not be null"); + } + logDebug("push " + opResult); stack.push(opResult); } @@ -403,28 +476,63 @@ public class FormulaEvaluator { return operation.evaluate(ops, srcRowNum, srcColNum); } + private Sheet getOtherSheet(int externSheetIndex) { + return _workbook.getSheetAt(_workbook.getSheetIndexFromExternSheetIndex(externSheetIndex)); + } + private FormulaEvaluator createEvaluatorForAnotherSheet(Sheet sheet) { + return new FormulaEvaluator(sheet, _workbook, _cache, _evaluationCounter); + } + /** * returns an appropriate Eval impl instance for the Ptg. The Ptg must be * one of: Area3DPtg, AreaPtg, ReferencePtg, Ref3DPtg, IntPtg, NumberPtg, * StringPtg, BoolPtg
    special Note: OperationPtg subtypes cannot be * passed here! */ - private static Eval getEvalForPtg(Ptg ptg, Sheet sheet, Workbook workbook) { + private Eval getEvalForPtg(Ptg ptg, Sheet sheet) { + if (ptg instanceof NamePtg) { + // named ranges, macro functions + NamePtg namePtg = (NamePtg) ptg; + int numberOfNames = _workbook.getNumberOfNames(); + int nameIndex = namePtg.getIndex(); + if(nameIndex < 0 || nameIndex >= numberOfNames) { + throw new RuntimeException("Bad name index (" + nameIndex + + "). Allowed range is (0.." + (numberOfNames-1) + ")"); + } + if(_workbook instanceof org.apache.poi.hssf.usermodel.HSSFWorkbook) { + org.apache.poi.hssf.usermodel.HSSFWorkbook hssfWb = + (org.apache.poi.hssf.usermodel.HSSFWorkbook)_workbook; + NameRecord nameRecord = hssfWb.getNameRecord(nameIndex); + if (nameRecord.isFunctionName()) { + return new NameEval(nameRecord.getNameText()); + } + if (nameRecord.hasFormula()) { + return evaluateNameFormula(nameRecord.getNameDefinition(), sheet); + } + throw new RuntimeException("Don't know how to evalate name '" + nameRecord.getNameText() + "'"); + } else { + throw new RuntimeException("Don't know how to evaluate name records for XSSF"); + } + } + if (ptg instanceof NameXPtg) { + NameXPtg nameXPtg = (NameXPtg) ptg; + return new NameXEval(nameXPtg.getSheetRefIndex(), nameXPtg.getNameIndex()); + } if (ptg instanceof RefPtg) { - return new LazyRefEval(((RefPtg) ptg), sheet, workbook); + return new LazyRefEval(((RefPtg) ptg), sheet, this); } if (ptg instanceof Ref3DPtg) { Ref3DPtg refPtg = (Ref3DPtg) ptg; - Sheet xsheet = workbook.getSheetAt(workbook.getSheetIndexFromExternSheetIndex(refPtg.getExternSheetIndex())); - return new LazyRefEval(refPtg, xsheet, workbook); + Sheet xsheet = getOtherSheet(refPtg.getExternSheetIndex()); + return new LazyRefEval(refPtg, xsheet, createEvaluatorForAnotherSheet(xsheet)); } if (ptg instanceof AreaPtg) { - return new LazyAreaEval(((AreaPtg) ptg), sheet, workbook); + return new LazyAreaEval(((AreaPtg) ptg), sheet, this); } if (ptg instanceof Area3DPtg) { Area3DPtg a3dp = (Area3DPtg) ptg; - Sheet xsheet = workbook.getSheetAt(workbook.getSheetIndexFromExternSheetIndex(a3dp.getExternSheetIndex())); - return new LazyAreaEval(a3dp, xsheet, workbook); + Sheet xsheet = getOtherSheet(a3dp.getExternSheetIndex()); + return new LazyAreaEval(a3dp, xsheet, createEvaluatorForAnotherSheet(xsheet)); } if (ptg instanceof IntPtg) { @@ -442,8 +550,19 @@ public class FormulaEvaluator { if (ptg instanceof ErrPtg) { return ErrorEval.valueOf(((ErrPtg) ptg).getErrorCode()); } + if (ptg instanceof UnknownPtg) { + // TODO - remove UnknownPtg + throw new RuntimeException("UnknownPtg not allowed"); + } throw new RuntimeException("Unexpected ptg class (" + ptg.getClass().getName() + ")"); } + private Eval evaluateNameFormula(Ptg[] ptgs, Sheet sheet) { + if (ptgs.length > 1) { + throw new RuntimeException("Complex name formulas not supported yet"); + } + return getEvalForPtg(ptgs[0], sheet); + } + /** * 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 @@ -453,7 +572,7 @@ public class FormulaEvaluator { * @param sheet * @param workbook */ - public static ValueEval getEvalForCell(Cell cell, Sheet sheet, Workbook workbook) { + public ValueEval getEvalForCell(Cell cell, Sheet sheet) { if (cell == null) { return BlankEval.INSTANCE; @@ -464,7 +583,7 @@ public class FormulaEvaluator { case Cell.CELL_TYPE_STRING: return new StringEval(cell.getRichStringCellValue().getString()); case Cell.CELL_TYPE_FORMULA: - return internalEvaluate(cell, sheet, workbook); + return internalEvaluate(cell, sheet); case Cell.CELL_TYPE_BOOLEAN: return BoolEval.valueOf(cell.getBooleanCellValue()); case Cell.CELL_TYPE_BLANK: diff --git a/src/java/org/apache/poi/ss/usermodel/OperationEvaluatorFactory.java b/src/java/org/apache/poi/ss/usermodel/OperationEvaluatorFactory.java index ed136e037..ca8a92312 100755 --- a/src/java/org/apache/poi/ss/usermodel/OperationEvaluatorFactory.java +++ b/src/java/org/apache/poi/ss/usermodel/OperationEvaluatorFactory.java @@ -69,8 +69,9 @@ import org.apache.poi.hssf.record.formula.eval.UnaryPlusEval; */ final class OperationEvaluatorFactory { private static final Class[] OPERATION_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class }; - + // TODO - use singleton instances directly instead of reflection private static final Map _constructorsByPtgClass = initialiseConstructorsMap(); + private static final Map _instancesByPtgClass = initialiseInstancesMap(); private OperationEvaluatorFactory() { // no instances of this class @@ -78,32 +79,44 @@ final class OperationEvaluatorFactory { private static Map initialiseConstructorsMap() { Map m = new HashMap(32); - add(m, AddPtg.class, AddEval.class); add(m, ConcatPtg.class, ConcatEval.class); - add(m, DividePtg.class, DivideEval.class); - add(m, EqualPtg.class, EqualEval.class); add(m, FuncPtg.class, FuncVarEval.class); add(m, FuncVarPtg.class, FuncVarEval.class); - add(m, GreaterEqualPtg.class, GreaterEqualEval.class); - add(m, GreaterThanPtg.class, GreaterThanEval.class); - add(m, LessEqualPtg.class, LessEqualEval.class); - add(m, LessThanPtg.class, LessThanEval.class); - add(m, MultiplyPtg.class, MultiplyEval.class); - add(m, NotEqualPtg.class, NotEqualEval.class); - add(m, PercentPtg.class, PercentEval.class); - add(m, PowerPtg.class, PowerEval.class); - add(m, SubtractPtg.class, SubtractEval.class); - add(m, UnaryMinusPtg.class, UnaryMinusEval.class); - add(m, UnaryPlusPtg.class, UnaryPlusEval.class); + return m; + } + private static Map initialiseInstancesMap() { + Map m = new HashMap(32); + add(m, EqualPtg.class, EqualEval.instance); + add(m, GreaterEqualPtg.class, GreaterEqualEval.instance); + add(m, GreaterThanPtg.class, GreaterThanEval.instance); + add(m, LessEqualPtg.class, LessEqualEval.instance); + add(m, LessThanPtg.class, LessThanEval.instance); + add(m, NotEqualPtg.class, NotEqualEval.instance); + + add(m, AddPtg.class, AddEval.instance); + add(m, DividePtg.class, DivideEval.instance); + add(m, MultiplyPtg.class, MultiplyEval.instance); + add(m, PercentPtg.class, PercentEval.instance); + add(m, PowerPtg.class, PowerEval.instance); + add(m, SubtractPtg.class, SubtractEval.instance); + add(m, UnaryMinusPtg.class, UnaryMinusEval.instance); + add(m, UnaryPlusPtg.class, UnaryPlusEval.instance); return m; } + private static void add(Map m, Class ptgClass, OperationEval evalInstance) { + if(!Ptg.class.isAssignableFrom(ptgClass)) { + throw new IllegalArgumentException("Expected Ptg subclass"); + } + m.put(ptgClass, evalInstance); + } + private static void add(Map m, Class ptgClass, Class evalClass) { - // perform some validation now, to keep later exception handlers simple if(!Ptg.class.isAssignableFrom(ptgClass)) { throw new IllegalArgumentException("Expected Ptg subclass"); } + if(!OperationEval.class.isAssignableFrom(evalClass)) { throw new IllegalArgumentException("Expected OperationEval subclass"); } @@ -125,7 +138,7 @@ final class OperationEvaluatorFactory { } m.put(ptgClass, constructor); } - + /** * returns the OperationEval concrete impl instance corresponding * to the supplied operationPtg @@ -134,9 +147,16 @@ final class OperationEvaluatorFactory { if(ptg == null) { throw new IllegalArgumentException("ptg must not be null"); } + Object result; Class ptgClass = ptg.getClass(); + result = _instancesByPtgClass.get(ptgClass); + if (result != null) { + return (OperationEval) result; + } + + Constructor constructor = (Constructor) _constructorsByPtgClass.get(ptgClass); if(constructor == null) { if(ptgClass == ExpPtg.class) { @@ -147,7 +167,6 @@ final class OperationEvaluatorFactory { throw new RuntimeException("Unexpected operation ptg class (" + ptgClass.getName() + ")"); } - Object result; Object[] initargs = { ptg }; try { result = constructor.newInstance(initargs); diff --git a/src/java/org/apache/poi/ss/util/AreaReference.java b/src/java/org/apache/poi/ss/util/AreaReference.java index b5e06b9af..ae2c9192f 100644 --- a/src/java/org/apache/poi/ss/util/AreaReference.java +++ b/src/java/org/apache/poi/ss/util/AreaReference.java @@ -46,29 +46,59 @@ public class AreaReference { } 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 { + String part0 = parts[0]; + if (parts.length == 1) { + // TODO - probably shouldn't initialize area ref when text is really a cell ref + // Need to fix some named range stuff to get rid of this + _firstCell = new CellReference(part0); + _lastCell = _firstCell; _isSingleCell = true; + return; } - } + if (parts.length != 2) { + throw new IllegalArgumentException("Bad area ref '" + reference + "'"); + } + + String part1 = parts[1]; + if (isPlainColumn(part0)) { + if (!isPlainColumn(part1)) { + throw new RuntimeException("Bad area ref '" + reference + "'"); + } + // Special handling for whole-column references + // Represented internally as x$1 to x$65536 + // which is the maximum range of rows + + boolean firstIsAbs = CellReference.isPartAbsolute(part0); + boolean lastIsAbs = CellReference.isPartAbsolute(part1); + + int col0 = CellReference.convertColStringToIndex(part0); + int col1 = CellReference.convertColStringToIndex(part1); + + _firstCell = new CellReference(0, col0, true, firstIsAbs); + _lastCell = new CellReference(0xFFFF, col1, true, lastIsAbs); + _isSingleCell = false; + // TODO - whole row refs + } else { + _firstCell = new CellReference(part0); + _lastCell = new CellReference(part1); + _isSingleCell = part0.equals(part1); + } + } + private boolean isPlainColumn(String refPart) { + for(int i=refPart.length()-1; i>=0; i--) { + int ch = refPart.charAt(i); + if (ch == '$' && i==0) { + continue; + } + if (ch < 'A' || ch > 'Z') { + return false; + } + } + return true; + } + /** * Creates an area ref from a pair of Cell References. */ diff --git a/src/java/org/apache/poi/ss/util/CellReference.java b/src/java/org/apache/poi/ss/util/CellReference.java index b46cd88cc..e5005db5d 100644 --- a/src/java/org/apache/poi/ss/util/CellReference.java +++ b/src/java/org/apache/poi/ss/util/CellReference.java @@ -80,7 +80,7 @@ public class CellReference { if (_isColAbs) { colRef=colRef.substring(1); } - _colIndex = convertColStringToNum(colRef); + _colIndex = convertColStringToIndex(colRef); String rowRef=parts[2]; if (rowRef.length() < 1) { @@ -94,7 +94,7 @@ public class CellReference { } public CellReference(int pRow, int pCol) { - this(pRow, pCol, false, false); + this(pRow, pCol & 0xFFFF, false, false); } public CellReference(int pRow, short pCol) { this(pRow, (int)pCol, false, false); @@ -130,18 +130,31 @@ public class CellReference { public String getSheetName(){ return _sheetName; } - + + public static boolean isPartAbsolute(String part) { + return part.charAt(0) == ABSOLUTE_REFERENCE_MARKER; + } + /** * takes in a column reference portion of a CellRef and converts it from * ALPHA-26 number format to 0-based base 10. + * 'A' -> 0 + * 'Z' -> 25 + * 'AA' -> 26 + * 'IV' -> 255 + * @return zero based column index */ - private int convertColStringToNum(String ref) { - int lastIx = ref.length()-1; - int retval=0; - int pos = 0; - - for (int k = lastIx; k > -1; k--) { + protected static int convertColStringToIndex(String ref) { + int pos = 0; + int retval=0; + for (int k = ref.length()-1; k >= 0; k--) { char thechar = ref.charAt(k); + if (thechar == ABSOLUTE_REFERENCE_MARKER) { + if (k != 0) { + throw new IllegalArgumentException("Bad col ref format '" + ref + "'"); + } + break; + } // Character.getNumericValue() returns the values // 10-35 for the letter A-Z int shift = (int)Math.pow(26, pos); diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/Document.java b/src/scratchpad/src/org/apache/poi/hslf/record/Document.java index f6252840c..3284153cb 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/Document.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/Document.java @@ -71,27 +71,44 @@ public class Document extends PositionDependentRecordContainer * This will normally return an array of size 2 or 3 */ public SlideListWithText[] getSlideListWithTexts() { return slwts; } - /** + + /** * Returns the SlideListWithText that deals with the * Master Slides */ public SlideListWithText getMasterSlideListWithText() { - if(slwts.length > 0) { return slwts[0]; } - return null; } + for (int i = 0; i < slwts.length; i++) { + if(slwts[i].getInstance() == SlideListWithText.MASTER) { + return slwts[i]; + } + } + return null; + } + /** * Returns the SlideListWithText that deals with the * Slides, or null if there isn't one */ - public SlideListWithText getSlideSlideListWithText() { - if(slwts.length > 1) { return slwts[1]; } - return null; } + public SlideListWithText getSlideSlideListWithText() { + for (int i = 0; i < slwts.length; i++) { + if(slwts[i].getInstance() == SlideListWithText.SLIDES) { + return slwts[i]; + } + } + return null; + } /** * Returns the SlideListWithText that deals with the * notes, or null if there isn't one */ public SlideListWithText getNotesSlideListWithText() { - if(slwts.length > 2) { return slwts[2]; } - return null; } + for (int i = 0; i < slwts.length; i++) { + if(slwts[i].getInstance() == SlideListWithText.NOTES) { + return slwts[i]; + } + } + return null; + } /** diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/RecordContainer.java b/src/scratchpad/src/org/apache/poi/hslf/record/RecordContainer.java index f5efedc1d..0772e0312 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/RecordContainer.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/RecordContainer.java @@ -243,8 +243,16 @@ public abstract class RecordContainer extends Record moveChildRecords(oldLoc, newLoc, number); } } - - + + /** + * Set child records. + * + * @param records the new child records + */ + public void setChildRecord(Record[] records) { + this._children = records; + } + /* =============================================================== * External Serialisation Methods * =============================================================== diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/SlideListWithText.java b/src/scratchpad/src/org/apache/poi/hslf/record/SlideListWithText.java index 1573c82a1..13ffa7075 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/SlideListWithText.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/SlideListWithText.java @@ -50,7 +50,24 @@ import java.util.Vector; // For now, pretend to be an atom public class SlideListWithText extends RecordContainer { - private byte[] _header; + + /** + * Instance filed of the record header indicates that this SlideListWithText stores + * references to slides + */ + public static final int SLIDES = 0; + /** + * Instance filed of the record header indicates that this SlideListWithText stores + * references to master slides + */ + public static final int MASTER = 1; + /** + * Instance filed of the record header indicates that this SlideListWithText stores + * references to notes + */ + public static final int NOTES = 2; + + private byte[] _header; private static long _type = 4080; private SlideAtomsSet[] slideAtomsSets; @@ -123,9 +140,9 @@ public class SlideListWithText extends RecordContainer public void addSlidePersistAtom(SlidePersistAtom spa) { // Add the new SlidePersistAtom at the end appendChildRecord(spa); - + SlideAtomsSet newSAS = new SlideAtomsSet(spa, new Record[0]); - + // Update our SlideAtomsSets with this SlideAtomsSet[] sas = new SlideAtomsSet[slideAtomsSets.length+1]; System.arraycopy(slideAtomsSets, 0, sas, 0, slideAtomsSets.length); @@ -133,7 +150,15 @@ public class SlideListWithText extends RecordContainer slideAtomsSets = sas; } - /** + public int getInstance(){ + return LittleEndian.getShort(_header, 0) >> 4; + } + + public void setInstance(int inst){ + LittleEndian.putShort(_header, (short)((inst << 4) | 0xF)); + } + + /** * Get access to the SlideAtomsSets of the children of this record */ public SlideAtomsSet[] getSlideAtomsSets() { return slideAtomsSets; } @@ -152,35 +177,6 @@ public class SlideListWithText extends RecordContainer } /** - * Shifts a SlideAtomsSet to a new position. - * Works by shifting the child records about, then updating - * the SlideAtomSets array - * @param toMove The SlideAtomsSet to move - * @param newPosition The new (0 based) position for the SlideAtomsSet - */ - public void repositionSlideAtomsSet(SlideAtomsSet toMove, int newPosition) { - // Ensure it's one of ours - int curPos = -1; - for(int i=0; i= slideAtomsSets.length) { - throw new IllegalArgumentException("The new position must be between 0, and the number of SlideAtomsSets"); - } - - // Build the new records list - moveChildrenBefore(toMove.getSlidePersistAtom(), toMove.slideRecords.length, slideAtomsSets[newPosition].getSlidePersistAtom()); - - // Build the new SlideAtomsSets list - ArrayUtil.arrayMoveWithin(slideAtomsSets, curPos, newPosition, 1); - } - - /** * Inner class to wrap up a matching set of records that hold the * text for a given sheet. Contains the leading SlidePersistAtom, * and all of the records until the next SlidePersistAtom. This diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java index 80bc2e855..7672923ad 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java @@ -536,32 +536,37 @@ public final class SlideShow { /** * Re-orders a slide, to a new position. - * @param oldSlideNumer The old slide number (1 based) + * @param oldSlideNumber The old slide number (1 based) * @param newSlideNumber The new slide number (1 based) */ - public void reorderSlide(int oldSlideNumer, int newSlideNumber) { + public void reorderSlide(int oldSlideNumber, int newSlideNumber) { // Ensure these numbers are valid - if(oldSlideNumer < 1 || newSlideNumber < 1) { + if(oldSlideNumber < 1 || newSlideNumber < 1) { throw new IllegalArgumentException("Old and new slide numbers must be greater than 0"); } - if(oldSlideNumer > _slides.length || newSlideNumber > _slides.length) { + if(oldSlideNumber > _slides.length || newSlideNumber > _slides.length) { throw new IllegalArgumentException("Old and new slide numbers must not exceed the number of slides (" + _slides.length + ")"); } - - // Shift the SlideAtomsSet - SlideListWithText slwt = _documentRecord.getSlideSlideListWithText(); - slwt.repositionSlideAtomsSet( - slwt.getSlideAtomsSets()[(oldSlideNumer-1)], - (newSlideNumber-1) - ); - - // Re-order the slides - ArrayUtil.arrayMoveWithin(_slides, (oldSlideNumer-1), (newSlideNumber-1), 1); - - // Tell the appropriate slides their new numbers - for(int i=0; i<_slides.length; i++) { - _slides[i].setSlideNumber( (i+1) ); - } + + // The order of slides is defined by the order of slide atom sets in the SlideListWithText container. + SlideListWithText slwt = _documentRecord.getSlideSlideListWithText(); + SlideAtomsSet[] sas = slwt.getSlideAtomsSets(); + + SlideAtomsSet tmp = sas[oldSlideNumber-1]; + sas[oldSlideNumber-1] = sas[newSlideNumber-1]; + sas[newSlideNumber-1] = tmp; + + ArrayList lst = new ArrayList(); + for (int i = 0; i < sas.length; i++) { + lst.add(sas[i].getSlidePersistAtom()); + Record[] r = sas[i].getSlideRecords(); + for (int j = 0; j < r.length; j++) { + lst.add(r[j]); + } + _slides[i].setSlideNumber(i+1); + } + Record[] r = (Record[])lst.toArray(new Record[lst.size()]); + slwt.setChildRecord(r); } /* =============================================================== @@ -585,7 +590,8 @@ public final class SlideShow { if(slist == null) { // Need to add a new one slist = new SlideListWithText(); - _documentRecord.addSlideListWithText(slist); + slist.setInstance(SlideListWithText.SLIDES); + _documentRecord.addSlideListWithText(slist); } // Grab the SlidePersistAtom with the highest Slide Number. @@ -678,11 +684,11 @@ public final class SlideShow { ptr.addSlideLookup(sp.getRefID(), slideOffset); logger.log(POILogger.INFO, "New slide ended up at " + slideOffset); - // All done and added + slide.setMasterSheet(_masters[0]); + // All done and added return slide; } - /** * Adds a picture to this presentation and returns the associated index. * diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestReOrderingSlides.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestReOrderingSlides.java index 590212fb2..e039781cc 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestReOrderingSlides.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestReOrderingSlides.java @@ -279,18 +279,23 @@ public class TestReOrderingSlides extends TestCase { assertEquals(3, ss_read.getSlides().length); // And check it's as expected - s1 = ss_read.getSlides()[0]; - s2 = ss_read.getSlides()[1]; - s3 = ss_read.getSlides()[2]; - - assertEquals(257, s1._getSheetNumber()); - assertEquals(4, s1._getSheetRefId()); + Slide _s1 = ss_read.getSlides()[0]; + Slide _s2 = ss_read.getSlides()[1]; + Slide _s3 = ss_read.getSlides()[2]; + + // 1 --> 3 + assertEquals(s1._getSheetNumber(), _s3._getSheetNumber()); + assertEquals(s1._getSheetRefId(), _s3._getSheetRefId()); assertEquals(1, s1.getSlideNumber()); - assertEquals(256, s2._getSheetNumber()); - assertEquals(3, s2._getSheetRefId()); + + // 2nd slide is not updated + assertEquals(s2._getSheetNumber(), _s2._getSheetNumber()); + assertEquals(s2._getSheetRefId(), _s2._getSheetRefId()); assertEquals(2, s2.getSlideNumber()); - assertEquals(258, s3._getSheetNumber()); - assertEquals(5, s3._getSheetRefId()); + + // 3 --> 1 + assertEquals(s3._getSheetNumber(), _s1._getSheetNumber()); + assertEquals(s3._getSheetRefId(), _s1._getSheetRefId()); assertEquals(3, s3.getSlideNumber()); } } diff --git a/src/testcases/org/apache/poi/hssf/data/45376.xls b/src/testcases/org/apache/poi/hssf/data/45376.xls deleted file mode 100644 index 74602fd0b7caf9e946da734e108494e566f914d8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27648 zcmeHQdvp}neZI3=2}vMW34y_YSXR72uqz260g`s5U4d+b2*`%Svc*bTjEscj)nbF6 ziyw`fIIUwliJLevXOyYkateDdO7i@dEx6o|7EC88(?w{T98 zTR?~vIH7)aVq!vpLg4g+?T@Gi-a%QHqwxwLen=sNZ9W513@L$>LS{l{LCPSrA#)%w z0Z|UAfXszdLgqo{LoR|WfGmVu3|RzO3|RuHf-HpuALu%^|J1qiE5ClpS_v>q=t`Ox3~oL&@OtQw~NbwoNo&W%iXU0%0y z{kdUO?zzZ?ofl&ScCMZM*0eeKJICBGCB^8)MTO!!OMf!s#Zx%NK=5nf6HoYG#9oLq zkX|vye&3bMWDX4_$Ag_$r-p;S4WA!)G;r%=JoIJ3laRwYNA_)uy*6-)Q0hBD(L z$+5B2g9tqvV+}*YBCww;_m9i?&&z!?nA6sfShVZ=kw%2zy=%WOD#Lw3?n81v8+%K9 zTgLgNL?Rf$UPAbZTkm003-R$nD+a3_5=k+PQRsKe`o(g=eDTkauOFlGx!98^>a@!2 zo1AhKRy}d+^Q=k(`w~iXZZ;?1bHptXQ>9!w1l_Wo8mH%4YFB^o_hbvx-8qkUA`XHz3WabSQHHyu4EfLGfjJRn_aOtHZs)P~x&Zp{>0&xoVYvh4>0;(;McBJ*mMf z^8>CFUlq&jfE&X3>1m|OzfOEj;07-9(fHQfR5e`XkBNuHHX-_I)&_Hn)>nhFPJ$RJ zR1>nV24VVYqJ1@0{#b2!DQj8#5d_JJGB(=%56?)RsL%6D1GPL5V_Tk63Sr9 z#bb1@b6O}TEp>4++#2yXZY}xYa#PF++#;S3O*vPqGRv(t7jdhf#nFw}BA(1kp)cOo zk5m}~k@eJoh z$FcR+jEwZw3=UTL%fz=hc^bji8$5pew450U@gEcDi|%$RNZf)h@i!b|YMeJ@Q}}b? zOw|TrxMIPqCB9fe!xz?V&d2krD#I$X!Fc+1<56K;0;s{HGaj?WjB`R10N zxKo~d+*JN*hF6=X{GloEpb+@ylt+~l;f>`)>mTz4qm1FHXX1e%&>WoY1^lLR);`i- zYqyJoFZ1AKm}jZ*m>=eZSM|f|s;E%*woaxq8{?qGroAZ;Q28o6Cw%!i!k3F6O5~pu zewjBs+e_vfKxj8S*P;~e%~zL4E>j`AvVY-}Gh<(uZT~uad0zU?b(QCppZTIr2AlE=B(VtK607RsZ@3VEED^~>Y@?2L&6mJf;D zi8Z$sC^XmR5<^F>PWGqn#*$qGe~(#yWNP^@LTM~J-xsZ7@gkJa3zm3WHYN)!7x%WO z(#gTp1gxQ0{1yv&>{A29^B{Iy9Jr3h?PZvZP>Tcs&mzH0-)b3vyT5{LM2vqy|HyPIGl*@ep1$g`GzkRu@e|Kx246|0^>*W46iDY#R zixsFqOlidlFfaO9!z||QGV~pC-z6FlqzhB?0Zh`kT;AG`1)jYk2>)*s+)*)t zi5(Zbf>?s$R6YYejnufII*WcK_7@c3hGAh@=d&~GiwAKw2MY$g67GeF$wfyQ;eFV# zZMgBs)OqB>4i26z-nr|tkO){i3ucKQz5Jg}kv(A?MK4G>sXNRE$Nw)iklXSBD0{nl zF5$IfB(dI0Kj}O&b~MvAbVPJ?@9Emxec7J&gxHhV8{fH8Ty|tAof_v^=aEb*J)BBj zhw(~ucXWu(k>u5>p5tRFamnt)_O8^R=uQu%j%1RVq0u8^clVy2J+aOn(LOpnoXn(T zls&2O(cz<#?;hhJB6cUoQt7=zH>AYQL{Cp*55te9yG937qPD(&Y;3x1%YmX zdif#-?GlVn7)SBvLm%Jo``D6gZ@+WPtF6w_!jiBL!{*I$$P}Wp;3DWgsWaBI>p8k9 zoyU6vS}(vOmS=NxKNc{aovC@QHc&4>JmKSQO^vHUL~&N^lOsRQJ2^Ors5J*G?E&OT zexnDYl8+S_Fpi~BFKD!dp+pnQyXlza@1 z-Xx5zlOK-mP(H@G%E$P4Dj#DF=3{WECt+}cCt-ZNl^>V)pL~q>qkN2y!}2jc9?Qq* zk>_Jr5SWDVaaw*jK3>bm=*j0}eEgP=VM!qulXivm17ParbJA)NFVJ;_wJnf~?6>%+ z6=2$N`XgtemX8+V$6^#ivF|LxETX{H=3#NP3XcRCihW|CU~Hd?{k=2M7tohz^Nh0? zsW1&%=28(vT1CM9V3z?AGB+XaT2}c^A@1X%;M5Bu3%USZw;D?szwf^lSO{xHfoIF7 zrmOJ0at&rO1@Zz5Q7(8x;`P=dTuLGSsL3tLr>sSIwjM{IQ&pBI;8g;0gA_-Lq0g8o z6c*v<{d1w>Y_+H^5j)`*u-Ntb%*QkPyV>6u=7 zrb`DUJm^;` zr7K)|gQVwr>A5Z)mUN|;u5{^$r003*c`jWm>G@uIzDw6h`XVoVkxSQ0dV!Z-;L@8U zz0gZ9bm<03U+kqXcInNMUgV`0xpbqX7klZ&F5M*QC0=@oOK*{Mm6xt^>8+Ap>ZO;u z^fpNcy>!r}nOD}Wj7DE&K}xkG0ay~0bcaA*;eu~&NO zl@6U%^eQjC%ArM@jJ?`RuXgCHqStuoH4ZJ>W$d+zUgM4_Yg)woNn^@dCstO)TIa-4 zW6C;*7IB&AdM~}+p|gsv@zOO8EfO;J1~0wAp|gtK=%qJ0wCIqr!(KY<&{;)CymZ8& z#daCH)=SqqbXL)IUb@bq#SR&}-b>dzbXL)uy!0lA7M(J7gO_e_=&YhQd+E&%EiRF< z8@+U+LuVD;=rNG zf=79B%PP86)4XrC;)$Tzc17h|L+ZZSBJY5__0sp)fY*UG%3CiDv2vdKkHhtr^aZALD|zWk@V0;`CKY7y7EE2(z%bRK!Zf!r}ta zR-n9=B4ngWakBpmF1$$H&j~;8--kW^a6j)-_jBYd@9Qyh&WbaIG3GqO&N+Bcpy}B*eLAaX=@V#rj!mDsL($SF&~(72vv-||u15V!pFq>)Hhti3MN6MR z(-k%yy+_f~C(!g|Y&!cnMN6MR)AMcmz(b0bK7pn$ zvgzQL6fJ!MO)s!%@ij$DpFqhik3csrZ2YXlaDD{`UIL@WYgKF6fJ!MO)s|T z=(CEJK7po}*mUqYMN6MR(^a5-d^+%*i3zrt^a(V*)S;hHwDbuy9dziEik3csrk6SN z1B#YDfu=(aeV3x8PoQbOVIqr`RkZX8G`-xRk11OE1e#vq(1#Q)eF9Cdbm*|6rB9&g zRSx~IqNPuu>D3OsT+z}e(DWLIwyQ<@1Vxy;IO7t1g5SxwwARTvpb94a3g)ba?R5@) zx1yzAq3QJw?N_w)D>Pl>&~}4Jze3Y|-9on4n<}>SD>S{)p)-n>eubv_MvsjBx}v3D zq3MW2+s!Hc3QgBK^lK`%^eZ%7=g_^1mVSk%>mB+Z6fOMc7O9z{#PLemWn{gR@k zU!m#E4jost^eZ&o=+MtATKW~5ZgS{GMN7Xz(_0+c?w``H(DYV^Uaw+Hze3a79NM;W z=~rmF*`b3fw)87B-Gb*Lvc(=Gy;MlQLes7Ctzj%FUa@h0S!aSuQrq#kjhq!j-ugSGN>4 z+r_xLrLZ|J#?>u_1ze1)TM8?8F|KYYtir{(x}~tWF2>a@g;ly3SGN>4&&9a9rLg%f z#?>u_UF2e1-BQ>B7vt)d!WOz1SGN>)v5RqaOJR##jH_D;TkK+7-BQ>R7vt*IX4zU* z6610VSGO7=++J^8%@roPl?W=`%6G5?wG@j_c!GtE3sOsAN?Y)$$)J!U|S8?HUrjdz*-DgYaYfP zN&Dj?lS0`)X?a?s&1sF+rZw7`)@Wf`qit!8R;4xClh$ZSTB8kVjn<$`2KgXfjn83gLKaN9Dq?+H)V+O3vfVCU2xB*KTunq&Z-GJ>dV4Vi+5(9Rr z0o!T7x(wJZ1J-T8b{nt{8L&MDY_9?9F<_S&u*(hDJ_EMjfPL72eZ+uWVZeF~*p&wC zDg)M+hjARD{b?JgqXlS-?W#4lsMgq)T4O6J z?fo2wvPd<596DjZZZlxF8?ZYJ*qsLKPYl>c4cJ`<>`x8Y#|+rV4cOfV>=Op;&kWd~ z8?bu}*e4Cxy$0+)19raw`;-Cuv;q5!0eirJ{e=PhtO5I+0ejGZecph5!GJxKhjARD z{b?JgqXlSEhrVdQ{?dSb$$)*?fPKY)ebs<{&44{@z`kz49x-6wFks&_V2>KG#|+rx z2J8s~_M`!O%78s>z@9N+-!fp&8nAC0u4vC$( zYixb3vE8-C7S|fvT5D`&t+9Qz#+KC@+f-|8O|7vVwZ<0I8rx25Y&EU1y|l)b(i+=H zYiu2@v0b#r7SS5pLThXVt+D=<_H(RYe;V%r7r(1jmlj`zc%IKiFyqx5Te1W#8iV-p z*klIw{KgAjWA@2Vd>fQ!gygSS_!6mx@IZU_g`q1P8p^XpO4D^-p>DA-+!4-It+6$@~6Gh z?x%bGL4<9#f15Li`kwMPJP140R*c@{T>EtMPawgis(+%Nsh=CMI(QAd7QRx&I^`oz z7L#3#k1+Yjl8-3)NRp2r`K={BV&o&mck}%x#$SfGAxJf3Ib;Q7C1e$3HDnD0?|;a5 z$kyXn1K9xC2;u8hw3u25zXMed*#v2TaKzvvK0d?S0@(`TGdn&SYk{;vqL3J*4bl#Y zLs+H`9JfPuKsq6pKrV&gEiC-BOoi#Tj~~Ku4`eT-2XYzYatPDekK>0SAF=gb9Iu32 z1?jV~q z?)l_Yh%kf@ud2hl+n@ar3(v&77N?T8o_#{ZGMV&H|55x*GxKnKN@TfhVUsW z;e3lqsE<*F%0i{3C)<+azLQ}zRXAK5Zj3bjrbYz91;y>6(pO1*^wL`Pt6(>?deo9lk6Y1<}KZwP7S68hAWbysB=&}+mk+eqO2`( zY;1HqRk>L7q(;X0Q@I22RC?$-U&Xy`M^BAsMn{Hj@W%@{FrH_-fc}5>3~T6v{!g2D zAGY_a|8o@3Jv{g9gZ_UW^)4_NmG?M~mGj;c5xd~-tT{!i?4;NgGawRbf<7WzbjUP1V#)l0K$`Ug~ zH?9RguSOaPl!>*Mz$Zhf?L-bdPap@=555!lM9F}hfdyquwo4G{^^0qO4#-a=>_k5O zvPAjc2YudX9ekrTcO-KBjvACmoom+NcvL<~H9Q+-9lBQbNh^q>T>H@Wy=a@`5Pq~_ z1T9c2kqmymJu2ssjGw_-Kd!kOHIR^Pd=%KIoY^`poZG(Z-S(}8<#fQaipvtU<4QKc zYC2#gHK?I5qzz|n;1gifmBhfRE2y{eT6kCLO6qXbSF#)RnBTtJ<#llGO{-(Or%>WJ z&Ul5Kp&!8&np$tKID}AXgcwAK$yU&X+UiDaIDNsCMy@5j8ynzyjt^snA5(@RpM@1B z%o%YE_|$EZwM;K9^4foP7E34V!fv0+F=HHl>(t6^H2eImQXgiK=U>a4VVkXAQ7H&X*p{{=8w%m&R0lNyC}qE6Q8DANiZxLxoRYdMCru{@*-%Vt-Lx zIC|Sd|N7lS|9t-!9Gpka$FdNt)8M^abUQ4k6#5g#22#V>bpcV}zq~E>UuOcq_6;40 zG`x6=eOgyL<5tpL$$_Vz3J5>LH9T64Z}j+gr$?_z9nM5vjs--Ke>^idI-I)iKtPl- zj3fs#FMOX|_rZfh1F3mO1ESnN=_|dDp9qK&6m6ow5`Bs5lEX(aaar&S=HRUA?)eyk z`1g&b504*8r7|y`B}uh^boB7y1p&OMjTssjTIR98*pIL$?wtv^Ku0la#AjF*<2x(|gmrrYO&K2=xFdnP zLsks^ObpzXIGHW~bk;hN5Vi4+#(20Mx5iCv9kH57I2&oJX^ST|WfMP(#j@@7*}~ep z64?jD)#>EONU9h2w~^G)k?|yF&2h9kX3a->yM_kRqxeG8!Ax-Pp(O5MgT44hOedU{ zNTe6vl-Ur*H)N_m8n}H|FcOVy2nW0SPy8U5-8s~sPNt9J+b_bJbK5IEG55BDw*sXL z@olYA!*m=TWeATBhQ#oA){61&+keU$5`E)Q=?aWRMXEhAisHMhS~ZdGFu@&_dgMX`0tvdwiYvRwpX1!DP$Rl_4~cSf=+epp=l za`8V_oh>@^!>SkgQ-qJ*wowwn_|v9Ce&+Il z5Z6n&dGlA;h3qjRSX?+PhOi9NiMtzt9<$wuLX?Cb1LN}!`nH|$v<{-9r6pqW6TLaC zM*hr~>*LeSe;>l0wv*xSM!X*+yRh;!zKi<50(sA}ufH?i9J;ZisXfxRsiUqY-jGPt z)HlT1YGScSxTdMTv7B4t*7?UkueNP{llrc+U8JQQ(bLsEpkDC z_O9*Cq4vi1SVz30EgZ=emUMSpbBJLho5LNO!;!EOFx%UkLz~+-C+eFLO&v}3wH+}^ zTO3qb=@mOhF*+uWWl~25Q|Vymk%OaGmX*5K7HPh*uCcB*-cTQ|Y42zX*F+-i9W{-K z`nsBMI2LYiibTTUO|hG|Uf!12>c$Gol;u4Q^ZgVOoK$_y;q^iHUwc`pQ*rsYZ8SND zv#|I=`^jn!EZ1UB-}%tLztw#x@QqL6D=@2`{so_xJOSYXJDWcW;hl>f1N+YbNCkvT z?R?gA52Ot8AY?A&VF>TKuOSkDRuzDF{!;+x=f&li&mmuWB(erEBDB_Dl zKVHHABZ7T;I@?s&?|bDZuROS6QQ*GM;QH6U^%$@JR}ena;`Q%_@HrNr)9@#2yl(d6 zEQHr~7lhY+4}?E+djP`vI0<3>JOjzCj|I?2Qkg@egTb+6`fzYBF7x_iIu(pjq58~?Wjj(g5Fo%jb3XlyzGry0h{o5yKJd0&J5G3RgLNo4^Z@bVh)CeUb^oa8pRt;PhG2$`(XdSu?GGJ D(>WJg diff --git a/src/testcases/org/apache/poi/hssf/data/45720.xls b/src/testcases/org/apache/poi/hssf/data/45720.xls new file mode 100755 index 0000000000000000000000000000000000000000..9c6c484d6284beffdf3e29e804138214c7973fa4 GIT binary patch literal 16384 zcmeHOYiL|W6#njJliZ|D^J;6K>2{+uFGE_fEylWAt75Um601f{i8a}_q0yD3l`3Ml zwSq_mwW5D2KB#Z;fuf*-3HU>QNJS{32u4M)Rdf}_3flGi=I(VL*$q)sq;<|^UT4nC zIdksZJ9EyR`{Ci*vBzJT_XYCc0+irHssd$Mc!&80zgz>>r&22V=|bj-vTzD%pu**i zEGxy)MIV&EKf-JY@C5>RIXKD~_>!RqH)A?*O>Ap?D0Er;w!!!V_9avcEti})quo;6m*|iS&zOCT~^7$^nV??K=Vr`kTkrHh~95*s; z2rfV=&H%z^h8x=&8#{WNJA0b^LQiu;a510a1v&NCb;bHObL)kuix0P&TiR?L#T(-NEsS=bx=bu zgu>|LgL5{^x!8u_xp)$D3XxxjF2vA}O+^~6z*C(2S$x{bs~ETJx`yCtJk3{k(Wb>X z=XMgqGi2xVa#Sq{jT@Qp-k6Km`fbCc4X5?wwNo60;)Y-^o@H8qQ7;wikxR9?Y%+DC zP@Tsl7w@y!iRZAg5R2>~7h^S^CYNOdk)mUZ<9VE0oN-3l@gwxuW+=up&8_psI)<5i zdjP+rLMHX1k&y6u%^Cd9e$~Wyy`Bou7sIR9LiDFI-r>y4fUnP1PY=ia8XwkFV=>7`S z{7$j`9>{ym%0m8%+y%Pm}0^45Q?aR zK!;rh`2Mao`C21#4VnG^?2jLB)^K^V0F947!0E8RVYPP~}|N$3RyF z?qjHDILIJ%f5k@nj;OW*<)yUj4@}wk^#grF@!|NE9ifYN^~DAaUp#nxZqu7@IZ+Y5 zc3IWl>i4B-3swVG16Bi816Bi816Bi816Bi816Bi816Bi80|hiNk^LVy^1+dV?el96 zJWTt)?Y9?Y|G&$iV<7SmY+#Ukr<;MtAn2+BbmBwyzwG%P46^%gXPC{fk3s%`{S5LC zJjzhWA0RuvddW1?7qdmv1~QQx@<(`QQZSt;bt#vEM7cUXw0&5|Rfdg&#rLkT#)8#= z)qvH2)qvH2)qvH2)qvH2)qvH2)qvH&|40L}h-EO#=GOOk9i-6t1liR3rY~DxClF*z z%VyWVzB*(gyI(ebErY)K%g)zP2-VBhmp?#7=_i_J00A>7KlG<~AVqnC?JU3GS{Qt#Obv3yU@nGi-MvmPP%vn1LQ{OC+Mva>Xw(&zh%3?9hSAkX zL?YnDUX8|(m{zbhp^kMxV=}F_A&D4lNh+udbXgTwlCq+C=`z!O?nTfpe%yV(``zz+ zd+&4RoVo5MtGmfsGatXv9HUs)pG!;eFnflX`i4Ke`=ni6E4HvZR`s@6X%F7tuSzEAcHWdK>%Q& z54430KAyFP+7l7M1@E$c#aU}~zlGIin1!p8Z1+a~(r&l7BtU`z=rRVhsK^xHpS2rO% zS%I*u5<%=j2;PG*?)Nn4Z5q&wu)h_lFiP|py}O8TrJGv2jM|z&wnj_%5)(Zo8&;Yz z*z<{dD~ek!p(f9w%{fB!N%Y#5b(|G9dWKgfqxhT{b=I~R;TwBFQJ>lNDPLaPsODEW zIXJ4-^;PeP>r#>M%A0(4+zGY)l>=hTI8tuhzD4xis!&v=I-R>|$h7L60=}zIJpZna z=Dd)!cXmwV5o9u6&E_t;m#FzXcVCU?kLsgB4gDw>W7Lq3hT~X0j??3KJx9`Vd@(!IeLd z*foT3>JVJk5M1^UT+R^Ova>3cE5={MuXbCr!+%h78u)KtbvWH3~Zz~uuKGCV25Yy-d=_QYS8NsC-2_9+o<2+ z4=_s0Hv{tpnIIbC!D4jKFNz1Vy5RMLvrRCD-aXhIY5g{;EKI=sxlhdOA8+(07g;8u zF+T)AB!(%vX6}yqif~y{Lcf=RjWM2z`gkkNca)rcV-D^fXVT?gOu270?1p6c|4mci z;A*JvG@jDaWa&o6EM$Qk0pKY+Sb`aqBWU5+C=l!*?S@d;fG_CxZrrEee}`N2`={_E z-DB9_D1OMgJoZDc28b(i!yocS@P%-YQB3m=U4EIzd# z54W14`4Zae*4|`ZN2fqmZz^y4(`;}O@g&`N`yr8g zr$W(sI3|d5-x6{NLZ>B~Ced3Gt&%8(4jOH*z(TQ0a($Akk|;{SWZ@>{ol7VI{o3Xf zEMoRrtvdxfj_*dz$e5xX_5OA{3=NJiW|9>UW949g9ffHA_q6g4isUWD3DckNYod* zRnc$}#>@7#q(zt}+|;=C^dkHXuUpWlU0X!{4GS8*5sNY0!e1tfjHT2L#L>W&5_QO0 zUt5ZaqFrWhTZ$hiiqn$ZyMr1il?}A+p!7bnLzNn+A(L#C8rVgkOr-?%1R7LopjBF6 z9oomzI%pwYoXXuwt@gMV=LolS+V92ZM2$p|&6p!{=e#epUo_(~u~(Me+Ki8jdii!# z3(gYFk}GM!0ue6DJ<@`Yh*Pq%J1tbt7*nm5dJGrv2Fkwu7-oqMsc$<*E9jG|=;N5o zo8nG*=N-p{kn(qllN(^VBIppHQzCCOnCK{=XUGK(%>(ZhI%vjO0eu4sMOPddP?)8H z{)-0{CVfAoP@OVI2neYNF%)KI2BFhP_N=MaR(E2$EZ`5F=oGtTj`N*3fy2T^ zO>~jEPEzSzMER(?;x4MrC#$RK!bBcQs^d}>Aw9q7B8wWSk2#O|qKc4r<#`MfqEP;5 zU+Tu0qFs(X(@nx2iFgmKTq_x;_25L_E;IB{h7$QJsFKJnQHzeG$JHMC;k#tAs7o|q zv6O$_C7dkgOKR&S+$3I>S?!n6W)_88|EXvZy|~OGQSO^%%7MyWM6_tzZeSc8W7^Rh zm~2m=e;4{sr_+^?UAuV$-wSx`D#EH;2;bit{P=Mm{Tz{b7k`O29j2?_AT%&-bs#I1 zA4c1}bNm<g)?$u zQeCNeX|A*xX_>}F{Er^ViE(AP(lRsh@@8h{r%%qz&yZKXH=&S?Ks80sDKv-Wp{4)h PoU6}n$ng5+vRvhVp5!TR delta 2820 zcmZve3s6+o8OOixoU_Y5cr1!MtWV+t1wnAd#X#I$SCUE4V#gSxB1DN|WQ?y`(Y9<& zVX4L{z2Hs66tqR1ViMzyt|&1Zv{tJDlVYQQ29Tx4oWa)Vea1l{oVif zI`^L0bso_>kLY*J!!Om4q3rQ@)~vy!ET3tuH^Sb%W>nURV)mR)xhGxU5SK&&U-q^>&mF#{M$RGlCApkJw z3p9idTz;qz3ywns3)Ha(jt}*tAEeM5?k9^>CK>um{j^^hQU|iM8^Qw-W;~oDEI99n zY2P#4PfMR}Zu}&E_>P;hrw~YAx%!3G%U;Y_xpdx(>o=?~f*}z1h2l$zU=HSvoPIB@ z@E_FxYij`xy$>*}0igb$09#rB%r^n<^#F8#4$%A`fN$;stojaM5mh!B5mG`CMu#Iz zib5DZ4B^;FgzM1=AB;t~8jCPL3E{6Z5QaR3uzxN>))Is>c?jNB2%A>Z^+tr11vm}t zPK4t{2=yfhIlB?^Uq^8MiGmJOz;OiMIi$v@&=tDZf?&OYu((al{tWlJa|>Pyz#w;{ z^LHq&HXH!=;;m^yv`O_w@dnOHE8T;)C7|dcqNHz+7FA12gp$Ag3Xl0?rQ&!sk)P1I zlufU`EqbLRcE^4`s@$bq-f>VIk{c zl&}9Vo;OjFu}Uh30GE=hI5@n0)?K4KEeeA`C!YfVhJcQlAQ})JWsmt`WBjnOe%Lra zZ2SN#{JwC^0Q{a!@WUqhVX=N#oF6vnN375U@jrqlNbtiZ6C3o%Y9;z%Nq*QAKg=vy z;Ufu?{TQeDVHQ7Zx*ztqAC~$Mo7(@}(;kBT>d&*E*ws*BV=#5VHOn}b_ zjzB=KBN&)BNDHH3A_Rbqtn$z21G?5plNPww5inrV^_isYzR9ShR{cdu*jMcSVBbW& zl2%jijyRl)(>@&x;9x^(Yp;ed`Y*uEedpa+{DboqSV-5=te!q!VfF5#Z|*UQyK>`X z)(+z(ip%|SZE4`q^MA&p=PqO5S5)|0pU-!P&Pwaw#=5I7Xh!p0v}fMzPT+a8n?C6_ zbLYuyCA`PR&(n5W*ptdmXDhGuB#U7IF5&*D=dCCa_{$2e{#JvrVvJO7Ql`!g*+R}# zfm)v)93kp^8jLb%XWmf>L?kKCSEOR`J9S(V zE@J;KQeR2JCT1&9pPzyqBG-EWJTuIg8YQ6d0H}qtF-BaLNo!_fGIy3%s-3gZ!mAXQ zI&u!0`5_YX=U@^)LSp+Id{*cv&eJ&uZwHC4&GVSrycnnQu5F3xpe)MZCEGJuI6)lQ zNL_7`$}3fM7EY84qJETxQ-y_=LNzYIUvY0uqPl$vi7b6fMhXoJ%q= zcm*kw?A5jc<3yyazIFv(j1!|Iw3g8TZE}G8GAiFi>8i_UfXkGmt&9d}BN0(f1-eLN zl+yq)GDUSc20unmMm=@{r}G?IpW7#JuIQDSo~*@<0+x|_zZTPk_t<`}vXi)6%#&^B zpTs%BB=4R+iRr>AneG&3h^w;Q1*dSPm@GT1JVpKFd|ISlJ%!H&^(wBu#~Yyu^$k_= zT?|t*>u|Vm>L`3|9cBhuC`i5l5t_liDCr1SJL}NI?YJwS$MDTXVjjurq`KCCBjx<}8ZeRT%3NxE zBbi3YxfV6jLg&jA+Z!=jWJvXnR1Q*}j~nsV1`#LW^UXMd#~e;nH#cKEx00wKA@<6d zt~b+~d;e9W4r;+{fD}kU2|RDa9q}Ki7&5ah8-P_&P=iary5QF`*r239F1<*G|SvsSdVd0*|D`*V=JB z&y*E9s6ww?mdR4VFGywesf*;_)fh%Ic7dr2Z(%}k6VuQpMrdNBG^%IpChtzD?8>{*o%8Y0_a`Y6;XD(E#G{cXFvAY(`_xvw|b+F77TZ+YEvpFnwd*Z`KbdbT3X-|tz zNl$TD(ruQML^`I~Qtlsg_sN-Q(KfR!*>1K^vCp)p*d6jPd&Xw6A?UHrWj54_i+#UY Pxw5ukn#cPyX4U*3eTx4J diff --git a/src/testcases/org/apache/poi/hssf/data/LookupFunctionsTestCaseData.xls b/src/testcases/org/apache/poi/hssf/data/LookupFunctionsTestCaseData.xls index f4b35fb93504540b85455ff37341318cbcd74e1e..94f16e9840536697981a8c31ce59900625c6236f 100755 GIT binary patch delta 5460 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 delta 3500 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 delta 1500 zcmah}TSyd982-;Vd*8L$ZQP5qv+J6f88(KxY-Z&Wc7NzX50X@1AQE8+K|P34Qcy_R z{~{F`ln+@%wp3_R8I(yk3bCi~L69CIy4Z_|c4qb*cPye^&dmRv|NFlGKZkRst(~;B zk&%8@B&4_aWk-~ zrcA;NiejcrZOrG=C@Ocq7smB zAj&4*MKzp=Fz_qVbTKR;<}uu*EXTKb+wq8!pDxp-G~wYW1LvYqnvqcAYbp%4;^ZIA z3iu_;nw9gKFc_1tQC++q0Z+sjZjQ+s*7;ec*{|VDfB`9} z;U}*7Ni|tHEECGn`~iAQvf!^n^N>1h3^Lw6>X4tyKq6Pet=!>2j)qgb>MYf?bIm2H zY2ca`s_EdG>r}IfHRCAkao>9#QuK7$g$GGMU)nxMp6@)bZibZRnAbs#{ ziiQE+TnTONK6iLU9rkjE#!wWmMkJK;(<@9B=l6Mq8>?}=Y{&Y_W%N3we|D&?N$%Oa zC%H+0aNrIR=@lbl0J7;vNdNZD&FfbVV3^qH0BD%P3+2V65C_YTMusWbDgw9!ZkN8H zgBn0ZDb`f{aDb3)=n3wMt9UamJD&o~hF~pa;z)eWmXBbCdV{h-@WBJC9#$I|U|SEs sdAmdhn=oG6dsJ8gS;Xj&c1!<)kP0T+$-T+}!P@hvkA9g`PpXoZ--0=Au>b%7 diff --git a/src/testcases/org/apache/poi/hssf/data/testNames.xls b/src/testcases/org/apache/poi/hssf/data/testNames.xls index 7ebbb633af8bd6a0a951f1d4335c9242fe2c3f20..c5d43cc038a5d0ee463a66ddb0d1c125c914a750 100644 GIT binary patch delta 1911 zcma)7eP~-%6hHUnC3!DNle}8Pn_Xi*bY16I(|j$_e!Mp8#wCu`khp=W*^m z`JH<{??(=N%?{jPN4it!UtfLhGR!XJP12}I&z8^V+97c{HN0A>y?&9w} z91VA(M5|*s$B~oB3FH(q;VX+zJjCYZXSLyj>rO8J`6-LJ?ymdF`xy)J7v$HZpX2-w zIq$oAs3^dSlF!=r`-)#K32fK(A89s!vG+6fDA!b-?{ljEM{=ofgkc^Ml+6lC*(~5; znCAuQA60;FSKY#6fWK9dbiPw8XG)8ABV&+RHDF4(vN)@h&2(3OSfQ`8|QFeAM@1!By4%ih`h`(7I8s^>$L=o+^F~hEc>DQ(oUxi=%lN_pah0 z6n%AgGeBKnAJ6+cnZ}Rd*vvoock=5#ookxT?&TY_b~)d38O*0NXM~OMorx>=>F5<4 zHX6$`Ju(zeB!jnrhm+wbU1LzFnhH(Nj10s_wj>?_JZ}=RjrS#Yv1PuHJWk01bz~0j z_;j2PrGCSWFGJXWloZKN0Lme{Zs@}&F#1uC(WR@$D9?I~{MKXkDkkDrg3wv7Ur4)K zkEwOos}j{Dnh#myBIsB(qpg4q28|oVxXG{q>8K}!#ssQ%i79T__=$9@96Noz}u+R9U+95}+S%v`IM28FDg|+y3Aq^dU_+EEFuhb0Z$QhtXnNT>a z_>rps&63fE-fa|wc3lOPzrOipUd%7eXYQ~s%-=I^ThQ?nDw7!T8qTLNAU^4(QT)9J zvy{w8_d)3%>?0~XLWL&>Kf8Ipw^L#s^9SP_wi%BNY60$E!2zi#Mp2Hyty$5!3h_0$ zcD^8oOe1I0r8jn)gF6xmyK2sjJX0rXMR63E$S~=3>+A=)Q6Uh!_v#E4CmCE2C z7&Y5+s2I%VQi(K~4x;H+;{G%bjNjrb;}`7T(H{-Jv}ZrN!@DMi*g5{nM2^|`#}j%x z^{IMIz=H>#wjqd2*XZeeGtbRVj*mY-J3sjnTW`)y9%3$Rgy;+e`Cof=Ch@S`W*Ye- F`x{9>UzGp= delta 1569 zcmai!e`r%z6vxkbeX%b;+Pu_OB2}BFO0BrnG%rh5ZI`|#nJB0zBB%x1HQILVI_<_L zY_xL&#WK*IIYDsj4-q;Tti0-QiogC)5&TDrZfvr$e>S!;3UgxJ+}pV~Nt%;dy?#03R;E z&lKQe`Ft=h@p!%=k6$jpj~3t;3h?;~+9sZOPDOc%&&fb;cA5KzzLeaXmTB%Mx9(B-YbeHFs zgz2zt{xEz6kRDsK;XZiaH{P)hO5r7_gJz`+&WjKb7acc~lMHJ*E3?k@rei6;Em0>o zStnhOy*JYM82_-Q8{I|A)G0O?oM(c^uh@fc9J_#u=7Ax5n*6^-%Fue&kM;oGR%#;~2Qf4Gy!4g+ui`f)RB~z{8 zun`G|BMCDZNoK^>S6iEcv6fiOh$dc6G$dMt8WL2#@7Iy8ZyYgkA<~tu{rEeDx;Nq| zz2Du63SH~=*Lt`%@nyxeEsn#E{sXTMbfwd84P<|UjA^9jU0h-v>^Y2O;uqZ1-k0tS XY;AvIUz+_Wu(N_bR~K9H-mCZ@Pc%~H diff --git a/src/testcases/org/apache/poi/hssf/record/formula/TestExternalFunctionFormulas.java b/src/testcases/org/apache/poi/hssf/record/formula/TestExternalFunctionFormulas.java index cc5198acf..1f6d0d3c2 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/TestExternalFunctionFormulas.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/TestExternalFunctionFormulas.java @@ -74,9 +74,18 @@ public final class TestExternalFunctionFormulas extends TestCase { public void testEvaluate() { HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("externalFunctionExample.xls"); HSSFSheet sheet = wb.getSheetAt(0); - HSSFCell cell = sheet.getRow(0).getCell(0); HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb); - CellValue evalResult = fe.evaluate(cell); - evalResult.toString(); + confirmCellEval(sheet, 0, 0, fe, "YEARFRAC(B1,C1)", 29.0/90.0); + confirmCellEval(sheet, 1, 0, fe, "YEARFRAC(B2,C2)", 0.0); + confirmCellEval(sheet, 2, 0, fe, "IF(ISEVEN(3),1.2,1.6)", 1.6); + confirmCellEval(sheet, 3, 0, fe, "IF(ISODD(3),1.2,1.6)", 1.2); + } + + private static void confirmCellEval(HSSFSheet sheet, int rowIx, int colIx, + HSSFFormulaEvaluator fe, String expectedFormula, double expectedResult) { + HSSFCell cell = sheet.getRow(rowIx).getCell(colIx); + assertEquals(expectedFormula, cell.getCellFormula()); + CellValue cv = fe.evaluate(cell); + assertEquals(expectedResult, cv.getNumberValue(), 0.0); } } diff --git a/src/testcases/org/apache/poi/hssf/record/formula/atp/TestYearFracCalculator.java b/src/testcases/org/apache/poi/hssf/record/formula/atp/TestYearFracCalculator.java index aaf03a238..890a5c2cb 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/atp/TestYearFracCalculator.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/atp/TestYearFracCalculator.java @@ -38,6 +38,7 @@ public final class TestYearFracCalculator extends TestCase { confirm(md(1999, 3, 31), md(1999, 4, 3), 1, 0.008219178); confirm(md(1999, 4, 5), md(1999, 4, 8), 1, 0.008219178); confirm(md(1999, 4, 4), md(1999, 4, 7), 1, 0.008219178); + confirm(md(2000, 2, 5), md(2000, 6, 1), 0, 0.322222222); } private void confirm(double startDate, double endDate, int basis, double expectedValue) { diff --git a/src/testcases/org/apache/poi/hssf/record/formula/eval/AllFormulaEvalTests.java b/src/testcases/org/apache/poi/hssf/record/formula/eval/AllFormulaEvalTests.java index 8887445ad..755106194 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/eval/AllFormulaEvalTests.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/eval/AllFormulaEvalTests.java @@ -31,6 +31,8 @@ public class AllFormulaEvalTests { TestSuite result = new TestSuite(AllFormulaEvalTests.class.getName()); result.addTestSuite(TestAreaEval.class); result.addTestSuite(TestCircularReferences.class); + result.addTestSuite(TestDivideEval.class); + result.addTestSuite(TestEqualEval.class); result.addTestSuite(TestExternalFunction.class); result.addTestSuite(TestFormulaBugs.class); result.addTestSuite(TestFormulasFromSpreadsheet.class); diff --git a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestCircularReferences.java b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestCircularReferences.java index ee2047987..07f13a044 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestCircularReferences.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestCircularReferences.java @@ -93,7 +93,6 @@ public final class TestCircularReferences extends TestCase { HSSFCell testCell = row.createCell(0); testCell.setCellFormula("A1"); - HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb); CellValue cellValue = evaluateWithCycles(wb, sheet, testCell); confirmCycleErrorCode(cellValue); @@ -114,7 +113,6 @@ public final class TestCircularReferences extends TestCase { HSSFCell testCell = row.createCell(3); testCell.setCellFormula("A1"); - HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb); CellValue cellValue = evaluateWithCycles(wb, sheet, testCell); confirmCycleErrorCode(cellValue); diff --git a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestDivideEval.java b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestDivideEval.java new file mode 100644 index 000000000..71bf03e50 --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestDivideEval.java @@ -0,0 +1,62 @@ +/* ==================================================================== + 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 junit.framework.TestCase; + +import org.apache.poi.hssf.record.formula.functions.EvalFactory; +import org.apache.poi.hssf.record.formula.functions.NumericFunctionInvoker; + +/** + * Test for divide operator evaluator. + * + * @author Josh Micich + */ +public final class TestDivideEval extends TestCase { + + private static void confirm(ValueEval arg0, ValueEval arg1, double expectedResult) { + Eval[] args = { + arg0, arg1, + }; + + double result = NumericFunctionInvoker.invoke(DivideEval.instance, args, 0, 0); + + assertEquals(expectedResult, result, 0); + } + + public void testBasic() { + confirm(new NumberEval(5), new NumberEval(2), 2.5); + confirm(new NumberEval(3), new NumberEval(16), 0.1875); + confirm(new NumberEval(-150), new NumberEval(-15), 10.0); + confirm(new StringEval("0.2"), new NumberEval(0.05), 4.0); + confirm(BoolEval.TRUE, new StringEval("-0.2"), -5.0); + } + + public void test1x1Area() { + AreaEval ae0 = EvalFactory.createAreaEval("B2:B2", new ValueEval[] { new NumberEval(50), }); + AreaEval ae1 = EvalFactory.createAreaEval("C2:C2", new ValueEval[] { new NumberEval(10), }); + confirm(ae0, ae1, 5); + } + public void testDivZero() { + Eval[] args = { + new NumberEval(5), NumberEval.ZERO, + }; + Eval result = DivideEval.instance.evaluate(args, 0, (short) 0); + assertEquals(ErrorEval.DIV_ZERO, result); + } +} diff --git a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestEqualEval.java b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestEqualEval.java new file mode 100644 index 000000000..d1b1db0d1 --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestEqualEval.java @@ -0,0 +1,69 @@ +/* ==================================================================== + 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 junit.framework.AssertionFailedError; +import junit.framework.TestCase; + +import org.apache.poi.hssf.record.formula.functions.EvalFactory; + +/** + * Test for unary plus operator evaluator. + * + * @author Josh Micich + */ +public final class TestEqualEval extends TestCase { + + /** + * Test for bug observable at svn revision 692218 (Sep 2008)
    + * The value from a 1x1 area should be taken immediately, regardless of srcRow and srcCol + */ + public void test1x1AreaOperand() { + + ValueEval[] values = { BoolEval.FALSE, }; + Eval[] args = { + EvalFactory.createAreaEval("B1:B1", values), + BoolEval.FALSE, + }; + Eval result = EqualEval.instance.evaluate(args, 10, (short)20); + if (result instanceof ErrorEval) { + if (result == ErrorEval.VALUE_INVALID) { + throw new AssertionFailedError("Identified bug in evaluation of 1x1 area"); + } + } + assertEquals(BoolEval.class, result.getClass()); + assertTrue(((BoolEval)result).getBooleanValue()); + } + /** + * Empty string is equal to blank + */ + public void testBlankEqualToEmptyString() { + + Eval[] args = { + new StringEval(""), + BlankEval.INSTANCE, + }; + Eval result = EqualEval.instance.evaluate(args, 10, (short)20); + assertEquals(BoolEval.class, result.getClass()); + BoolEval be = (BoolEval) result; + if (!be.getBooleanValue()) { + throw new AssertionFailedError("Identified bug blank/empty string equality"); + } + assertTrue(be.getBooleanValue()); + } +} diff --git a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestPercentEval.java b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestPercentEval.java index e027605df..ef2340d4a 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestPercentEval.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestPercentEval.java @@ -20,7 +20,7 @@ package org.apache.poi.hssf.record.formula.eval; import junit.framework.AssertionFailedError; import junit.framework.TestCase; -import org.apache.poi.hssf.record.formula.PercentPtg; +import org.apache.poi.hssf.record.formula.functions.EvalFactory; import org.apache.poi.hssf.record.formula.functions.NumericFunctionInvoker; import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator; @@ -41,8 +41,8 @@ public final class TestPercentEval extends TestCase { arg, }; - PercentEval opEval = new PercentEval(PercentPtg.instance); - double result = NumericFunctionInvoker.invoke(opEval, args, -1, (short)-1); + OperationEval opEval = PercentEval.instance; + double result = NumericFunctionInvoker.invoke(opEval, args, 0, 0); assertEquals(expectedResult, result, 0); } @@ -55,6 +55,10 @@ public final class TestPercentEval extends TestCase { confirm(BoolEval.TRUE, 0.01); } + public void test1x1Area() { + AreaEval ae = EvalFactory.createAreaEval("B2:B2", new ValueEval[] { new NumberEval(50), }); + confirm(ae, 0.5); + } public void testInSpreadSheet() { HSSFWorkbook wb = new HSSFWorkbook(); HSSFSheet sheet = wb.createSheet("Sheet1"); 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 33c38a6c1..726633c40 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 @@ -1,27 +1,25 @@ -/* -* 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; 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; @@ -53,7 +51,7 @@ public final class TestUnaryPlusEval extends TestCase { EvalFactory.createAreaEval(areaPtg, values), }; - double result = NumericFunctionInvoker.invoke(new UnaryPlusEval(UnaryPlusPtg.instance), args, 10, (short)20); + double result = NumericFunctionInvoker.invoke(UnaryPlusEval.instance, args, 10, (short)20); assertEquals(35, result, 0); } diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java index 5973d7cb2..2ec7ad005 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java @@ -36,6 +36,7 @@ public final class AllIndividualFunctionEvaluationTests { result.addTestSuite(TestIndex.class); result.addTestSuite(TestIsBlank.class); result.addTestSuite(TestLen.class); + result.addTestSuite(TestLookupFunctionsFromSpreadsheet.class); result.addTestSuite(TestMid.class); result.addTestSuite(TestMathX.class); result.addTestSuite(TestMatch.class); diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestDate.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestDate.java index 83c9fcd34..68bc43154 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestDate.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestDate.java @@ -28,29 +28,29 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook; * @author Pavel Krupets (pkrupets at palmtreebusiness dot com) */ public final class TestDate extends TestCase { - + private HSSFCell cell11; private HSSFFormulaEvaluator evaluator; public void setUp() { HSSFWorkbook wb = new HSSFWorkbook(); HSSFSheet sheet = wb.createSheet("new sheet"); - cell11 = sheet.createRow(0).createCell(0); + cell11 = sheet.createRow(0).createCell(0); cell11.setCellType(HSSFCell.CELL_TYPE_FORMULA); evaluator = new HSSFFormulaEvaluator(sheet, wb); } - - /** - * Test disabled pending a fix in the formula evaluator - * TODO - create MissingArgEval and modify the formula evaluator to handle this - */ + + /** + * Test disabled pending a fix in the formula evaluator + * TODO - create MissingArgEval and modify the formula evaluator to handle this + */ public void DISABLEDtestSomeArgumentsMissing() { confirm("DATE(, 1, 0)", 0.0); confirm("DATE(, 1, 1)", 1.0); } - + public void testValid() { - + confirm("DATE(1900, 1, 1)", 1); confirm("DATE(1900, 1, 32)", 32); confirm("DATE(1900, 222, 1)", 6727); @@ -58,7 +58,7 @@ public final class TestDate extends TestCase { confirm("DATE(2000, 1, 222)", 36747.00); confirm("DATE(2007, 1, 1)", 39083); } - + public void testBugDate() { confirm("DATE(1900, 2, 29)", 60); confirm("DATE(1900, 2, 30)", 61); @@ -66,7 +66,7 @@ public final class TestDate extends TestCase { confirm("DATE(1900, 1, 2222)", 2222); confirm("DATE(1900, 1, 22222)", 22222); } - + public void testPartYears() { confirm("DATE(4, 1, 1)", 1462.00); confirm("DATE(14, 1, 1)", 5115.00); @@ -74,10 +74,11 @@ public final class TestDate extends TestCase { confirm("DATE(1004, 1, 1)", 366705.00); } - private void confirm(String formulaText, double expectedResult) { + private void confirm(String formulaText, double expectedResult) { cell11.setCellFormula(formulaText); + evaluator.clearCache(); double actualValue = evaluator.evaluate(cell11).getNumberValue(); - assertEquals(expectedResult, actualValue, 0); - } + assertEquals(expectedResult, actualValue, 0); + } } diff --git a/src/testcases/org/apache/poi/hssf/usermodel/AllUserModelTests.java b/src/testcases/org/apache/poi/hssf/usermodel/AllUserModelTests.java index 772a4fdfc..72d420cb4 100755 --- a/src/testcases/org/apache/poi/hssf/usermodel/AllUserModelTests.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/AllUserModelTests.java @@ -46,6 +46,7 @@ public class AllUserModelTests { result.addTestSuite(TestHSSFConditionalFormatting.class); result.addTestSuite(TestHSSFDataFormatter.class); result.addTestSuite(TestHSSFDateUtil.class); + result.addTestSuite(TestHSSFFormulaEvaluator.class); result.addTestSuite(TestHSSFHeaderFooter.class); result.addTestSuite(TestHSSFHyperlink.class); result.addTestSuite(TestHSSFOptimiser.class); diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java index e7c793176..1b5e4cd5d 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java @@ -37,6 +37,7 @@ import org.apache.poi.hssf.record.EmbeddedObjectRefSubRecord; import org.apache.poi.hssf.record.NameRecord; import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate; import org.apache.poi.hssf.record.formula.DeletedArea3DPtg; +import org.apache.poi.hssf.record.formula.Ptg; import org.apache.poi.util.TempFile; /** @@ -1020,9 +1021,9 @@ public final class TestBugs extends TestCase { NameRecord r = w.getNameRecord(i); assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets()); - List nd = r.getNameDefinition(); - assertEquals(1, nd.size()); - assertTrue(nd.get(0) instanceof DeletedArea3DPtg); + Ptg[] nd = r.getNameDefinition(); + assertEquals(1, nd.length); + assertTrue(nd[0] instanceof DeletedArea3DPtg); } @@ -1038,9 +1039,9 @@ public final class TestBugs extends TestCase { NameRecord r = w.getNameRecord(i); assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets()); - List nd = r.getNameDefinition(); - assertEquals(1, nd.size()); - assertTrue(nd.get(0) instanceof DeletedArea3DPtg); + Ptg[] nd = r.getNameDefinition(); + assertEquals(1, nd.length); + assertTrue(nd[0] instanceof DeletedArea3DPtg); } @@ -1055,9 +1056,9 @@ public final class TestBugs extends TestCase { NameRecord r = w.getNameRecord(i); assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets()); - List nd = r.getNameDefinition(); - assertEquals(1, nd.size()); - assertTrue(nd.get(0) instanceof DeletedArea3DPtg); + Ptg[] nd = r.getNameDefinition(); + assertEquals(1, nd.length); + assertTrue(nd[0] instanceof DeletedArea3DPtg); } } diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorBugs.java b/src/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorBugs.java index 6ebcf96bb..f1d838efa 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorBugs.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorBugs.java @@ -19,6 +19,8 @@ package org.apache.poi.hssf.usermodel; import java.io.File; import java.io.FileOutputStream; +import java.util.Calendar; +import java.util.GregorianCalendar; import java.util.Iterator; import junit.framework.AssertionFailedError; @@ -35,6 +37,7 @@ import org.apache.poi.hssf.record.formula.Ptg; */ public final class TestFormulaEvaluatorBugs extends TestCase { + private static final boolean OUTPUT_TEST_FILES = false; private String tmpDirName; protected void setUp() { @@ -65,13 +68,15 @@ public final class TestFormulaEvaluatorBugs extends TestCase { HSSFFormulaEvaluator.evaluateAllFormulaCells(wb); assertEquals(4.2 * 25, row.getCell(3).getNumericCellValue(), 0.0001); - // Save - File existing = new File(tmpDirName, "44636-existing.xls"); - FileOutputStream out = new FileOutputStream(existing); - wb.write(out); - out.close(); - System.err.println("Existing file for bug #44636 written to " + existing.toString()); - + FileOutputStream out; + if (OUTPUT_TEST_FILES) { + // Save + File existing = new File(tmpDirName, "44636-existing.xls"); + out = new FileOutputStream(existing); + wb.write(out); + out.close(); + System.err.println("Existing file for bug #44636 written to " + existing.toString()); + } // Now, do a new file from scratch wb = new HSSFWorkbook(); sheet = wb.createSheet(); @@ -86,12 +91,14 @@ public final class TestFormulaEvaluatorBugs extends TestCase { HSSFFormulaEvaluator.evaluateAllFormulaCells(wb); assertEquals(5.4, row.getCell(0).getNumericCellValue(), 0.0001); - // Save - File scratch = new File(tmpDirName, "44636-scratch.xls"); - out = new FileOutputStream(scratch); - wb.write(out); - out.close(); - System.err.println("New file for bug #44636 written to " + scratch.toString()); + if (OUTPUT_TEST_FILES) { + // Save + File scratch = new File(tmpDirName, "44636-scratch.xls"); + out = new FileOutputStream(scratch); + wb.write(out); + out.close(); + System.err.println("New file for bug #44636 written to " + scratch.toString()); + } } /** @@ -281,64 +288,39 @@ public final class TestFormulaEvaluatorBugs extends TestCase { } /** - * Apparently, each subsequent call takes longer, which is very - * odd. - * We think it's because the formulas are recursive and crazy + * The HSSFFormula evaluator performance benefits greatly from caching of intermediate cell values */ - public void DISABLEDtestSlowEvaluate45376() throws Exception { - HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("45376.xls"); + public void testSlowEvaluate45376() { - final String SHEET_NAME = "Eingabe"; - final int row = 6; - final HSSFSheet sheet = wb.getSheet(SHEET_NAME); - - int firstCol = 4; - int lastCol = 14; - long[] timings = new long[lastCol-firstCol+1]; - long[] rtimings = new long[lastCol-firstCol+1]; - - long then, now; - - final HSSFRow excelRow = sheet.getRow(row); - for(int i = firstCol; i <= lastCol; i++) { - final HSSFCell excelCell = excelRow.getCell(i); - final HSSFFormulaEvaluator evaluator = new - HSSFFormulaEvaluator(sheet, wb); - - now = System.currentTimeMillis(); - evaluator.evaluate(excelCell); - then = System.currentTimeMillis(); - timings[i-firstCol] = (then-now); - System.err.println("Col " + i + " took " + (then-now) + "ms"); - } - for(int i = lastCol; i >= firstCol; i--) { - final HSSFCell excelCell = excelRow.getCell(i); - final HSSFFormulaEvaluator evaluator = new - HSSFFormulaEvaluator(sheet, wb); - - now = System.currentTimeMillis(); - evaluator.evaluate(excelCell); - then = System.currentTimeMillis(); - rtimings[i-firstCol] = (then-now); - System.err.println("Col " + i + " took " + (then-now) + "ms"); - } - - // The timings for each should be about the same - long avg = 0; - for(int i=0; i 1.5*avg) { - System.err.println("Doing col " + (i+firstCol) + - " took " + timings[i] + "ms, vs avg " + - avg + "ms" - ); - } - } + // Firstly set up a sequence of formula cells where each depends on the previous multiple + // times. Without caching, each subsequent cell take about 4 times longer to evaluate. + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet = wb.createSheet("Sheet1"); + HSSFRow row = sheet.createRow(0); + for(int i=1; i<10; i++) { + HSSFCell cell = row.createCell(i); + char prevCol = (char) ('A' + i-1); + String prevCell = prevCol + "1"; + // this formula is inspired by the offending formula of the attachment for bug 45376 + String formula = "IF(DATE(YEAR(" + prevCell + "),MONTH(" + prevCell + ")+1,1)<=$D$3," + + "DATE(YEAR(" + prevCell + "),MONTH(" + prevCell + ")+1,1),NA())"; + cell.setCellFormula(formula); + + } + Calendar cal = new GregorianCalendar(2000, 0, 1, 0, 0, 0); + row.createCell(0).setCellValue(cal); + + // Choose cell A9, so that the failing test case doesn't take too long to execute. + HSSFCell cell = row.getCell(8); + HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb); + evaluator.evaluate(cell); + int evalCount = evaluator.getEvaluationCount(); + // With caching, the evaluationCount is 8 which is a big improvement + if (evalCount > 10) { + // Without caching, evaluating cell 'A9' takes 21845 evaluations which consumes + // much time (~3 sec on Core 2 Duo 2.2GHz) + System.err.println("Cell A9 took " + evalCount + " intermediate evaluations"); + throw new AssertionFailedError("Identifed bug 45376 - Formula evaluator should cache values"); + } } } \ No newline at end of file diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFFormulaEvaluator.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFFormulaEvaluator.java new file mode 100644 index 000000000..970b166dd --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFFormulaEvaluator.java @@ -0,0 +1,82 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hssf.usermodel; + +import org.apache.poi.hssf.HSSFTestDataSamples; +import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue; + +import junit.framework.TestCase; +/** + * + * @author Josh Micich + */ +public final class TestHSSFFormulaEvaluator extends TestCase { + + /** + * Test that the HSSFFormulaEvaluator can evaluate simple named ranges + * (single cells and rectangular areas) + */ + public void testEvaluateSimple() { + HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("testNames.xls"); + HSSFSheet sheet = wb.getSheetAt(0); + HSSFCell cell = sheet.getRow(8).getCell(0); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb); + CellValue cv = fe.evaluate(cell); + assertEquals(HSSFCell.CELL_TYPE_NUMERIC, cv.getCellType()); + assertEquals(3.72, cv.getNumberValue(), 0.0); + } + + public void testFullColumnRefs() { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet = wb.createSheet("Sheet1"); + HSSFRow row = sheet.createRow(0); + HSSFCell cell0 = row.createCell(0); + cell0.setCellFormula("sum(D:D)"); + HSSFCell cell1 = row.createCell(1); + cell1.setCellFormula("sum(D:E)"); + + // some values in column D + setValue(sheet, 1, 3, 5.0); + setValue(sheet, 2, 3, 6.0); + setValue(sheet, 5, 3, 7.0); + setValue(sheet, 50, 3, 8.0); + + // some values in column E + setValue(sheet, 1, 4, 9.0); + setValue(sheet, 2, 4, 10.0); + setValue(sheet, 30000, 4, 11.0); + + // some other values + setValue(sheet, 1, 2, 100.0); + setValue(sheet, 2, 5, 100.0); + setValue(sheet, 3, 6, 100.0); + + + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb); + assertEquals(26.0, fe.evaluate(cell0).getNumberValue(), 0.0); + assertEquals(56.0, fe.evaluate(cell1).getNumberValue(), 0.0); + } + + private static void setValue(HSSFSheet sheet, int rowIndex, int colIndex, double value) { + HSSFRow row = sheet.getRow(rowIndex); + if (row == null) { + row = sheet.createRow(rowIndex); + } + row.createCell(colIndex).setCellValue(value); + } +} diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java index 4c8c8c5d6..00db13d91 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java @@ -28,15 +28,11 @@ import junit.framework.TestCase; import org.apache.poi.hssf.HSSFTestDataSamples; import org.apache.poi.hssf.model.Sheet; -import org.apache.poi.hssf.record.HCenterRecord; -import org.apache.poi.hssf.record.PasswordRecord; -import org.apache.poi.hssf.record.ProtectRecord; -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.model.DrawingManager2; +import org.apache.poi.hssf.record.*; import org.apache.poi.ss.util.Region; import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ddf.EscherDgRecord; /** * Tests HSSFSheet. This test case is very incomplete at the moment. @@ -47,806 +43,849 @@ import org.apache.poi.ss.util.CellRangeAddress; */ public final class TestHSSFSheet extends TestCase { - private static HSSFWorkbook openSample(String sampleFileName) { - return HSSFTestDataSamples.openSampleWorkbook(sampleFileName); - } - - /** - * Test the gridset field gets set as expected. - */ - public void testBackupRecord() { - HSSFWorkbook wb = new HSSFWorkbook(); - HSSFSheet s = wb.createSheet(); - Sheet sheet = s.getSheet(); - - assertEquals(true, sheet.getGridsetRecord().getGridset()); - s.setGridsPrinted(true); - assertEquals(false, sheet.getGridsetRecord().getGridset()); - } - - /** - * Test vertically centered output. - */ - public void testVerticallyCenter() { - HSSFWorkbook wb = new HSSFWorkbook(); - HSSFSheet s = wb.createSheet(); - Sheet sheet = s.getSheet(); - VCenterRecord record = sheet.getPageSettings().getVCenter(); - - assertEquals(false, record.getVCenter()); - s.setVerticallyCenter(true); - assertEquals(true, record.getVCenter()); - - // wb.write(new FileOutputStream("c:\\test.xls")); - } - - /** - * Test horizontally centered output. - */ - public void testHorizontallyCenter() { - HSSFWorkbook wb = new HSSFWorkbook(); - HSSFSheet s = wb.createSheet(); - Sheet sheet = s.getSheet(); - HCenterRecord record = sheet.getPageSettings().getHCenter(); - - assertEquals(false, record.getHCenter()); - s.setHorizontallyCenter(true); - assertEquals(true, record.getHCenter()); - } - - - /** - * Test WSBboolRecord fields get set in the user model. - */ - public void testWSBool() { - HSSFWorkbook wb = new HSSFWorkbook(); - HSSFSheet s = wb.createSheet(); - Sheet sheet = s.getSheet(); - WSBoolRecord record = - (WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid); - - // Check defaults - assertEquals(true, record.getAlternateExpression()); - assertEquals(true, record.getAlternateFormula()); - assertEquals(false, record.getAutobreaks()); - assertEquals(false, record.getDialog()); - assertEquals(false, record.getDisplayGuts()); - assertEquals(true, record.getFitToPage()); - assertEquals(false, record.getRowSumsBelow()); - assertEquals(false, record.getRowSumsRight()); - - // Alter - s.setAlternativeExpression(false); - s.setAlternativeFormula(false); - s.setAutobreaks(true); - s.setDialog(true); - s.setDisplayGuts(true); - s.setFitToPage(false); - s.setRowSumsBelow(true); - s.setRowSumsRight(true); - - // Check - assertEquals(false, record.getAlternateExpression()); - assertEquals(false, record.getAlternateFormula()); - assertEquals(true, record.getAutobreaks()); - assertEquals(true, record.getDialog()); - assertEquals(true, record.getDisplayGuts()); - assertEquals(false, record.getFitToPage()); - assertEquals(true, record.getRowSumsBelow()); - assertEquals(true, record.getRowSumsRight()); - assertEquals(false, s.getAlternateExpression()); - assertEquals(false, s.getAlternateFormula()); - assertEquals(true, s.getAutobreaks()); - assertEquals(true, s.getDialog()); - assertEquals(true, s.getDisplayGuts()); - assertEquals(false, s.getFitToPage()); - assertEquals(true, s.getRowSumsBelow()); - assertEquals(true, s.getRowSumsRight()); - } - - public void testReadBooleans() { - HSSFWorkbook workbook = new HSSFWorkbook(); - HSSFSheet sheet = workbook.createSheet("Test boolean"); - HSSFRow row = sheet.createRow(2); - HSSFCell cell = row.createCell(9); - cell.setCellValue(true); - cell = row.createCell(11); - cell.setCellValue(true); - - workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook); - - sheet = workbook.getSheetAt(0); - row = sheet.getRow(2); - assertNotNull(row); - assertEquals(2, row.getPhysicalNumberOfCells()); - } - - public void testRemoveRow() { - HSSFWorkbook workbook = new HSSFWorkbook(); - HSSFSheet sheet = workbook.createSheet("Test boolean"); - HSSFRow row = sheet.createRow(2); - sheet.removeRow(row); - } - - public void testRemoveZeroRow() { - HSSFWorkbook workbook = new HSSFWorkbook(); - HSSFSheet sheet = workbook.createSheet("Sheet1"); - HSSFRow row = sheet.createRow(0); - try { - sheet.removeRow(row); - } catch (IllegalArgumentException e) { - if (e.getMessage().equals("Invalid row number (-1) outside allowable range (0..65535)")) { - throw new AssertionFailedError("Identified bug 45367"); - } - throw e; - } - } - - public void testCloneSheet() { - HSSFWorkbook workbook = new HSSFWorkbook(); - HSSFSheet sheet = workbook.createSheet("Test Clone"); - HSSFRow row = sheet.createRow(0); - HSSFCell cell = row.createCell(0); - HSSFCell cell2 = row.createCell(1); - cell.setCellValue(new HSSFRichTextString("clone_test")); - cell2.setCellFormula("sin(1)"); - - HSSFSheet clonedSheet = workbook.cloneSheet(0); - HSSFRow clonedRow = clonedSheet.getRow(0); - - //Check for a good clone - assertEquals(clonedRow.getCell(0).getRichStringCellValue().getString(), "clone_test"); - - //Check that the cells are not somehow linked - cell.setCellValue(new HSSFRichTextString("Difference Check")); - cell2.setCellFormula("cos(2)"); - if ("Difference Check".equals(clonedRow.getCell(0).getRichStringCellValue().getString())) { - fail("string cell not properly cloned"); - } - if ("COS(2)".equals(clonedRow.getCell(1).getCellFormula())) { - fail("formula cell not properly cloned"); - } - assertEquals(clonedRow.getCell(0).getRichStringCellValue().getString(), "clone_test"); - assertEquals(clonedRow.getCell(1).getCellFormula(), "SIN(1)"); - } - - /** tests that the sheet name for multiple clones of the same sheet is unique - * BUG 37416 - */ - public void testCloneSheetMultipleTimes() { - HSSFWorkbook workbook = new HSSFWorkbook(); - HSSFSheet sheet = workbook.createSheet("Test Clone"); - HSSFRow row = sheet.createRow(0); - HSSFCell cell = row.createCell(0); - cell.setCellValue(new HSSFRichTextString("clone_test")); - //Clone the sheet multiple times - workbook.cloneSheet(0); - workbook.cloneSheet(0); - - assertNotNull(workbook.getSheet("Test Clone")); - assertNotNull(workbook.getSheet("Test Clone(1)")); - assertNotNull(workbook.getSheet("Test Clone(2)")); - } - - /** - * Setting landscape and portrait stuff on new sheets - */ - public void testPrintSetupLandscapeNew() throws Exception { - HSSFWorkbook workbook = new HSSFWorkbook(); - HSSFSheet sheetL = workbook.createSheet("LandscapeS"); - HSSFSheet sheetP = workbook.createSheet("LandscapeP"); - - // Check two aspects of the print setup - assertFalse(sheetL.getPrintSetup().getLandscape()); - assertFalse(sheetP.getPrintSetup().getLandscape()); - assertEquals(0, sheetL.getPrintSetup().getCopies()); - assertEquals(0, sheetP.getPrintSetup().getCopies()); - - // Change one on each - sheetL.getPrintSetup().setLandscape(true); - sheetP.getPrintSetup().setCopies((short)3); - - // Check taken - assertTrue(sheetL.getPrintSetup().getLandscape()); - assertFalse(sheetP.getPrintSetup().getLandscape()); - assertEquals(0, sheetL.getPrintSetup().getCopies()); - assertEquals(3, sheetP.getPrintSetup().getCopies()); - - // Save and re-load, and check still there - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - workbook.write(baos); - workbook = new HSSFWorkbook(new ByteArrayInputStream(baos.toByteArray())); - - assertTrue(sheetL.getPrintSetup().getLandscape()); - assertFalse(sheetP.getPrintSetup().getLandscape()); - assertEquals(0, sheetL.getPrintSetup().getCopies()); - assertEquals(3, sheetP.getPrintSetup().getCopies()); - } - - /** - * Setting landscape and portrait stuff on existing sheets - */ - public void testPrintSetupLandscapeExisting() { - HSSFWorkbook workbook = openSample("SimpleWithPageBreaks.xls"); - - assertEquals(3, workbook.getNumberOfSheets()); - - HSSFSheet sheetL = workbook.getSheetAt(0); - HSSFSheet sheetPM = workbook.getSheetAt(1); - HSSFSheet sheetLS = workbook.getSheetAt(2); - - // Check two aspects of the print setup - assertFalse(sheetL.getPrintSetup().getLandscape()); - assertTrue(sheetPM.getPrintSetup().getLandscape()); - assertTrue(sheetLS.getPrintSetup().getLandscape()); - assertEquals(1, sheetL.getPrintSetup().getCopies()); - assertEquals(1, sheetPM.getPrintSetup().getCopies()); - assertEquals(1, sheetLS.getPrintSetup().getCopies()); - - // Change one on each - sheetL.getPrintSetup().setLandscape(true); - sheetPM.getPrintSetup().setLandscape(false); - sheetPM.getPrintSetup().setCopies((short)3); - - // Check taken - assertTrue(sheetL.getPrintSetup().getLandscape()); - assertFalse(sheetPM.getPrintSetup().getLandscape()); - assertTrue(sheetLS.getPrintSetup().getLandscape()); - assertEquals(1, sheetL.getPrintSetup().getCopies()); - assertEquals(3, sheetPM.getPrintSetup().getCopies()); - assertEquals(1, sheetLS.getPrintSetup().getCopies()); - - // Save and re-load, and check still there - workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook); - - assertTrue(sheetL.getPrintSetup().getLandscape()); - assertFalse(sheetPM.getPrintSetup().getLandscape()); - assertTrue(sheetLS.getPrintSetup().getLandscape()); - assertEquals(1, sheetL.getPrintSetup().getCopies()); - assertEquals(3, sheetPM.getPrintSetup().getCopies()); - assertEquals(1, sheetLS.getPrintSetup().getCopies()); - } - - public void testGroupRows() { - HSSFWorkbook workbook = new HSSFWorkbook(); - HSSFSheet s = workbook.createSheet(); - HSSFRow r1 = s.createRow(0); - HSSFRow r2 = s.createRow(1); - HSSFRow r3 = s.createRow(2); - HSSFRow r4 = s.createRow(3); - HSSFRow r5 = s.createRow(4); - - assertEquals(0, r1.getOutlineLevel()); - assertEquals(0, r2.getOutlineLevel()); - assertEquals(0, r3.getOutlineLevel()); - assertEquals(0, r4.getOutlineLevel()); - assertEquals(0, r5.getOutlineLevel()); - - s.groupRow(2,3); - - assertEquals(0, r1.getOutlineLevel()); - assertEquals(0, r2.getOutlineLevel()); - assertEquals(1, r3.getOutlineLevel()); - assertEquals(1, r4.getOutlineLevel()); - assertEquals(0, r5.getOutlineLevel()); - - // Save and re-open - workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook); - - s = workbook.getSheetAt(0); - r1 = s.getRow(0); - r2 = s.getRow(1); - r3 = s.getRow(2); - r4 = s.getRow(3); - r5 = s.getRow(4); - - assertEquals(0, r1.getOutlineLevel()); - assertEquals(0, r2.getOutlineLevel()); - assertEquals(1, r3.getOutlineLevel()); - assertEquals(1, r4.getOutlineLevel()); - assertEquals(0, r5.getOutlineLevel()); - } - - public void testGroupRowsExisting() { - HSSFWorkbook workbook = openSample("NoGutsRecords.xls"); - - HSSFSheet s = workbook.getSheetAt(0); - HSSFRow r1 = s.getRow(0); - HSSFRow r2 = s.getRow(1); - HSSFRow r3 = s.getRow(2); - HSSFRow r4 = s.getRow(3); - HSSFRow r5 = s.getRow(4); - HSSFRow r6 = s.getRow(5); - - assertEquals(0, r1.getOutlineLevel()); - assertEquals(0, r2.getOutlineLevel()); - assertEquals(0, r3.getOutlineLevel()); - assertEquals(0, r4.getOutlineLevel()); - assertEquals(0, r5.getOutlineLevel()); - assertEquals(0, r6.getOutlineLevel()); - - // This used to complain about lacking guts records - s.groupRow(2, 4); - - assertEquals(0, r1.getOutlineLevel()); - assertEquals(0, r2.getOutlineLevel()); - assertEquals(1, r3.getOutlineLevel()); - assertEquals(1, r4.getOutlineLevel()); - assertEquals(1, r5.getOutlineLevel()); - assertEquals(0, r6.getOutlineLevel()); - - // Save and re-open - try { - workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook); - } catch (OutOfMemoryError e) { - throw new AssertionFailedError("Identified bug 39903"); - } - - s = workbook.getSheetAt(0); - r1 = s.getRow(0); - r2 = s.getRow(1); - r3 = s.getRow(2); - r4 = s.getRow(3); - r5 = s.getRow(4); - r6 = s.getRow(5); - - assertEquals(0, r1.getOutlineLevel()); - assertEquals(0, r2.getOutlineLevel()); - assertEquals(1, r3.getOutlineLevel()); - assertEquals(1, r4.getOutlineLevel()); - assertEquals(1, r5.getOutlineLevel()); - assertEquals(0, r6.getOutlineLevel()); - } - - public void testGetDrawings() { - HSSFWorkbook wb1c = openSample("WithChart.xls"); - HSSFWorkbook wb2c = openSample("WithTwoCharts.xls"); - - // 1 chart sheet -> data on 1st, chart on 2nd - assertNotNull(wb1c.getSheetAt(0).getDrawingPatriarch()); - assertNotNull(wb1c.getSheetAt(1).getDrawingPatriarch()); - assertFalse(wb1c.getSheetAt(0).getDrawingPatriarch().containsChart()); - assertTrue(wb1c.getSheetAt(1).getDrawingPatriarch().containsChart()); - - // 2 chart sheet -> data on 1st, chart on 2nd+3rd - assertNotNull(wb2c.getSheetAt(0).getDrawingPatriarch()); - assertNotNull(wb2c.getSheetAt(1).getDrawingPatriarch()); - assertNotNull(wb2c.getSheetAt(2).getDrawingPatriarch()); - assertFalse(wb2c.getSheetAt(0).getDrawingPatriarch().containsChart()); - assertTrue(wb2c.getSheetAt(1).getDrawingPatriarch().containsChart()); - assertTrue(wb2c.getSheetAt(2).getDrawingPatriarch().containsChart()); - } - - /** - * Test that the ProtectRecord is included when creating or cloning a sheet - */ - public void testProtect() { - HSSFWorkbook workbook = new HSSFWorkbook(); - HSSFSheet hssfSheet = workbook.createSheet(); - Sheet sheet = hssfSheet.getSheet(); - ProtectRecord protect = sheet.getProtect(); - - assertFalse(protect.getProtect()); - - // This will tell us that cloneSheet, and by extension, - // the list forms of createSheet leave us with an accessible - // ProtectRecord. - hssfSheet.setProtect(true); - Sheet cloned = sheet.cloneSheet(); - assertNotNull(cloned.getProtect()); - assertTrue(hssfSheet.getProtect()); - } - - public void testProtectSheet() { - short expected = (short)0xfef1; - HSSFWorkbook wb = new HSSFWorkbook(); - HSSFSheet s = wb.createSheet(); - s.protectSheet("abcdefghij"); - Sheet sheet = s.getSheet(); - ProtectRecord protect = sheet.getProtect(); - PasswordRecord pass = sheet.getPassword(); - assertTrue("protection should be on",protect.getProtect()); - assertTrue("object protection should be on",sheet.isProtected()[1]); - assertTrue("scenario protection should be on",sheet.isProtected()[2]); - assertEquals("well known value for top secret hash should be "+Integer.toHexString(expected).substring(4),expected,pass.getPassword()); - } - - - public void testZoom() { - HSSFWorkbook wb = new HSSFWorkbook(); - HSSFSheet sheet = wb.createSheet(); - assertEquals(-1, sheet.getSheet().findFirstRecordLocBySid(SCLRecord.sid)); - sheet.setZoom(3,4); - assertTrue(sheet.getSheet().findFirstRecordLocBySid(SCLRecord.sid) > 0); - SCLRecord sclRecord = (SCLRecord) sheet.getSheet().findFirstRecordBySid(SCLRecord.sid); - assertEquals(3, sclRecord.getNumerator()); - assertEquals(4, sclRecord.getDenominator()); - - int sclLoc = sheet.getSheet().findFirstRecordLocBySid(SCLRecord.sid); - int window2Loc = sheet.getSheet().findFirstRecordLocBySid(WindowTwoRecord.sid); - assertTrue(sclLoc == window2Loc + 1); - } - - - /** - * When removing one merged region, it would break - * - */ - public void testRemoveMerged() { - HSSFWorkbook wb = new HSSFWorkbook(); - HSSFSheet sheet = wb.createSheet(); - CellRangeAddress region = new CellRangeAddress(0, 1, 0, 1); - sheet.addMergedRegion(region); - region = new CellRangeAddress(1, 2, 0, 1); - sheet.addMergedRegion(region); - - sheet.removeMergedRegion(0); - - region = sheet.getMergedRegion(0); - assertEquals("Left over region should be starting at row 1", 1, region.getFirstRow()); - - sheet.removeMergedRegion(0); - - assertEquals("there should be no merged regions left!", 0, sheet.getNumMergedRegions()); - - //an, add, remove, get(0) would null pointer - sheet.addMergedRegion(region); - assertEquals("there should now be one merged region!", 1, sheet.getNumMergedRegions()); - sheet.removeMergedRegion(0); - assertEquals("there should now be zero merged regions!", 0, sheet.getNumMergedRegions()); - //add it again! - region.setLastRow(4); - - sheet.addMergedRegion(region); - assertEquals("there should now be one merged region!", 1, sheet.getNumMergedRegions()); - - //should exist now! - assertTrue("there isn't more than one merged region in there", 1 <= sheet.getNumMergedRegions()); - region = sheet.getMergedRegion(0); - assertEquals("the merged row to doesnt match the one we put in ", 4, region.getLastRow()); - } - - public void testShiftMerged() { - HSSFWorkbook wb = new HSSFWorkbook(); - HSSFSheet sheet = wb.createSheet(); - HSSFRow row = sheet.createRow(0); - HSSFCell cell = row.createCell(0); - cell.setCellValue(new HSSFRichTextString("first row, first cell")); - - row = sheet.createRow(1); - cell = row.createCell(1); - cell.setCellValue(new HSSFRichTextString("second row, second cell")); - - CellRangeAddress region = new CellRangeAddress(1, 1, 0, 1); - sheet.addMergedRegion(region); - - sheet.shiftRows(1, 1, 1); - - region = sheet.getMergedRegion(0); - assertEquals("Merged region not moved over to row 2", 2, region.getFirstRow()); - } - - /** - * Tests the display of gridlines, formulas, and rowcolheadings. - * @author Shawn Laubach (slaubach at apache dot org) - */ - public void testDisplayOptions() { - HSSFWorkbook wb = new HSSFWorkbook(); - HSSFSheet sheet = wb.createSheet(); - - wb = HSSFTestDataSamples.writeOutAndReadBack(wb); - sheet = wb.getSheetAt(0); - - assertEquals(sheet.isDisplayGridlines(), true); - assertEquals(sheet.isDisplayRowColHeadings(), true); - assertEquals(sheet.isDisplayFormulas(), false); - - sheet.setDisplayGridlines(false); - sheet.setDisplayRowColHeadings(false); - sheet.setDisplayFormulas(true); - - wb = HSSFTestDataSamples.writeOutAndReadBack(wb); - sheet = wb.getSheetAt(0); - - assertEquals(sheet.isDisplayGridlines(), false); - assertEquals(sheet.isDisplayRowColHeadings(), false); - assertEquals(sheet.isDisplayFormulas(), true); - } - - - /** - * Make sure the excel file loads work - * - */ - public void testPageBreakFiles() { - HSSFWorkbook wb = openSample("SimpleWithPageBreaks.xls"); - - HSSFSheet sheet = wb.getSheetAt(0); - assertNotNull(sheet); - - assertEquals("1 row page break", 1, sheet.getRowBreaks().length); - assertEquals("1 column page break", 1, sheet.getColumnBreaks().length); - - assertTrue("No row page break", sheet.isRowBroken(22)); - assertTrue("No column page break", sheet.isColumnBroken((short)4)); - - sheet.setRowBreak(10); - sheet.setColumnBreak((short)13); - - assertEquals("row breaks number", 2, sheet.getRowBreaks().length); - assertEquals("column breaks number", 2, sheet.getColumnBreaks().length); - - wb = HSSFTestDataSamples.writeOutAndReadBack(wb); - sheet = wb.getSheetAt(0); - - assertTrue("No row page break", sheet.isRowBroken(22)); - assertTrue("No column page break", sheet.isColumnBroken((short)4)); - - assertEquals("row breaks number", 2, sheet.getRowBreaks().length); - assertEquals("column breaks number", 2, sheet.getColumnBreaks().length); - } - - public void testDBCSName () { - HSSFWorkbook wb = openSample("DBCSSheetName.xls"); - wb.getSheetAt(1); - assertEquals ("DBCS Sheet Name 2", wb.getSheetName(1),"\u090f\u0915" ); - assertEquals("DBCS Sheet Name 1", wb.getSheetName(0),"\u091c\u093e"); - } - - /** - * Testing newly added method that exposes the WINDOW2.toprow - * parameter to allow setting the toprow in the visible view - * of the sheet when it is first opened. - */ - public void testTopRow() { - HSSFWorkbook wb = openSample("SimpleWithPageBreaks.xls"); - - HSSFSheet sheet = wb.getSheetAt(0); - assertNotNull(sheet); - - short toprow = (short) 100; - short leftcol = (short) 50; - sheet.showInPane(toprow,leftcol); - assertEquals("HSSFSheet.getTopRow()", toprow, sheet.getTopRow()); - assertEquals("HSSFSheet.getLeftCol()", leftcol, sheet.getLeftCol()); - } - - /** cell with formula becomes null on cloning a sheet*/ - public void test35084() { - - HSSFWorkbook wb = new HSSFWorkbook(); - HSSFSheet s = wb.createSheet("Sheet1"); - HSSFRow r = s.createRow(0); - r.createCell(0).setCellValue(1); - r.createCell(1).setCellFormula("A1*2"); - HSSFSheet s1 = wb.cloneSheet(0); - r = s1.getRow(0); - assertEquals("double", r.getCell(0).getNumericCellValue(), 1, 0); // sanity check - assertNotNull(r.getCell(1)); - assertEquals("formula", r.getCell(1).getCellFormula(), "A1*2"); - } - - /** test that new default column styles get applied */ - public void testDefaultColumnStyle() { - HSSFWorkbook wb = new HSSFWorkbook(); - HSSFCellStyle style = wb.createCellStyle(); - HSSFSheet s = wb.createSheet(); - s.setDefaultColumnStyle((short) 0, style); - HSSFRow r = s.createRow(0); - HSSFCell c = r.createCell(0); - assertEquals("style should match", style.getIndex(), c.getCellStyle().getIndex()); - } - - - /** - * - */ - public void testAddEmptyRow() { - //try to add 5 empty rows to a new sheet - HSSFWorkbook workbook = new HSSFWorkbook(); - HSSFSheet sheet = workbook.createSheet(); - for (int i = 0; i < 5; i++) { - sheet.createRow(i); - } - - workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook); - - //try adding empty rows in an existing worksheet - workbook = openSample("Simple.xls"); - - sheet = workbook.getSheetAt(0); - for (int i = 3; i < 10; i++) sheet.createRow(i); - - workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook); - } - - public void testAutoSizeColumn() { - HSSFWorkbook wb = openSample("43902.xls"); - String sheetName = "my sheet"; - HSSFSheet sheet = wb.getSheet(sheetName); - - // Can't use literal numbers for column sizes, as - // will come out with different values on different - // machines based on the fonts available. - // So, we use ranges, which are pretty large, but - // thankfully don't overlap! - int minWithRow1And2 = 6400; - int maxWithRow1And2 = 7800; - int minWithRow1Only = 2750; - int maxWithRow1Only = 3300; - - // autoSize the first column and check its size before the merged region (1,0,1,1) is set: - // it has to be based on the 2nd row width - sheet.autoSizeColumn((short)0); - assertTrue("Column autosized with only one row: wrong width", sheet.getColumnWidth((short)0) >= minWithRow1And2); - assertTrue("Column autosized with only one row: wrong width", sheet.getColumnWidth((short)0) <= maxWithRow1And2); - - //create a region over the 2nd row and auto size the first column - sheet.addMergedRegion(new CellRangeAddress(1,1,0,1)); - sheet.autoSizeColumn((short)0); - HSSFWorkbook wb2 = HSSFTestDataSamples.writeOutAndReadBack(wb); - - // check that the autoSized column width has ignored the 2nd row - // because it is included in a merged region (Excel like behavior) - HSSFSheet sheet2 = wb2.getSheet(sheetName); - assertTrue(sheet2.getColumnWidth((short)0) >= minWithRow1Only); - assertTrue(sheet2.getColumnWidth((short)0) <= maxWithRow1Only); - - // remove the 2nd row merged region and check that the 2nd row value is used to the autoSizeColumn width - sheet2.removeMergedRegion(1); - sheet2.autoSizeColumn((short)0); - HSSFWorkbook wb3 = HSSFTestDataSamples.writeOutAndReadBack(wb2); - HSSFSheet sheet3 = wb3.getSheet(sheetName); - assertTrue(sheet3.getColumnWidth((short)0) >= minWithRow1And2); - assertTrue(sheet3.getColumnWidth((short)0) <= maxWithRow1And2); - } - - /** - * Setting ForceFormulaRecalculation on sheets - */ - public void testForceRecalculation() throws Exception { - HSSFWorkbook workbook = openSample("UncalcedRecord.xls"); - - HSSFSheet sheet = workbook.getSheetAt(0); - HSSFSheet sheet2 = workbook.getSheetAt(0); - HSSFRow row = sheet.getRow(0); - row.createCell(0).setCellValue(5); - row.createCell(1).setCellValue(8); - assertFalse(sheet.getForceFormulaRecalculation()); - assertFalse(sheet2.getForceFormulaRecalculation()); - - // Save and manually verify that on column C we have 0, value in template - File tempFile = new File(System.getProperty("java.io.tmpdir")+"/uncalced_err.xls" ); - tempFile.delete(); - FileOutputStream fout = new FileOutputStream( tempFile ); - workbook.write( fout ); - fout.close(); - sheet.setForceFormulaRecalculation(true); - assertTrue(sheet.getForceFormulaRecalculation()); - - // Save and manually verify that on column C we have now 13, calculated value - tempFile = new File(System.getProperty("java.io.tmpdir")+"/uncalced_succ.xls" ); - tempFile.delete(); - fout = new FileOutputStream( tempFile ); - workbook.write( fout ); - fout.close(); - - // Try it can be opened - HSSFWorkbook wb2 = new HSSFWorkbook(new FileInputStream(tempFile)); - - // And check correct sheet settings found - sheet = wb2.getSheetAt(0); - sheet2 = wb2.getSheetAt(1); - assertTrue(sheet.getForceFormulaRecalculation()); - assertFalse(sheet2.getForceFormulaRecalculation()); - - // Now turn if back off again - sheet.setForceFormulaRecalculation(false); - - fout = new FileOutputStream( tempFile ); - wb2.write( fout ); - fout.close(); - wb2 = new HSSFWorkbook(new FileInputStream(tempFile)); - - assertFalse(wb2.getSheetAt(0).getForceFormulaRecalculation()); - assertFalse(wb2.getSheetAt(1).getForceFormulaRecalculation()); - assertFalse(wb2.getSheetAt(2).getForceFormulaRecalculation()); - - // Now add a new sheet, and check things work - // with old ones unset, new one set - HSSFSheet s4 = wb2.createSheet(); - s4.setForceFormulaRecalculation(true); - - assertFalse(sheet.getForceFormulaRecalculation()); - assertFalse(sheet2.getForceFormulaRecalculation()); - assertTrue(s4.getForceFormulaRecalculation()); - - fout = new FileOutputStream( tempFile ); - wb2.write( fout ); - fout.close(); - - HSSFWorkbook wb3 = new HSSFWorkbook(new FileInputStream(tempFile)); - assertFalse(wb3.getSheetAt(0).getForceFormulaRecalculation()); - assertFalse(wb3.getSheetAt(1).getForceFormulaRecalculation()); - assertFalse(wb3.getSheetAt(2).getForceFormulaRecalculation()); - assertTrue(wb3.getSheetAt(3).getForceFormulaRecalculation()); - } - - public void testColumnWidth() { - //check we can correctly read column widths from a reference workbook - HSSFWorkbook wb = openSample("colwidth.xls"); - - //reference values - int[] ref = {365, 548, 731, 914, 1097, 1280, 1462, 1645, 1828, 2011, 2194, 2377, 2560, 2742, 2925, 3108, 3291, 3474, 3657}; - - HSSFSheet sh = wb.getSheetAt(0); - for (char i = 'A'; i <= 'S'; i++) { - int idx = i - 'A'; - int w = sh.getColumnWidth((short)idx); - assertEquals(ref[idx], w); - } - - //the second sheet doesn't have overridden column widths - sh = wb.getSheetAt(1); - int def_width = sh.getDefaultColumnWidth(); - for (char i = 'A'; i <= 'S'; i++) { - int idx = i - 'A'; - int w = sh.getColumnWidth((short)idx); - //getDefaultColumnWidth returns width measued in characters - //getColumnWidth returns width measued in 1/256th units - assertEquals(def_width*256, w); - } - - //test new workbook - wb = new HSSFWorkbook(); - sh = wb.createSheet(); - sh.setDefaultColumnWidth((short)10); - assertEquals(10, sh.getDefaultColumnWidth()); - assertEquals(256*10, sh.getColumnWidth((short)0)); - assertEquals(256*10, sh.getColumnWidth((short)1)); - assertEquals(256*10, sh.getColumnWidth((short)2)); - for (char i = 'D'; i <= 'F'; i++) { - short w = (short)(256*12); - sh.setColumnWidth((short)i, w); - assertEquals(w, sh.getColumnWidth((short)i)); - } - - //serialize and read again - wb = HSSFTestDataSamples.writeOutAndReadBack(wb); - - sh = wb.getSheetAt(0); - assertEquals(10, sh.getDefaultColumnWidth()); - //columns A-C have default width - assertEquals(256*10, sh.getColumnWidth((short)0)); - assertEquals(256*10, sh.getColumnWidth((short)1)); - assertEquals(256*10, sh.getColumnWidth((short)2)); - //columns D-F have custom width - for (char i = 'D'; i <= 'F'; i++) { - short w = (short)(256*12); - assertEquals(w, sh.getColumnWidth((short)i)); - } - } - - /** - * Some utilities write Excel files without the ROW records. - * Excel, ooo, and google docs are OK with this. - * Now POI is too. - */ - public void testMissingRowRecords_bug41187() { - HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("ex41187-19267.xls"); - - HSSFSheet sheet = wb.getSheetAt(0); - HSSFRow row = sheet.getRow(0); - if(row == null) { - throw new AssertionFailedError("Identified bug 41187 a"); - } - if (row.getHeight() == 0) { - throw new AssertionFailedError("Identified bug 41187 b"); - } - assertEquals("Hi Excel!", row.getCell(0).getRichStringCellValue().getString()); - // check row height for 'default' flag - assertEquals((short)0xFF, row.getHeight()); - - HSSFTestDataSamples.writeOutAndReadBack(wb); - } + private static HSSFWorkbook openSample(String sampleFileName) { + return HSSFTestDataSamples.openSampleWorkbook(sampleFileName); + } + + /** + * Test the gridset field gets set as expected. + */ + public void testBackupRecord() { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet s = wb.createSheet(); + Sheet sheet = s.getSheet(); + + assertEquals(true, sheet.getGridsetRecord().getGridset()); + s.setGridsPrinted(true); + assertEquals(false, sheet.getGridsetRecord().getGridset()); + } + + /** + * Test vertically centered output. + */ + public void testVerticallyCenter() { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet s = wb.createSheet(); + Sheet sheet = s.getSheet(); + VCenterRecord record = sheet.getPageSettings().getVCenter(); + + assertEquals(false, record.getVCenter()); + s.setVerticallyCenter(true); + assertEquals(true, record.getVCenter()); + + // wb.write(new FileOutputStream("c:\\test.xls")); + } + + /** + * Test horizontally centered output. + */ + public void testHorizontallyCenter() { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet s = wb.createSheet(); + Sheet sheet = s.getSheet(); + HCenterRecord record = sheet.getPageSettings().getHCenter(); + + assertEquals(false, record.getHCenter()); + s.setHorizontallyCenter(true); + assertEquals(true, record.getHCenter()); + } + + + /** + * Test WSBboolRecord fields get set in the user model. + */ + public void testWSBool() { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet s = wb.createSheet(); + Sheet sheet = s.getSheet(); + WSBoolRecord record = + (WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid); + + // Check defaults + assertEquals(true, record.getAlternateExpression()); + assertEquals(true, record.getAlternateFormula()); + assertEquals(false, record.getAutobreaks()); + assertEquals(false, record.getDialog()); + assertEquals(false, record.getDisplayGuts()); + assertEquals(true, record.getFitToPage()); + assertEquals(false, record.getRowSumsBelow()); + assertEquals(false, record.getRowSumsRight()); + + // Alter + s.setAlternativeExpression(false); + s.setAlternativeFormula(false); + s.setAutobreaks(true); + s.setDialog(true); + s.setDisplayGuts(true); + s.setFitToPage(false); + s.setRowSumsBelow(true); + s.setRowSumsRight(true); + + // Check + assertEquals(false, record.getAlternateExpression()); + assertEquals(false, record.getAlternateFormula()); + assertEquals(true, record.getAutobreaks()); + assertEquals(true, record.getDialog()); + assertEquals(true, record.getDisplayGuts()); + assertEquals(false, record.getFitToPage()); + assertEquals(true, record.getRowSumsBelow()); + assertEquals(true, record.getRowSumsRight()); + assertEquals(false, s.getAlternateExpression()); + assertEquals(false, s.getAlternateFormula()); + assertEquals(true, s.getAutobreaks()); + assertEquals(true, s.getDialog()); + assertEquals(true, s.getDisplayGuts()); + assertEquals(false, s.getFitToPage()); + assertEquals(true, s.getRowSumsBelow()); + assertEquals(true, s.getRowSumsRight()); + } + + public void testReadBooleans() { + HSSFWorkbook workbook = new HSSFWorkbook(); + HSSFSheet sheet = workbook.createSheet("Test boolean"); + HSSFRow row = sheet.createRow(2); + HSSFCell cell = row.createCell(9); + cell.setCellValue(true); + cell = row.createCell(11); + cell.setCellValue(true); + + workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook); + + sheet = workbook.getSheetAt(0); + row = sheet.getRow(2); + assertNotNull(row); + assertEquals(2, row.getPhysicalNumberOfCells()); + } + + public void testRemoveRow() { + HSSFWorkbook workbook = new HSSFWorkbook(); + HSSFSheet sheet = workbook.createSheet("Test boolean"); + HSSFRow row = sheet.createRow(2); + sheet.removeRow(row); + } + + public void testRemoveZeroRow() { + HSSFWorkbook workbook = new HSSFWorkbook(); + HSSFSheet sheet = workbook.createSheet("Sheet1"); + HSSFRow row = sheet.createRow(0); + try { + sheet.removeRow(row); + } catch (IllegalArgumentException e) { + if (e.getMessage().equals("Invalid row number (-1) outside allowable range (0..65535)")) { + throw new AssertionFailedError("Identified bug 45367"); + } + throw e; + } + } + + public void testCloneSheet() { + HSSFWorkbook workbook = new HSSFWorkbook(); + HSSFSheet sheet = workbook.createSheet("Test Clone"); + HSSFRow row = sheet.createRow(0); + HSSFCell cell = row.createCell(0); + HSSFCell cell2 = row.createCell(1); + cell.setCellValue(new HSSFRichTextString("clone_test")); + cell2.setCellFormula("sin(1)"); + + HSSFSheet clonedSheet = workbook.cloneSheet(0); + HSSFRow clonedRow = clonedSheet.getRow(0); + + //Check for a good clone + assertEquals(clonedRow.getCell(0).getRichStringCellValue().getString(), "clone_test"); + + //Check that the cells are not somehow linked + cell.setCellValue(new HSSFRichTextString("Difference Check")); + cell2.setCellFormula("cos(2)"); + if ("Difference Check".equals(clonedRow.getCell(0).getRichStringCellValue().getString())) { + fail("string cell not properly cloned"); + } + if ("COS(2)".equals(clonedRow.getCell(1).getCellFormula())) { + fail("formula cell not properly cloned"); + } + assertEquals(clonedRow.getCell(0).getRichStringCellValue().getString(), "clone_test"); + assertEquals(clonedRow.getCell(1).getCellFormula(), "SIN(1)"); + } + + /** tests that the sheet name for multiple clones of the same sheet is unique + * BUG 37416 + */ + public void testCloneSheetMultipleTimes() { + HSSFWorkbook workbook = new HSSFWorkbook(); + HSSFSheet sheet = workbook.createSheet("Test Clone"); + HSSFRow row = sheet.createRow(0); + HSSFCell cell = row.createCell(0); + cell.setCellValue(new HSSFRichTextString("clone_test")); + //Clone the sheet multiple times + workbook.cloneSheet(0); + workbook.cloneSheet(0); + + assertNotNull(workbook.getSheet("Test Clone")); + assertNotNull(workbook.getSheet("Test Clone (2)")); + assertEquals("Test Clone (3)", workbook.getSheetName(2)); + assertNotNull(workbook.getSheet("Test Clone (3)")); + + workbook.removeSheetAt(0); + workbook.removeSheetAt(0); + workbook.removeSheetAt(0); + workbook.createSheet("abc ( 123)"); + workbook.cloneSheet(0); + assertEquals("abc (124)", workbook.getSheetName(1)); + } + + /** + * Setting landscape and portrait stuff on new sheets + */ + public void testPrintSetupLandscapeNew() throws Exception { + HSSFWorkbook workbook = new HSSFWorkbook(); + HSSFSheet sheetL = workbook.createSheet("LandscapeS"); + HSSFSheet sheetP = workbook.createSheet("LandscapeP"); + + // Check two aspects of the print setup + assertFalse(sheetL.getPrintSetup().getLandscape()); + assertFalse(sheetP.getPrintSetup().getLandscape()); + assertEquals(0, sheetL.getPrintSetup().getCopies()); + assertEquals(0, sheetP.getPrintSetup().getCopies()); + + // Change one on each + sheetL.getPrintSetup().setLandscape(true); + sheetP.getPrintSetup().setCopies((short)3); + + // Check taken + assertTrue(sheetL.getPrintSetup().getLandscape()); + assertFalse(sheetP.getPrintSetup().getLandscape()); + assertEquals(0, sheetL.getPrintSetup().getCopies()); + assertEquals(3, sheetP.getPrintSetup().getCopies()); + + // Save and re-load, and check still there + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + workbook.write(baos); + workbook = new HSSFWorkbook(new ByteArrayInputStream(baos.toByteArray())); + + assertTrue(sheetL.getPrintSetup().getLandscape()); + assertFalse(sheetP.getPrintSetup().getLandscape()); + assertEquals(0, sheetL.getPrintSetup().getCopies()); + assertEquals(3, sheetP.getPrintSetup().getCopies()); + } + + /** + * Setting landscape and portrait stuff on existing sheets + */ + public void testPrintSetupLandscapeExisting() { + HSSFWorkbook workbook = openSample("SimpleWithPageBreaks.xls"); + + assertEquals(3, workbook.getNumberOfSheets()); + + HSSFSheet sheetL = workbook.getSheetAt(0); + HSSFSheet sheetPM = workbook.getSheetAt(1); + HSSFSheet sheetLS = workbook.getSheetAt(2); + + // Check two aspects of the print setup + assertFalse(sheetL.getPrintSetup().getLandscape()); + assertTrue(sheetPM.getPrintSetup().getLandscape()); + assertTrue(sheetLS.getPrintSetup().getLandscape()); + assertEquals(1, sheetL.getPrintSetup().getCopies()); + assertEquals(1, sheetPM.getPrintSetup().getCopies()); + assertEquals(1, sheetLS.getPrintSetup().getCopies()); + + // Change one on each + sheetL.getPrintSetup().setLandscape(true); + sheetPM.getPrintSetup().setLandscape(false); + sheetPM.getPrintSetup().setCopies((short)3); + + // Check taken + assertTrue(sheetL.getPrintSetup().getLandscape()); + assertFalse(sheetPM.getPrintSetup().getLandscape()); + assertTrue(sheetLS.getPrintSetup().getLandscape()); + assertEquals(1, sheetL.getPrintSetup().getCopies()); + assertEquals(3, sheetPM.getPrintSetup().getCopies()); + assertEquals(1, sheetLS.getPrintSetup().getCopies()); + + // Save and re-load, and check still there + workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook); + + assertTrue(sheetL.getPrintSetup().getLandscape()); + assertFalse(sheetPM.getPrintSetup().getLandscape()); + assertTrue(sheetLS.getPrintSetup().getLandscape()); + assertEquals(1, sheetL.getPrintSetup().getCopies()); + assertEquals(3, sheetPM.getPrintSetup().getCopies()); + assertEquals(1, sheetLS.getPrintSetup().getCopies()); + } + + public void testGroupRows() { + HSSFWorkbook workbook = new HSSFWorkbook(); + HSSFSheet s = workbook.createSheet(); + HSSFRow r1 = s.createRow(0); + HSSFRow r2 = s.createRow(1); + HSSFRow r3 = s.createRow(2); + HSSFRow r4 = s.createRow(3); + HSSFRow r5 = s.createRow(4); + + assertEquals(0, r1.getOutlineLevel()); + assertEquals(0, r2.getOutlineLevel()); + assertEquals(0, r3.getOutlineLevel()); + assertEquals(0, r4.getOutlineLevel()); + assertEquals(0, r5.getOutlineLevel()); + + s.groupRow(2,3); + + assertEquals(0, r1.getOutlineLevel()); + assertEquals(0, r2.getOutlineLevel()); + assertEquals(1, r3.getOutlineLevel()); + assertEquals(1, r4.getOutlineLevel()); + assertEquals(0, r5.getOutlineLevel()); + + // Save and re-open + workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook); + + s = workbook.getSheetAt(0); + r1 = s.getRow(0); + r2 = s.getRow(1); + r3 = s.getRow(2); + r4 = s.getRow(3); + r5 = s.getRow(4); + + assertEquals(0, r1.getOutlineLevel()); + assertEquals(0, r2.getOutlineLevel()); + assertEquals(1, r3.getOutlineLevel()); + assertEquals(1, r4.getOutlineLevel()); + assertEquals(0, r5.getOutlineLevel()); + } + + public void testGroupRowsExisting() { + HSSFWorkbook workbook = openSample("NoGutsRecords.xls"); + + HSSFSheet s = workbook.getSheetAt(0); + HSSFRow r1 = s.getRow(0); + HSSFRow r2 = s.getRow(1); + HSSFRow r3 = s.getRow(2); + HSSFRow r4 = s.getRow(3); + HSSFRow r5 = s.getRow(4); + HSSFRow r6 = s.getRow(5); + + assertEquals(0, r1.getOutlineLevel()); + assertEquals(0, r2.getOutlineLevel()); + assertEquals(0, r3.getOutlineLevel()); + assertEquals(0, r4.getOutlineLevel()); + assertEquals(0, r5.getOutlineLevel()); + assertEquals(0, r6.getOutlineLevel()); + + // This used to complain about lacking guts records + s.groupRow(2, 4); + + assertEquals(0, r1.getOutlineLevel()); + assertEquals(0, r2.getOutlineLevel()); + assertEquals(1, r3.getOutlineLevel()); + assertEquals(1, r4.getOutlineLevel()); + assertEquals(1, r5.getOutlineLevel()); + assertEquals(0, r6.getOutlineLevel()); + + // Save and re-open + try { + workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook); + } catch (OutOfMemoryError e) { + throw new AssertionFailedError("Identified bug 39903"); + } + + s = workbook.getSheetAt(0); + r1 = s.getRow(0); + r2 = s.getRow(1); + r3 = s.getRow(2); + r4 = s.getRow(3); + r5 = s.getRow(4); + r6 = s.getRow(5); + + assertEquals(0, r1.getOutlineLevel()); + assertEquals(0, r2.getOutlineLevel()); + assertEquals(1, r3.getOutlineLevel()); + assertEquals(1, r4.getOutlineLevel()); + assertEquals(1, r5.getOutlineLevel()); + assertEquals(0, r6.getOutlineLevel()); + } + + public void testGetDrawings() { + HSSFWorkbook wb1c = openSample("WithChart.xls"); + HSSFWorkbook wb2c = openSample("WithTwoCharts.xls"); + + // 1 chart sheet -> data on 1st, chart on 2nd + assertNotNull(wb1c.getSheetAt(0).getDrawingPatriarch()); + assertNotNull(wb1c.getSheetAt(1).getDrawingPatriarch()); + assertFalse(wb1c.getSheetAt(0).getDrawingPatriarch().containsChart()); + assertTrue(wb1c.getSheetAt(1).getDrawingPatriarch().containsChart()); + + // 2 chart sheet -> data on 1st, chart on 2nd+3rd + assertNotNull(wb2c.getSheetAt(0).getDrawingPatriarch()); + assertNotNull(wb2c.getSheetAt(1).getDrawingPatriarch()); + assertNotNull(wb2c.getSheetAt(2).getDrawingPatriarch()); + assertFalse(wb2c.getSheetAt(0).getDrawingPatriarch().containsChart()); + assertTrue(wb2c.getSheetAt(1).getDrawingPatriarch().containsChart()); + assertTrue(wb2c.getSheetAt(2).getDrawingPatriarch().containsChart()); + } + + /** + * Test that the ProtectRecord is included when creating or cloning a sheet + */ + public void testProtect() { + HSSFWorkbook workbook = new HSSFWorkbook(); + HSSFSheet hssfSheet = workbook.createSheet(); + Sheet sheet = hssfSheet.getSheet(); + ProtectRecord protect = sheet.getProtect(); + + assertFalse(protect.getProtect()); + + // This will tell us that cloneSheet, and by extension, + // the list forms of createSheet leave us with an accessible + // ProtectRecord. + hssfSheet.setProtect(true); + Sheet cloned = sheet.cloneSheet(); + assertNotNull(cloned.getProtect()); + assertTrue(hssfSheet.getProtect()); + } + + public void testProtectSheet() { + short expected = (short)0xfef1; + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet s = wb.createSheet(); + s.protectSheet("abcdefghij"); + Sheet sheet = s.getSheet(); + ProtectRecord protect = sheet.getProtect(); + PasswordRecord pass = sheet.getPassword(); + assertTrue("protection should be on",protect.getProtect()); + assertTrue("object protection should be on",sheet.isProtected()[1]); + assertTrue("scenario protection should be on",sheet.isProtected()[2]); + assertEquals("well known value for top secret hash should be "+Integer.toHexString(expected).substring(4),expected,pass.getPassword()); + } + + + public void testZoom() { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet = wb.createSheet(); + assertEquals(-1, sheet.getSheet().findFirstRecordLocBySid(SCLRecord.sid)); + sheet.setZoom(3,4); + assertTrue(sheet.getSheet().findFirstRecordLocBySid(SCLRecord.sid) > 0); + SCLRecord sclRecord = (SCLRecord) sheet.getSheet().findFirstRecordBySid(SCLRecord.sid); + assertEquals(3, sclRecord.getNumerator()); + assertEquals(4, sclRecord.getDenominator()); + + int sclLoc = sheet.getSheet().findFirstRecordLocBySid(SCLRecord.sid); + int window2Loc = sheet.getSheet().findFirstRecordLocBySid(WindowTwoRecord.sid); + assertTrue(sclLoc == window2Loc + 1); + } + + + /** + * When removing one merged region, it would break + * + */ + public void testRemoveMerged() { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet = wb.createSheet(); + CellRangeAddress region = new CellRangeAddress(0, 1, 0, 1); + sheet.addMergedRegion(region); + region = new CellRangeAddress(1, 2, 0, 1); + sheet.addMergedRegion(region); + + sheet.removeMergedRegion(0); + + region = sheet.getMergedRegion(0); + assertEquals("Left over region should be starting at row 1", 1, region.getFirstRow()); + + sheet.removeMergedRegion(0); + + assertEquals("there should be no merged regions left!", 0, sheet.getNumMergedRegions()); + + //an, add, remove, get(0) would null pointer + sheet.addMergedRegion(region); + assertEquals("there should now be one merged region!", 1, sheet.getNumMergedRegions()); + sheet.removeMergedRegion(0); + assertEquals("there should now be zero merged regions!", 0, sheet.getNumMergedRegions()); + //add it again! + region.setLastRow(4); + + sheet.addMergedRegion(region); + assertEquals("there should now be one merged region!", 1, sheet.getNumMergedRegions()); + + //should exist now! + assertTrue("there isn't more than one merged region in there", 1 <= sheet.getNumMergedRegions()); + region = sheet.getMergedRegion(0); + assertEquals("the merged row to doesnt match the one we put in ", 4, region.getLastRow()); + } + + public void testShiftMerged() { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet = wb.createSheet(); + HSSFRow row = sheet.createRow(0); + HSSFCell cell = row.createCell(0); + cell.setCellValue(new HSSFRichTextString("first row, first cell")); + + row = sheet.createRow(1); + cell = row.createCell(1); + cell.setCellValue(new HSSFRichTextString("second row, second cell")); + + CellRangeAddress region = new CellRangeAddress(1, 1, 0, 1); + sheet.addMergedRegion(region); + + sheet.shiftRows(1, 1, 1); + + region = sheet.getMergedRegion(0); + assertEquals("Merged region not moved over to row 2", 2, region.getFirstRow()); + } + + /** + * Tests the display of gridlines, formulas, and rowcolheadings. + * @author Shawn Laubach (slaubach at apache dot org) + */ + public void testDisplayOptions() { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet = wb.createSheet(); + + wb = HSSFTestDataSamples.writeOutAndReadBack(wb); + sheet = wb.getSheetAt(0); + + assertEquals(sheet.isDisplayGridlines(), true); + assertEquals(sheet.isDisplayRowColHeadings(), true); + assertEquals(sheet.isDisplayFormulas(), false); + + sheet.setDisplayGridlines(false); + sheet.setDisplayRowColHeadings(false); + sheet.setDisplayFormulas(true); + + wb = HSSFTestDataSamples.writeOutAndReadBack(wb); + sheet = wb.getSheetAt(0); + + assertEquals(sheet.isDisplayGridlines(), false); + assertEquals(sheet.isDisplayRowColHeadings(), false); + assertEquals(sheet.isDisplayFormulas(), true); + } + + + /** + * Make sure the excel file loads work + * + */ + public void testPageBreakFiles() { + HSSFWorkbook wb = openSample("SimpleWithPageBreaks.xls"); + + HSSFSheet sheet = wb.getSheetAt(0); + assertNotNull(sheet); + + assertEquals("1 row page break", 1, sheet.getRowBreaks().length); + assertEquals("1 column page break", 1, sheet.getColumnBreaks().length); + + assertTrue("No row page break", sheet.isRowBroken(22)); + assertTrue("No column page break", sheet.isColumnBroken((short)4)); + + sheet.setRowBreak(10); + sheet.setColumnBreak((short)13); + + assertEquals("row breaks number", 2, sheet.getRowBreaks().length); + assertEquals("column breaks number", 2, sheet.getColumnBreaks().length); + + wb = HSSFTestDataSamples.writeOutAndReadBack(wb); + sheet = wb.getSheetAt(0); + + assertTrue("No row page break", sheet.isRowBroken(22)); + assertTrue("No column page break", sheet.isColumnBroken((short)4)); + + assertEquals("row breaks number", 2, sheet.getRowBreaks().length); + assertEquals("column breaks number", 2, sheet.getColumnBreaks().length); + } + + public void testDBCSName () { + HSSFWorkbook wb = openSample("DBCSSheetName.xls"); + wb.getSheetAt(1); + assertEquals ("DBCS Sheet Name 2", wb.getSheetName(1),"\u090f\u0915" ); + assertEquals("DBCS Sheet Name 1", wb.getSheetName(0),"\u091c\u093e"); + } + + /** + * Testing newly added method that exposes the WINDOW2.toprow + * parameter to allow setting the toprow in the visible view + * of the sheet when it is first opened. + */ + public void testTopRow() { + HSSFWorkbook wb = openSample("SimpleWithPageBreaks.xls"); + + HSSFSheet sheet = wb.getSheetAt(0); + assertNotNull(sheet); + + short toprow = (short) 100; + short leftcol = (short) 50; + sheet.showInPane(toprow,leftcol); + assertEquals("HSSFSheet.getTopRow()", toprow, sheet.getTopRow()); + assertEquals("HSSFSheet.getLeftCol()", leftcol, sheet.getLeftCol()); + } + + /** cell with formula becomes null on cloning a sheet*/ + public void test35084() { + + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet s = wb.createSheet("Sheet1"); + HSSFRow r = s.createRow(0); + r.createCell(0).setCellValue(1); + r.createCell(1).setCellFormula("A1*2"); + HSSFSheet s1 = wb.cloneSheet(0); + r = s1.getRow(0); + assertEquals("double", r.getCell(0).getNumericCellValue(), 1, 0); // sanity check + assertNotNull(r.getCell(1)); + assertEquals("formula", r.getCell(1).getCellFormula(), "A1*2"); + } + + /** test that new default column styles get applied */ + public void testDefaultColumnStyle() { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFCellStyle style = wb.createCellStyle(); + HSSFSheet s = wb.createSheet(); + s.setDefaultColumnStyle((short) 0, style); + HSSFRow r = s.createRow(0); + HSSFCell c = r.createCell(0); + assertEquals("style should match", style.getIndex(), c.getCellStyle().getIndex()); + } + + + /** + * + */ + public void testAddEmptyRow() { + //try to add 5 empty rows to a new sheet + HSSFWorkbook workbook = new HSSFWorkbook(); + HSSFSheet sheet = workbook.createSheet(); + for (int i = 0; i < 5; i++) { + sheet.createRow(i); + } + + workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook); + + //try adding empty rows in an existing worksheet + workbook = openSample("Simple.xls"); + + sheet = workbook.getSheetAt(0); + for (int i = 3; i < 10; i++) sheet.createRow(i); + + workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook); + } + + public void testAutoSizeColumn() { + HSSFWorkbook wb = openSample("43902.xls"); + String sheetName = "my sheet"; + HSSFSheet sheet = wb.getSheet(sheetName); + + // Can't use literal numbers for column sizes, as + // will come out with different values on different + // machines based on the fonts available. + // So, we use ranges, which are pretty large, but + // thankfully don't overlap! + int minWithRow1And2 = 6400; + int maxWithRow1And2 = 7800; + int minWithRow1Only = 2750; + int maxWithRow1Only = 3300; + + // autoSize the first column and check its size before the merged region (1,0,1,1) is set: + // it has to be based on the 2nd row width + sheet.autoSizeColumn((short)0); + assertTrue("Column autosized with only one row: wrong width", sheet.getColumnWidth((short)0) >= minWithRow1And2); + assertTrue("Column autosized with only one row: wrong width", sheet.getColumnWidth((short)0) <= maxWithRow1And2); + + //create a region over the 2nd row and auto size the first column + sheet.addMergedRegion(new CellRangeAddress(1,1,0,1)); + sheet.autoSizeColumn((short)0); + HSSFWorkbook wb2 = HSSFTestDataSamples.writeOutAndReadBack(wb); + + // check that the autoSized column width has ignored the 2nd row + // because it is included in a merged region (Excel like behavior) + HSSFSheet sheet2 = wb2.getSheet(sheetName); + assertTrue(sheet2.getColumnWidth((short)0) >= minWithRow1Only); + assertTrue(sheet2.getColumnWidth((short)0) <= maxWithRow1Only); + + // remove the 2nd row merged region and check that the 2nd row value is used to the autoSizeColumn width + sheet2.removeMergedRegion(1); + sheet2.autoSizeColumn((short)0); + HSSFWorkbook wb3 = HSSFTestDataSamples.writeOutAndReadBack(wb2); + HSSFSheet sheet3 = wb3.getSheet(sheetName); + assertTrue(sheet3.getColumnWidth((short)0) >= minWithRow1And2); + assertTrue(sheet3.getColumnWidth((short)0) <= maxWithRow1And2); + } + + /** + * Setting ForceFormulaRecalculation on sheets + */ + public void testForceRecalculation() throws Exception { + HSSFWorkbook workbook = openSample("UncalcedRecord.xls"); + + HSSFSheet sheet = workbook.getSheetAt(0); + HSSFSheet sheet2 = workbook.getSheetAt(0); + HSSFRow row = sheet.getRow(0); + row.createCell(0).setCellValue(5); + row.createCell(1).setCellValue(8); + assertFalse(sheet.getForceFormulaRecalculation()); + assertFalse(sheet2.getForceFormulaRecalculation()); + + // Save and manually verify that on column C we have 0, value in template + File tempFile = new File(System.getProperty("java.io.tmpdir")+"/uncalced_err.xls" ); + tempFile.delete(); + FileOutputStream fout = new FileOutputStream( tempFile ); + workbook.write( fout ); + fout.close(); + sheet.setForceFormulaRecalculation(true); + assertTrue(sheet.getForceFormulaRecalculation()); + + // Save and manually verify that on column C we have now 13, calculated value + tempFile = new File(System.getProperty("java.io.tmpdir")+"/uncalced_succ.xls" ); + tempFile.delete(); + fout = new FileOutputStream( tempFile ); + workbook.write( fout ); + fout.close(); + + // Try it can be opened + HSSFWorkbook wb2 = new HSSFWorkbook(new FileInputStream(tempFile)); + + // And check correct sheet settings found + sheet = wb2.getSheetAt(0); + sheet2 = wb2.getSheetAt(1); + assertTrue(sheet.getForceFormulaRecalculation()); + assertFalse(sheet2.getForceFormulaRecalculation()); + + // Now turn if back off again + sheet.setForceFormulaRecalculation(false); + + fout = new FileOutputStream( tempFile ); + wb2.write( fout ); + fout.close(); + wb2 = new HSSFWorkbook(new FileInputStream(tempFile)); + + assertFalse(wb2.getSheetAt(0).getForceFormulaRecalculation()); + assertFalse(wb2.getSheetAt(1).getForceFormulaRecalculation()); + assertFalse(wb2.getSheetAt(2).getForceFormulaRecalculation()); + + // Now add a new sheet, and check things work + // with old ones unset, new one set + HSSFSheet s4 = wb2.createSheet(); + s4.setForceFormulaRecalculation(true); + + assertFalse(sheet.getForceFormulaRecalculation()); + assertFalse(sheet2.getForceFormulaRecalculation()); + assertTrue(s4.getForceFormulaRecalculation()); + + fout = new FileOutputStream( tempFile ); + wb2.write( fout ); + fout.close(); + + HSSFWorkbook wb3 = new HSSFWorkbook(new FileInputStream(tempFile)); + assertFalse(wb3.getSheetAt(0).getForceFormulaRecalculation()); + assertFalse(wb3.getSheetAt(1).getForceFormulaRecalculation()); + assertFalse(wb3.getSheetAt(2).getForceFormulaRecalculation()); + assertTrue(wb3.getSheetAt(3).getForceFormulaRecalculation()); + } + + public void testColumnWidth() { + //check we can correctly read column widths from a reference workbook + HSSFWorkbook wb = openSample("colwidth.xls"); + + //reference values + int[] ref = {365, 548, 731, 914, 1097, 1280, 1462, 1645, 1828, 2011, 2194, 2377, 2560, 2742, 2925, 3108, 3291, 3474, 3657}; + + HSSFSheet sh = wb.getSheetAt(0); + for (char i = 'A'; i <= 'S'; i++) { + int idx = i - 'A'; + int w = sh.getColumnWidth((short)idx); + assertEquals(ref[idx], w); + } + + //the second sheet doesn't have overridden column widths + sh = wb.getSheetAt(1); + int def_width = sh.getDefaultColumnWidth(); + for (char i = 'A'; i <= 'S'; i++) { + int idx = i - 'A'; + int w = sh.getColumnWidth((short)idx); + //getDefaultColumnWidth returns width measued in characters + //getColumnWidth returns width measued in 1/256th units + assertEquals(def_width*256, w); + } + + //test new workbook + wb = new HSSFWorkbook(); + sh = wb.createSheet(); + sh.setDefaultColumnWidth((short)10); + assertEquals(10, sh.getDefaultColumnWidth()); + assertEquals(256*10, sh.getColumnWidth((short)0)); + assertEquals(256*10, sh.getColumnWidth((short)1)); + assertEquals(256*10, sh.getColumnWidth((short)2)); + for (char i = 'D'; i <= 'F'; i++) { + short w = (short)(256*12); + sh.setColumnWidth((short)i, w); + assertEquals(w, sh.getColumnWidth((short)i)); + } + + //serialize and read again + wb = HSSFTestDataSamples.writeOutAndReadBack(wb); + + sh = wb.getSheetAt(0); + assertEquals(10, sh.getDefaultColumnWidth()); + //columns A-C have default width + assertEquals(256*10, sh.getColumnWidth((short)0)); + assertEquals(256*10, sh.getColumnWidth((short)1)); + assertEquals(256*10, sh.getColumnWidth((short)2)); + //columns D-F have custom width + for (char i = 'D'; i <= 'F'; i++) { + short w = (short)(256*12); + assertEquals(w, sh.getColumnWidth((short)i)); + } + } + + /** + * Some utilities write Excel files without the ROW records. + * Excel, ooo, and google docs are OK with this. + * Now POI is too. + */ + public void testMissingRowRecords_bug41187() { + HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("ex41187-19267.xls"); + + HSSFSheet sheet = wb.getSheetAt(0); + HSSFRow row = sheet.getRow(0); + if(row == null) { + throw new AssertionFailedError("Identified bug 41187 a"); + } + if (row.getHeight() == 0) { + throw new AssertionFailedError("Identified bug 41187 b"); + } + assertEquals("Hi Excel!", row.getCell(0).getRichStringCellValue().getString()); + // check row height for 'default' flag + assertEquals((short)0xFF, row.getHeight()); + + HSSFTestDataSamples.writeOutAndReadBack(wb); + } + + /** + * If we clone a sheet containing drawings, + * we must allocate a new ID of the drawing group and re-create shape IDs + * + * See bug #45720. + */ + public void testCloneSheetWithDrawings() { + HSSFWorkbook wb1 = HSSFTestDataSamples.openSampleWorkbook("45720.xls"); + + HSSFSheet sheet1 = wb1.getSheetAt(0); + + wb1.getWorkbook().findDrawingGroup(); + DrawingManager2 dm1 = wb1.getWorkbook().getDrawingManager(); + + wb1.cloneSheet(0); + + HSSFWorkbook wb2 = HSSFTestDataSamples.writeOutAndReadBack(wb1); + wb2.getWorkbook().findDrawingGroup(); + DrawingManager2 dm2 = wb2.getWorkbook().getDrawingManager(); + + //check EscherDggRecord - a workbook-level registry of drawing objects + assertEquals(dm1.getDgg().getMaxDrawingGroupId() + 1, dm2.getDgg().getMaxDrawingGroupId()); + + HSSFSheet sheet2 = wb2.getSheetAt(1); + + //check that id of the drawing group was updated + EscherDgRecord dg1 = (EscherDgRecord)sheet1.getDrawingEscherAggregate().findFirstWithId(EscherDgRecord.RECORD_ID); + EscherDgRecord dg2 = (EscherDgRecord)sheet2.getDrawingEscherAggregate().findFirstWithId(EscherDgRecord.RECORD_ID); + int dg_id_1 = dg1.getOptions() >> 4; + int dg_id_2 = dg2.getOptions() >> 4; + assertEquals(dg_id_1 + 1, dg_id_2); + + //TODO: check shapeId in the cloned sheet + } } diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFWorkbook.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFWorkbook.java index 658a3f488..1717aeff6 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFWorkbook.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFWorkbook.java @@ -429,9 +429,9 @@ public final class TestHSSFWorkbook extends TestCase { assertEquals("On2", nr.getNameText()); assertEquals(0, nr.getSheetNumber()); assertEquals(1, nr.getExternSheetNumber()); - assertEquals(1, nr.getNameDefinition().size()); + assertEquals(1, nr.getNameDefinition().length); - ptg = (Area3DPtg)nr.getNameDefinition().get(0); + ptg = (Area3DPtg)nr.getNameDefinition()[0]; assertEquals(1, ptg.getExternSheetIndex()); assertEquals(0, ptg.getFirstColumn()); assertEquals(0, ptg.getFirstRow()); @@ -452,9 +452,9 @@ public final class TestHSSFWorkbook extends TestCase { assertEquals("OnOne", nr.getNameText()); assertEquals(0, nr.getSheetNumber()); assertEquals(0, nr.getExternSheetNumber()); - assertEquals(1, nr.getNameDefinition().size()); + assertEquals(1, nr.getNameDefinition().length); - ptg = (Area3DPtg)nr.getNameDefinition().get(0); + ptg = (Area3DPtg)nr.getNameDefinition()[0]; assertEquals(0, ptg.getExternSheetIndex()); assertEquals(0, ptg.getFirstColumn()); assertEquals(2, ptg.getFirstRow()); @@ -475,9 +475,9 @@ public final class TestHSSFWorkbook extends TestCase { assertEquals("OnSheet3", nr.getNameText()); assertEquals(0, nr.getSheetNumber()); assertEquals(2, nr.getExternSheetNumber()); - assertEquals(1, nr.getNameDefinition().size()); + assertEquals(1, nr.getNameDefinition().length); - ptg = (Area3DPtg)nr.getNameDefinition().get(0); + ptg = (Area3DPtg)nr.getNameDefinition()[0]; assertEquals(2, ptg.getExternSheetIndex()); assertEquals(0, ptg.getFirstColumn()); assertEquals(0, ptg.getFirstRow()); diff --git a/src/testcases/org/apache/poi/hssf/util/TestAreaReference.java b/src/testcases/org/apache/poi/hssf/util/TestAreaReference.java index a5dbb096d..c9356b852 100644 --- a/src/testcases/org/apache/poi/hssf/util/TestAreaReference.java +++ b/src/testcases/org/apache/poi/hssf/util/TestAreaReference.java @@ -19,7 +19,6 @@ package org.apache.poi.hssf.util; import java.io.IOException; import java.io.InputStream; -import java.util.List; import junit.framework.TestCase; @@ -28,6 +27,7 @@ import org.apache.poi.hssf.model.Workbook; import org.apache.poi.hssf.record.NameRecord; import org.apache.poi.hssf.record.formula.Area3DPtg; import org.apache.poi.hssf.record.formula.MemFuncPtg; +import org.apache.poi.hssf.record.formula.Ptg; import org.apache.poi.hssf.record.formula.UnionPtg; import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFName; @@ -85,7 +85,7 @@ public final class TestAreaReference extends TestCase { public void testReferenceWithSheet() { AreaReference ar; - ar = new AreaReference("Tabelle1!B5"); + ar = new AreaReference("Tabelle1!B5:B5"); assertTrue(ar.isSingleCell()); TestCellReference.confirmCell(ar.getFirstCell(), "Tabelle1", 4, 1, false, false, "Tabelle1!B5"); @@ -115,11 +115,11 @@ public final class TestAreaReference extends TestCase { } } - public void testContiguousReferences() throws Exception { - String refSimple = "$C$10"; + public void testContiguousReferences() { + String refSimple = "$C$10:$C$10"; String ref2D = "$C$10:$D$11"; - String refDCSimple = "$C$10,$D$12,$E$14"; - String refDC2D = "$C$10:$C$11,$D$12,$E$14:$E$20"; + String refDCSimple = "$C$10:$C$10,$D$12:$D$12,$E$14:$E$14"; + String refDC2D = "$C$10:$C$11,$D$12:$D$12,$E$14:$E$20"; String refDC3D = "Tabelle1!$C$10:$C$14,Tabelle1!$D$10:$D$12"; // Check that we detect as contiguous properly @@ -206,13 +206,13 @@ public final class TestAreaReference extends TestCase { assertNotNull(nr); assertEquals("test", nr.getNameText()); - List def =nr.getNameDefinition(); - assertEquals(4, def.size()); + Ptg[] def =nr.getNameDefinition(); + assertEquals(4, def.length); - MemFuncPtg ptgA = (MemFuncPtg)def.get(0); - Area3DPtg ptgB = (Area3DPtg)def.get(1); - Area3DPtg ptgC = (Area3DPtg)def.get(2); - UnionPtg ptgD = (UnionPtg)def.get(3); + MemFuncPtg ptgA = (MemFuncPtg)def[0]; + Area3DPtg ptgB = (Area3DPtg)def[1]; + Area3DPtg ptgC = (Area3DPtg)def[2]; + UnionPtg ptgD = (UnionPtg)def[3]; assertEquals("", ptgA.toFormulaString(wb)); assertEquals(refA, ptgB.toFormulaString(wb)); assertEquals(refB, ptgC.toFormulaString(wb)); @@ -245,16 +245,16 @@ public final class TestAreaReference extends TestCase { private static void confirmResolveCellRef(HSSFWorkbook wb, CellReference cref) { HSSFSheet s = wb.getSheet(cref.getSheetName()); HSSFRow r = s.getRow(cref.getRow()); - HSSFCell c = r.getCell(cref.getCol()); + HSSFCell c = r.getCell((int)cref.getCol()); assertNotNull(c); } public void testSpecialSheetNames() { AreaReference ar; - ar = new AreaReference("'Sheet A'!A1"); + ar = new AreaReference("'Sheet A'!A1:A1"); confirmAreaSheetName(ar, "Sheet A", "'Sheet A'!A1"); - ar = new AreaReference("'Hey! Look Here!'!A1"); + ar = new AreaReference("'Hey! Look Here!'!A1:A1"); confirmAreaSheetName(ar, "Hey! Look Here!", "'Hey! Look Here!'!A1"); ar = new AreaReference("'O''Toole'!A1:B2"); @@ -270,7 +270,24 @@ public final class TestAreaReference extends TestCase { assertEquals(expectedFullText, ar.formatAsString()); } - public static void main(String[] args) { - junit.textui.TestRunner.run(TestAreaReference.class); - } + public void testWholeColumnRefs() { + confirmWholeColumnRef("A:A", 0, 0, false, false); + confirmWholeColumnRef("$C:D", 2, 3, true, false); + confirmWholeColumnRef("AD:$AE", 29, 30, false, true); + + } + + private static void confirmWholeColumnRef(String ref, int firstCol, int lastCol, boolean firstIsAbs, boolean lastIsAbs) { + AreaReference ar = new AreaReference(ref); + confirmCell(ar.getFirstCell(), 0, firstCol, true, firstIsAbs); + confirmCell(ar.getLastCell(), 0xFFFF, lastCol, true, lastIsAbs); + } + + private static void confirmCell(CellReference cell, int row, int col, boolean isRowAbs, + boolean isColAbs) { + assertEquals(row, cell.getRow()); + assertEquals(col, cell.getCol()); + assertEquals(isRowAbs, cell.isRowAbsolute()); + assertEquals(isColAbs, cell.isColAbsolute()); + } }