You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

FormulaParser.java 41KB

Merged revisions 693591,693639,693658,693939,693941,693947,693990,694050,694065,694153,694534,694615,694619-694620,694631,694643,694877,694881 via svnmerge from https://svn.apache.org/repos/asf/poi/trunk ........ r693591 | josh | 2008-09-09 21:25:16 +0100 (Tue, 09 Sep 2008) | 1 line Added support for parsing array constants in formulas. (Helping investigation for bug 45752) ........ r693639 | josh | 2008-09-09 23:26:28 +0100 (Tue, 09 Sep 2008) | 1 line removed debug code accidentally submitted with r693591 ........ r693658 | josh | 2008-09-10 00:46:46 +0100 (Wed, 10 Sep 2008) | 2 lines Fixed special cases of INDEX function (single columns / single rows, and errors) ........ r693939 | josh | 2008-09-10 20:23:43 +0100 (Wed, 10 Sep 2008) | 1 line Fixing error value handling for numeric functions. Refactored hierarchy. ........ r693941 | josh | 2008-09-10 20:27:24 +0100 (Wed, 10 Sep 2008) | 1 line (Should have been submitted with 693939) Fixing error value handling for numeric functions. Refactored hierarchy. ........ r693947 | josh | 2008-09-10 20:33:58 +0100 (Wed, 10 Sep 2008) | 1 line (Should have been submitted with 693939) Fixing error value handling for numeric functions. Refactored hierarchy. ........ r693990 | josh | 2008-09-10 22:21:28 +0100 (Wed, 10 Sep 2008) | 1 line Refactored hierarchy of MultiOperandNumericFunction. Fixed error value handling. Enabled error value check in TestFormulasFromSpreadsheet ........ r694050 | josh | 2008-09-10 23:43:30 +0100 (Wed, 10 Sep 2008) | 1 line Refactored finance functions. ........ r694065 | josh | 2008-09-11 00:37:22 +0100 (Thu, 11 Sep 2008) | 1 line fixed special cases of MODE function ........ r694153 | josh | 2008-09-11 08:16:20 +0100 (Thu, 11 Sep 2008) | 1 line Refactoring MultiOperandNumericFunction - removed Ref2DEval. ........ r694534 | josh | 2008-09-12 00:18:50 +0100 (Fri, 12 Sep 2008) | 1 line Fix for bug 45639 - cleaned up index logic inside ColumnInfoRecordsAggregate ........ r694615 | josh | 2008-09-12 07:14:07 +0100 (Fri, 12 Sep 2008) | 1 line small tweak to unit test which was silently creating UnknownPtgs ........ r694619 | josh | 2008-09-12 07:58:52 +0100 (Fri, 12 Sep 2008) | 1 line Removed trailing comma from output of HexDump.toHex() ........ r694620 | josh | 2008-09-12 08:03:00 +0100 (Fri, 12 Sep 2008) | 1 line clarification of ArrayPtg size increment ........ r694631 | josh | 2008-09-12 08:43:20 +0100 (Fri, 12 Sep 2008) | 1 line Extended support for cached results of formula cells ........ r694643 | josh | 2008-09-12 09:18:54 +0100 (Fri, 12 Sep 2008) | 2 lines Made HSSFFormulaEvaluator no longer require initialisation with sheet or row. ........ r694877 | josh | 2008-09-13 06:14:26 +0100 (Sat, 13 Sep 2008) | 1 line Refactored TextFunctions. Some minor fixes - test cases added. ........ r694881 | josh | 2008-09-13 06:43:41 +0100 (Sat, 13 Sep 2008) | 1 line Added toString methods formatAsString to CellValue. Changed deprecation on CellValue.getRichTextStringValue ........ git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@694947 13f79535-47bb-0310-9956-ffa450edef68
16 years ago
Merged revisions 612484-612511,612513-612519,612521-613394,613397-613399,613402-614210,614212-614273,614275-614869,614871-614877,614879-614908,614910-615189,615191-615254,615256-615258,615260-615309,615311-615314,615316-615609,615611-615768,615770-615858,615860-617155,617157-617166,617168-617482,617484-617486,617488-617490,617492-617515,617517-617522,617524-617554,617556-617833,617835-618229,618231-618234,618236-618327,618329-618679,618681-618689,618691,618693-618939,618941-619000,619002-619309,619311-619381,619383-619501,619503-619508,619510-619847,619850,619852-619967,619969-620340,620342-620556,620558-620581,620583-627778,627780-627787,627789-627998,628000-628026,628028,628030-628032,628034,628036-628043,628045-628064,628066-628713,628715-629551,629553-629737,629739-629741,629743-629754,629756-629820,629822-629828,629830,629833-629836,629838-629848,629850-629864,629866-630159,630161-630163,630165-633113,633115-633117,633119-633125,633127-633150,633152-633168,633170-633204,633206-633504,633506-633546,633549-634317,634319-634370,634373-634616,634618,634620-634629,634631-636750,636752-636755,636757-636785,636787-636789,636791-637188,637190-637592,637594,637596-637597,637599,637602-637869,637871-638784,638786-638802,638805-638811,638813-638814,638816-639230,639233-639241,639243-639253,639255-639486,639488-639601,639603-639835,639837-640056,640058-642557 via svnmerge from https://svn.apache.org/repos/asf/poi/trunk ........ r639918 | josh | 2008-03-21 23:47:51 +0000 (Fri, 21 Mar 2008) | 1 line sort to int conversion sign extension fix ........ r640711 | josh | 2008-03-25 06:18:33 +0000 (Tue, 25 Mar 2008) | 1 line Added class javadoc. Patch 30311 from Dmtriy. ........ r641157 | josh | 2008-03-26 05:29:08 +0000 (Wed, 26 Mar 2008) | 1 line more javadoc + clean-up from Dmitriy (bug 30311 att 21711) ........ r641185 | josh | 2008-03-26 07:32:28 +0000 (Wed, 26 Mar 2008) | 1 line patch 44675 - made POI capable of recognising var-args functions. Some related fixes. ........ r641796 | nick | 2008-03-27 12:48:55 +0000 (Thu, 27 Mar 2008) | 1 line Patch from Raghu from bug #44652 - Improved handling of Pictures in Word Documents ........ r641799 | nick | 2008-03-27 12:54:32 +0000 (Thu, 27 Mar 2008) | 1 line Fix typo, and point hwpf people at the new microsoft docs ........ r641934 | nick | 2008-03-27 18:24:39 +0000 (Thu, 27 Mar 2008) | 1 line Add failing (but disabled) test from bug #44691 ........ r641964 | josh | 2008-03-27 20:03:29 +0000 (Thu, 27 Mar 2008) | 1 line fixes for ExternalNameRecord serialisation bug #44691 ........ r641967 | josh | 2008-03-27 20:15:13 +0000 (Thu, 27 Mar 2008) | 1 line annotated previous changelist(641964) with wrong buzilla number. Previous changes were for bug #44695. ........ r641996 | josh | 2008-03-27 21:54:21 +0000 (Thu, 27 Mar 2008) | 1 line (real) fix for bug #44691. Modified Pmt.java to allow for 3 arguments. Added TestPmt junit. ........ r642231 | nick | 2008-03-28 13:35:37 +0000 (Fri, 28 Mar 2008) | 1 line Add test to show that bug #44693 is incorrect ........ git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@642560 13f79535-47bb-0310-9956-ffa450edef68
16 years ago
Merged revisions 693591,693639,693658,693939,693941,693947,693990,694050,694065,694153,694534,694615,694619-694620,694631,694643,694877,694881 via svnmerge from https://svn.apache.org/repos/asf/poi/trunk ........ r693591 | josh | 2008-09-09 21:25:16 +0100 (Tue, 09 Sep 2008) | 1 line Added support for parsing array constants in formulas. (Helping investigation for bug 45752) ........ r693639 | josh | 2008-09-09 23:26:28 +0100 (Tue, 09 Sep 2008) | 1 line removed debug code accidentally submitted with r693591 ........ r693658 | josh | 2008-09-10 00:46:46 +0100 (Wed, 10 Sep 2008) | 2 lines Fixed special cases of INDEX function (single columns / single rows, and errors) ........ r693939 | josh | 2008-09-10 20:23:43 +0100 (Wed, 10 Sep 2008) | 1 line Fixing error value handling for numeric functions. Refactored hierarchy. ........ r693941 | josh | 2008-09-10 20:27:24 +0100 (Wed, 10 Sep 2008) | 1 line (Should have been submitted with 693939) Fixing error value handling for numeric functions. Refactored hierarchy. ........ r693947 | josh | 2008-09-10 20:33:58 +0100 (Wed, 10 Sep 2008) | 1 line (Should have been submitted with 693939) Fixing error value handling for numeric functions. Refactored hierarchy. ........ r693990 | josh | 2008-09-10 22:21:28 +0100 (Wed, 10 Sep 2008) | 1 line Refactored hierarchy of MultiOperandNumericFunction. Fixed error value handling. Enabled error value check in TestFormulasFromSpreadsheet ........ r694050 | josh | 2008-09-10 23:43:30 +0100 (Wed, 10 Sep 2008) | 1 line Refactored finance functions. ........ r694065 | josh | 2008-09-11 00:37:22 +0100 (Thu, 11 Sep 2008) | 1 line fixed special cases of MODE function ........ r694153 | josh | 2008-09-11 08:16:20 +0100 (Thu, 11 Sep 2008) | 1 line Refactoring MultiOperandNumericFunction - removed Ref2DEval. ........ r694534 | josh | 2008-09-12 00:18:50 +0100 (Fri, 12 Sep 2008) | 1 line Fix for bug 45639 - cleaned up index logic inside ColumnInfoRecordsAggregate ........ r694615 | josh | 2008-09-12 07:14:07 +0100 (Fri, 12 Sep 2008) | 1 line small tweak to unit test which was silently creating UnknownPtgs ........ r694619 | josh | 2008-09-12 07:58:52 +0100 (Fri, 12 Sep 2008) | 1 line Removed trailing comma from output of HexDump.toHex() ........ r694620 | josh | 2008-09-12 08:03:00 +0100 (Fri, 12 Sep 2008) | 1 line clarification of ArrayPtg size increment ........ r694631 | josh | 2008-09-12 08:43:20 +0100 (Fri, 12 Sep 2008) | 1 line Extended support for cached results of formula cells ........ r694643 | josh | 2008-09-12 09:18:54 +0100 (Fri, 12 Sep 2008) | 2 lines Made HSSFFormulaEvaluator no longer require initialisation with sheet or row. ........ r694877 | josh | 2008-09-13 06:14:26 +0100 (Sat, 13 Sep 2008) | 1 line Refactored TextFunctions. Some minor fixes - test cases added. ........ r694881 | josh | 2008-09-13 06:43:41 +0100 (Sat, 13 Sep 2008) | 1 line Added toString methods formatAsString to CellValue. Changed deprecation on CellValue.getRichTextStringValue ........ git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@694947 13f79535-47bb-0310-9956-ffa450edef68
16 years ago
Merged revisions 627779-634630 via svnmerge from https://svn.apache.org/repos/asf/poi/trunk ........ r627779 | nick | 2008-02-14 16:32:49 +0100 (Thu, 14 Feb 2008) | 1 line In the interests of sanity, stop having hssf test data files in scratchpad and main, go to just having them in main ........ r627788 | nick | 2008-02-14 17:01:10 +0100 (Thu, 14 Feb 2008) | 1 line Big formula update from Josh from bug #44364 - support for Match, NA and SumProduct functions, and initial error support in functions ........ r627999 | nick | 2008-02-15 11:30:10 +0100 (Fri, 15 Feb 2008) | 1 line To avoid confusion and repeated changes in svn, update the TestDataValidation test to output its file (that needs opening in excel to check to output) into the system tmp directory ........ r628027 | nick | 2008-02-15 12:45:13 +0100 (Fri, 15 Feb 2008) | 1 line Fix for bug #44403 - Have mid use the third argument properly, and test ........ r628029 | nick | 2008-02-15 12:53:25 +0100 (Fri, 15 Feb 2008) | 1 line Fix for bug #44413 from Josh - Fix for circular references in INDEX, OFFSET, VLOOKUP formulas, where a cell is actually allowed to reference itself ........ r628033 | nick | 2008-02-15 13:04:42 +0100 (Fri, 15 Feb 2008) | 1 line Fix from Josh from bug #44417 - Improved handling of references for the need to quote the sheet name for some formulas, but not when fetching a sheet by name ........ r628035 | nick | 2008-02-15 13:13:25 +0100 (Fri, 15 Feb 2008) | 1 line Fix from Josh from bug #44421 - Update Match function to properly support Area references ........ r628044 | nick | 2008-02-15 13:59:40 +0100 (Fri, 15 Feb 2008) | 1 line Partial fix for bug #44410 - support whole column ranges such as C:C in the formula evaluator (so SUM(D:D) will now work). However, the formula string will still be displayed wrong ........ r628065 | nick | 2008-02-15 14:50:38 +0100 (Fri, 15 Feb 2008) | 1 line Further support for whole-column references, including formula strings and the evaluator. Also has some new tests for it ........ r628714 | nick | 2008-02-18 14:08:16 +0100 (Mon, 18 Feb 2008) | 1 line Update notice for latest guidance on ooxml xsd licence, and update getting involved to link to the newly released binary file format docs ........ r629552 | nick | 2008-02-20 19:14:30 +0100 (Wed, 20 Feb 2008) | 1 line Patch from Josh from bug #44403 - Further support for unusual, but valid, arguments to the Mid function ........ r629738 | nick | 2008-02-21 11:36:08 +0100 (Thu, 21 Feb 2008) | 1 line Fix from Josh from bug #44456 - Update contrib SViewer to not fail if a HSSFRow is null ........ r629742 | nick | 2008-02-21 11:49:25 +0100 (Thu, 21 Feb 2008) | 1 line Use the right way to figure out how many rows on a sheet, so we display the row number for all of them on the left hand side. Also, tidy up some imports ........ r629755 | nick | 2008-02-21 12:34:25 +0100 (Thu, 21 Feb 2008) | 1 line Fix bug 38921, where HSSFPalette.findSimilar() wasn't working properly, and add tests for it ........ r629821 | nick | 2008-02-21 16:08:44 +0100 (Thu, 21 Feb 2008) | 1 line Patch from Josh from bug #44371 - support for OFFSET function, and various tweaks to the formula evaluator to support this ........ r629829 | nick | 2008-02-21 16:35:59 +0100 (Thu, 21 Feb 2008) | 1 line Patch from Josh from bug #44366 - InputStreams passed to POIFSFileSystem are now automatically closed. A warning is generated for people who might've relied on them not being closed before, and a wrapper to restore the old behaviour is supplied ........ r629831 | nick | 2008-02-21 16:40:34 +0100 (Thu, 21 Feb 2008) | 1 line Patch from Josh from bug #44437 - improved unit test for poifs ........ r629832 | nick | 2008-02-21 16:42:06 +0100 (Thu, 21 Feb 2008) | 1 line Patch from Josh from bug #44437 - improved unit test for poifs ........ r629837 | nick | 2008-02-21 16:48:52 +0100 (Thu, 21 Feb 2008) | 1 line Patch from Josh from bug #44449 - Handle SharedFormulas better, for where there are formulas for the same area on two sheets, and when the shared formula flag is set incorrectly ........ r629849 | nick | 2008-02-21 17:22:18 +0100 (Thu, 21 Feb 2008) | 1 line Add a disabled test for a file with whacky StyleRecords that trigger an AIOOB ........ r629865 | nick | 2008-02-21 17:44:46 +0100 (Thu, 21 Feb 2008) | 1 line At the request of legal-discuss, shuffle the ooxml xsd licence details into LICENSE from NOTICE ........ r630160 | nick | 2008-02-22 12:23:50 +0100 (Fri, 22 Feb 2008) | 1 line Patch from Josh from bug #44450 - VLookup and HLookup support, and improvements to Lookup and Offset ........ r630164 | nick | 2008-02-22 12:40:00 +0100 (Fri, 22 Feb 2008) | 1 line Bug #44471 - Crystal Reports generates files with short StyleRecords, which isn't allowed in the spec. Work around this ........ r633114 | nick | 2008-03-03 16:01:18 +0100 (Mon, 03 Mar 2008) | 1 line Patch from Paolo from bug #44481 - getVerticallyCenter shouldn't take a parameter, but leave the old version in as deprecated for now ........ r633118 | nick | 2008-03-03 16:10:46 +0100 (Mon, 03 Mar 2008) | 1 line Fix from Yegor from bug #44491 - don't have the new style handy POIDocument property stuff break old style hpsf+hssf use ........ r633126 | nick | 2008-03-03 16:26:38 +0100 (Mon, 03 Mar 2008) | 1 line Patch from Josh from bug #44495 - Handle named cell ranges in formulas that have lower case parts ........ r633151 | nick | 2008-03-03 17:09:02 +0100 (Mon, 03 Mar 2008) | 1 line Patch from Josh from bug #44510 - Fix how DVALRecord works with dropdowns ........ r633169 | nick | 2008-03-03 17:55:00 +0100 (Mon, 03 Mar 2008) | 1 line Patch from Josh from bug #44508 - Fix formula evaluation with evaluateInCell on boolean formulas ........ r633205 | nick | 2008-03-03 18:47:36 +0100 (Mon, 03 Mar 2008) | 1 line Fix indent, add more documentation, and make the error message more helpful ........ r633505 | nick | 2008-03-04 16:06:29 +0100 (Tue, 04 Mar 2008) | 1 line Problem files from bug #44501 ........ r633547 | nick | 2008-03-04 17:53:32 +0100 (Tue, 04 Mar 2008) | 1 line Big patch from Josh from bug #44504 - lots of formula parser improvements ........ r633548 | nick | 2008-03-04 17:59:02 +0100 (Tue, 04 Mar 2008) | 1 line Changelog update for last patch ........ r634318 | nick | 2008-03-06 16:54:06 +0100 (Thu, 06 Mar 2008) | 1 line Change the behaviour on short last blocks to be a warning not an exception, as some people seem to have "real" valid files that trigger this. Fixed bug #28231 ........ r634371 | nick | 2008-03-06 19:06:48 +0100 (Thu, 06 Mar 2008) | 1 line Embeded files from bug #44524 ........ r634372 | nick | 2008-03-06 19:13:47 +0100 (Thu, 06 Mar 2008) | 1 line Add broken test for bug #43901 ........ r634617 | nick | 2008-03-07 12:18:02 +0100 (Fri, 07 Mar 2008) | 1 line Patch from Josh from bug #43901 - Correctly update the internal last cell number when adding and removing cells (previously sometimes off-by-one) ........ r634619 | nick | 2008-03-07 12:36:14 +0100 (Fri, 07 Mar 2008) | 1 line Improved support for read-only recommended workbooks, fixing bug #44536 ........ r634630 | nick | 2008-03-07 13:06:18 +0100 (Fri, 07 Mar 2008) | 1 line Patch largely from Josh from bug #44539 - Support for area references in formulas of rows >= 32768 ........ git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@634936 13f79535-47bb-0310-9956-ffa450edef68
16 years ago
Merged revisions 627779-634630 via svnmerge from https://svn.apache.org/repos/asf/poi/trunk ........ r627779 | nick | 2008-02-14 16:32:49 +0100 (Thu, 14 Feb 2008) | 1 line In the interests of sanity, stop having hssf test data files in scratchpad and main, go to just having them in main ........ r627788 | nick | 2008-02-14 17:01:10 +0100 (Thu, 14 Feb 2008) | 1 line Big formula update from Josh from bug #44364 - support for Match, NA and SumProduct functions, and initial error support in functions ........ r627999 | nick | 2008-02-15 11:30:10 +0100 (Fri, 15 Feb 2008) | 1 line To avoid confusion and repeated changes in svn, update the TestDataValidation test to output its file (that needs opening in excel to check to output) into the system tmp directory ........ r628027 | nick | 2008-02-15 12:45:13 +0100 (Fri, 15 Feb 2008) | 1 line Fix for bug #44403 - Have mid use the third argument properly, and test ........ r628029 | nick | 2008-02-15 12:53:25 +0100 (Fri, 15 Feb 2008) | 1 line Fix for bug #44413 from Josh - Fix for circular references in INDEX, OFFSET, VLOOKUP formulas, where a cell is actually allowed to reference itself ........ r628033 | nick | 2008-02-15 13:04:42 +0100 (Fri, 15 Feb 2008) | 1 line Fix from Josh from bug #44417 - Improved handling of references for the need to quote the sheet name for some formulas, but not when fetching a sheet by name ........ r628035 | nick | 2008-02-15 13:13:25 +0100 (Fri, 15 Feb 2008) | 1 line Fix from Josh from bug #44421 - Update Match function to properly support Area references ........ r628044 | nick | 2008-02-15 13:59:40 +0100 (Fri, 15 Feb 2008) | 1 line Partial fix for bug #44410 - support whole column ranges such as C:C in the formula evaluator (so SUM(D:D) will now work). However, the formula string will still be displayed wrong ........ r628065 | nick | 2008-02-15 14:50:38 +0100 (Fri, 15 Feb 2008) | 1 line Further support for whole-column references, including formula strings and the evaluator. Also has some new tests for it ........ r628714 | nick | 2008-02-18 14:08:16 +0100 (Mon, 18 Feb 2008) | 1 line Update notice for latest guidance on ooxml xsd licence, and update getting involved to link to the newly released binary file format docs ........ r629552 | nick | 2008-02-20 19:14:30 +0100 (Wed, 20 Feb 2008) | 1 line Patch from Josh from bug #44403 - Further support for unusual, but valid, arguments to the Mid function ........ r629738 | nick | 2008-02-21 11:36:08 +0100 (Thu, 21 Feb 2008) | 1 line Fix from Josh from bug #44456 - Update contrib SViewer to not fail if a HSSFRow is null ........ r629742 | nick | 2008-02-21 11:49:25 +0100 (Thu, 21 Feb 2008) | 1 line Use the right way to figure out how many rows on a sheet, so we display the row number for all of them on the left hand side. Also, tidy up some imports ........ r629755 | nick | 2008-02-21 12:34:25 +0100 (Thu, 21 Feb 2008) | 1 line Fix bug 38921, where HSSFPalette.findSimilar() wasn't working properly, and add tests for it ........ r629821 | nick | 2008-02-21 16:08:44 +0100 (Thu, 21 Feb 2008) | 1 line Patch from Josh from bug #44371 - support for OFFSET function, and various tweaks to the formula evaluator to support this ........ r629829 | nick | 2008-02-21 16:35:59 +0100 (Thu, 21 Feb 2008) | 1 line Patch from Josh from bug #44366 - InputStreams passed to POIFSFileSystem are now automatically closed. A warning is generated for people who might've relied on them not being closed before, and a wrapper to restore the old behaviour is supplied ........ r629831 | nick | 2008-02-21 16:40:34 +0100 (Thu, 21 Feb 2008) | 1 line Patch from Josh from bug #44437 - improved unit test for poifs ........ r629832 | nick | 2008-02-21 16:42:06 +0100 (Thu, 21 Feb 2008) | 1 line Patch from Josh from bug #44437 - improved unit test for poifs ........ r629837 | nick | 2008-02-21 16:48:52 +0100 (Thu, 21 Feb 2008) | 1 line Patch from Josh from bug #44449 - Handle SharedFormulas better, for where there are formulas for the same area on two sheets, and when the shared formula flag is set incorrectly ........ r629849 | nick | 2008-02-21 17:22:18 +0100 (Thu, 21 Feb 2008) | 1 line Add a disabled test for a file with whacky StyleRecords that trigger an AIOOB ........ r629865 | nick | 2008-02-21 17:44:46 +0100 (Thu, 21 Feb 2008) | 1 line At the request of legal-discuss, shuffle the ooxml xsd licence details into LICENSE from NOTICE ........ r630160 | nick | 2008-02-22 12:23:50 +0100 (Fri, 22 Feb 2008) | 1 line Patch from Josh from bug #44450 - VLookup and HLookup support, and improvements to Lookup and Offset ........ r630164 | nick | 2008-02-22 12:40:00 +0100 (Fri, 22 Feb 2008) | 1 line Bug #44471 - Crystal Reports generates files with short StyleRecords, which isn't allowed in the spec. Work around this ........ r633114 | nick | 2008-03-03 16:01:18 +0100 (Mon, 03 Mar 2008) | 1 line Patch from Paolo from bug #44481 - getVerticallyCenter shouldn't take a parameter, but leave the old version in as deprecated for now ........ r633118 | nick | 2008-03-03 16:10:46 +0100 (Mon, 03 Mar 2008) | 1 line Fix from Yegor from bug #44491 - don't have the new style handy POIDocument property stuff break old style hpsf+hssf use ........ r633126 | nick | 2008-03-03 16:26:38 +0100 (Mon, 03 Mar 2008) | 1 line Patch from Josh from bug #44495 - Handle named cell ranges in formulas that have lower case parts ........ r633151 | nick | 2008-03-03 17:09:02 +0100 (Mon, 03 Mar 2008) | 1 line Patch from Josh from bug #44510 - Fix how DVALRecord works with dropdowns ........ r633169 | nick | 2008-03-03 17:55:00 +0100 (Mon, 03 Mar 2008) | 1 line Patch from Josh from bug #44508 - Fix formula evaluation with evaluateInCell on boolean formulas ........ r633205 | nick | 2008-03-03 18:47:36 +0100 (Mon, 03 Mar 2008) | 1 line Fix indent, add more documentation, and make the error message more helpful ........ r633505 | nick | 2008-03-04 16:06:29 +0100 (Tue, 04 Mar 2008) | 1 line Problem files from bug #44501 ........ r633547 | nick | 2008-03-04 17:53:32 +0100 (Tue, 04 Mar 2008) | 1 line Big patch from Josh from bug #44504 - lots of formula parser improvements ........ r633548 | nick | 2008-03-04 17:59:02 +0100 (Tue, 04 Mar 2008) | 1 line Changelog update for last patch ........ r634318 | nick | 2008-03-06 16:54:06 +0100 (Thu, 06 Mar 2008) | 1 line Change the behaviour on short last blocks to be a warning not an exception, as some people seem to have "real" valid files that trigger this. Fixed bug #28231 ........ r634371 | nick | 2008-03-06 19:06:48 +0100 (Thu, 06 Mar 2008) | 1 line Embeded files from bug #44524 ........ r634372 | nick | 2008-03-06 19:13:47 +0100 (Thu, 06 Mar 2008) | 1 line Add broken test for bug #43901 ........ r634617 | nick | 2008-03-07 12:18:02 +0100 (Fri, 07 Mar 2008) | 1 line Patch from Josh from bug #43901 - Correctly update the internal last cell number when adding and removing cells (previously sometimes off-by-one) ........ r634619 | nick | 2008-03-07 12:36:14 +0100 (Fri, 07 Mar 2008) | 1 line Improved support for read-only recommended workbooks, fixing bug #44536 ........ r634630 | nick | 2008-03-07 13:06:18 +0100 (Fri, 07 Mar 2008) | 1 line Patch largely from Josh from bug #44539 - Support for area references in formulas of rows >= 32768 ........ git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@634936 13f79535-47bb-0310-9956-ffa450edef68
16 years ago
Merged revisions 627779-634630 via svnmerge from https://svn.apache.org/repos/asf/poi/trunk ........ r627779 | nick | 2008-02-14 16:32:49 +0100 (Thu, 14 Feb 2008) | 1 line In the interests of sanity, stop having hssf test data files in scratchpad and main, go to just having them in main ........ r627788 | nick | 2008-02-14 17:01:10 +0100 (Thu, 14 Feb 2008) | 1 line Big formula update from Josh from bug #44364 - support for Match, NA and SumProduct functions, and initial error support in functions ........ r627999 | nick | 2008-02-15 11:30:10 +0100 (Fri, 15 Feb 2008) | 1 line To avoid confusion and repeated changes in svn, update the TestDataValidation test to output its file (that needs opening in excel to check to output) into the system tmp directory ........ r628027 | nick | 2008-02-15 12:45:13 +0100 (Fri, 15 Feb 2008) | 1 line Fix for bug #44403 - Have mid use the third argument properly, and test ........ r628029 | nick | 2008-02-15 12:53:25 +0100 (Fri, 15 Feb 2008) | 1 line Fix for bug #44413 from Josh - Fix for circular references in INDEX, OFFSET, VLOOKUP formulas, where a cell is actually allowed to reference itself ........ r628033 | nick | 2008-02-15 13:04:42 +0100 (Fri, 15 Feb 2008) | 1 line Fix from Josh from bug #44417 - Improved handling of references for the need to quote the sheet name for some formulas, but not when fetching a sheet by name ........ r628035 | nick | 2008-02-15 13:13:25 +0100 (Fri, 15 Feb 2008) | 1 line Fix from Josh from bug #44421 - Update Match function to properly support Area references ........ r628044 | nick | 2008-02-15 13:59:40 +0100 (Fri, 15 Feb 2008) | 1 line Partial fix for bug #44410 - support whole column ranges such as C:C in the formula evaluator (so SUM(D:D) will now work). However, the formula string will still be displayed wrong ........ r628065 | nick | 2008-02-15 14:50:38 +0100 (Fri, 15 Feb 2008) | 1 line Further support for whole-column references, including formula strings and the evaluator. Also has some new tests for it ........ r628714 | nick | 2008-02-18 14:08:16 +0100 (Mon, 18 Feb 2008) | 1 line Update notice for latest guidance on ooxml xsd licence, and update getting involved to link to the newly released binary file format docs ........ r629552 | nick | 2008-02-20 19:14:30 +0100 (Wed, 20 Feb 2008) | 1 line Patch from Josh from bug #44403 - Further support for unusual, but valid, arguments to the Mid function ........ r629738 | nick | 2008-02-21 11:36:08 +0100 (Thu, 21 Feb 2008) | 1 line Fix from Josh from bug #44456 - Update contrib SViewer to not fail if a HSSFRow is null ........ r629742 | nick | 2008-02-21 11:49:25 +0100 (Thu, 21 Feb 2008) | 1 line Use the right way to figure out how many rows on a sheet, so we display the row number for all of them on the left hand side. Also, tidy up some imports ........ r629755 | nick | 2008-02-21 12:34:25 +0100 (Thu, 21 Feb 2008) | 1 line Fix bug 38921, where HSSFPalette.findSimilar() wasn't working properly, and add tests for it ........ r629821 | nick | 2008-02-21 16:08:44 +0100 (Thu, 21 Feb 2008) | 1 line Patch from Josh from bug #44371 - support for OFFSET function, and various tweaks to the formula evaluator to support this ........ r629829 | nick | 2008-02-21 16:35:59 +0100 (Thu, 21 Feb 2008) | 1 line Patch from Josh from bug #44366 - InputStreams passed to POIFSFileSystem are now automatically closed. A warning is generated for people who might've relied on them not being closed before, and a wrapper to restore the old behaviour is supplied ........ r629831 | nick | 2008-02-21 16:40:34 +0100 (Thu, 21 Feb 2008) | 1 line Patch from Josh from bug #44437 - improved unit test for poifs ........ r629832 | nick | 2008-02-21 16:42:06 +0100 (Thu, 21 Feb 2008) | 1 line Patch from Josh from bug #44437 - improved unit test for poifs ........ r629837 | nick | 2008-02-21 16:48:52 +0100 (Thu, 21 Feb 2008) | 1 line Patch from Josh from bug #44449 - Handle SharedFormulas better, for where there are formulas for the same area on two sheets, and when the shared formula flag is set incorrectly ........ r629849 | nick | 2008-02-21 17:22:18 +0100 (Thu, 21 Feb 2008) | 1 line Add a disabled test for a file with whacky StyleRecords that trigger an AIOOB ........ r629865 | nick | 2008-02-21 17:44:46 +0100 (Thu, 21 Feb 2008) | 1 line At the request of legal-discuss, shuffle the ooxml xsd licence details into LICENSE from NOTICE ........ r630160 | nick | 2008-02-22 12:23:50 +0100 (Fri, 22 Feb 2008) | 1 line Patch from Josh from bug #44450 - VLookup and HLookup support, and improvements to Lookup and Offset ........ r630164 | nick | 2008-02-22 12:40:00 +0100 (Fri, 22 Feb 2008) | 1 line Bug #44471 - Crystal Reports generates files with short StyleRecords, which isn't allowed in the spec. Work around this ........ r633114 | nick | 2008-03-03 16:01:18 +0100 (Mon, 03 Mar 2008) | 1 line Patch from Paolo from bug #44481 - getVerticallyCenter shouldn't take a parameter, but leave the old version in as deprecated for now ........ r633118 | nick | 2008-03-03 16:10:46 +0100 (Mon, 03 Mar 2008) | 1 line Fix from Yegor from bug #44491 - don't have the new style handy POIDocument property stuff break old style hpsf+hssf use ........ r633126 | nick | 2008-03-03 16:26:38 +0100 (Mon, 03 Mar 2008) | 1 line Patch from Josh from bug #44495 - Handle named cell ranges in formulas that have lower case parts ........ r633151 | nick | 2008-03-03 17:09:02 +0100 (Mon, 03 Mar 2008) | 1 line Patch from Josh from bug #44510 - Fix how DVALRecord works with dropdowns ........ r633169 | nick | 2008-03-03 17:55:00 +0100 (Mon, 03 Mar 2008) | 1 line Patch from Josh from bug #44508 - Fix formula evaluation with evaluateInCell on boolean formulas ........ r633205 | nick | 2008-03-03 18:47:36 +0100 (Mon, 03 Mar 2008) | 1 line Fix indent, add more documentation, and make the error message more helpful ........ r633505 | nick | 2008-03-04 16:06:29 +0100 (Tue, 04 Mar 2008) | 1 line Problem files from bug #44501 ........ r633547 | nick | 2008-03-04 17:53:32 +0100 (Tue, 04 Mar 2008) | 1 line Big patch from Josh from bug #44504 - lots of formula parser improvements ........ r633548 | nick | 2008-03-04 17:59:02 +0100 (Tue, 04 Mar 2008) | 1 line Changelog update for last patch ........ r634318 | nick | 2008-03-06 16:54:06 +0100 (Thu, 06 Mar 2008) | 1 line Change the behaviour on short last blocks to be a warning not an exception, as some people seem to have "real" valid files that trigger this. Fixed bug #28231 ........ r634371 | nick | 2008-03-06 19:06:48 +0100 (Thu, 06 Mar 2008) | 1 line Embeded files from bug #44524 ........ r634372 | nick | 2008-03-06 19:13:47 +0100 (Thu, 06 Mar 2008) | 1 line Add broken test for bug #43901 ........ r634617 | nick | 2008-03-07 12:18:02 +0100 (Fri, 07 Mar 2008) | 1 line Patch from Josh from bug #43901 - Correctly update the internal last cell number when adding and removing cells (previously sometimes off-by-one) ........ r634619 | nick | 2008-03-07 12:36:14 +0100 (Fri, 07 Mar 2008) | 1 line Improved support for read-only recommended workbooks, fixing bug #44536 ........ r634630 | nick | 2008-03-07 13:06:18 +0100 (Fri, 07 Mar 2008) | 1 line Patch largely from Josh from bug #44539 - Support for area references in formulas of rows >= 32768 ........ git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@634936 13f79535-47bb-0310-9956-ffa450edef68
16 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581
  1. /* ====================================================================
  2. Licensed to the Apache Software Foundation (ASF) under one or more
  3. contributor license agreements. See the NOTICE file distributed with
  4. this work for additional information regarding copyright ownership.
  5. The ASF licenses this file to You under the Apache License, Version 2.0
  6. (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ==================================================================== */
  15. package org.apache.poi.ss.formula;
  16. import java.util.ArrayList;
  17. import java.util.List;
  18. import java.util.regex.Pattern;
  19. import org.apache.poi.hssf.record.constant.ErrorConstant;
  20. import org.apache.poi.hssf.record.formula.*;
  21. import org.apache.poi.hssf.record.formula.function.FunctionMetadata;
  22. import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
  23. import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
  24. import org.apache.poi.ss.SpreadsheetVersion;
  25. import org.apache.poi.ss.util.AreaReference;
  26. import org.apache.poi.ss.util.CellReference;
  27. import org.apache.poi.ss.util.CellReference.NameType;
  28. /**
  29. * This class parses a formula string into a List of tokens in RPN order.
  30. * Inspired by
  31. * Lets Build a Compiler, by Jack Crenshaw
  32. * BNF for the formula expression is :
  33. * <expression> ::= <term> [<addop> <term>]*
  34. * <term> ::= <factor> [ <mulop> <factor> ]*
  35. * <factor> ::= <number> | (<expression>) | <cellRef> | <function>
  36. * <function> ::= <functionName> ([expression [, expression]*])
  37. * <p/>
  38. * For POI internal use only
  39. * <p/>
  40. *
  41. *
  42. * @author Avik Sengupta <avik at apache dot org>
  43. * @author Andrew C. oliver (acoliver at apache dot org)
  44. * @author Eric Ladner (eladner at goldinc dot com)
  45. * @author Cameron Riley (criley at ekmail.com)
  46. * @author Peter M. Murray (pete at quantrix dot com)
  47. * @author Pavel Krupets (pkrupets at palmtreebusiness dot com)
  48. * @author Josh Micich
  49. * @author David Lewis (DLewis400 at gmail dot com)
  50. */
  51. public final class FormulaParser {
  52. private static final class Identifier {
  53. private final String _name;
  54. private final boolean _isQuoted;
  55. public Identifier(String name, boolean isQuoted) {
  56. _name = name;
  57. _isQuoted = isQuoted;
  58. }
  59. public String getName() {
  60. return _name;
  61. }
  62. public boolean isQuoted() {
  63. return _isQuoted;
  64. }
  65. public String toString() {
  66. StringBuffer sb = new StringBuffer(64);
  67. sb.append(getClass().getName());
  68. sb.append(" [");
  69. if (_isQuoted) {
  70. sb.append("'").append(_name).append("'");
  71. } else {
  72. sb.append(_name);
  73. }
  74. sb.append("]");
  75. return sb.toString();
  76. }
  77. }
  78. private static final class SheetIdentifier {
  79. private final String _bookName;
  80. private final Identifier _sheetIdentifier;
  81. public SheetIdentifier(String bookName, Identifier sheetIdentifier) {
  82. _bookName = bookName;
  83. _sheetIdentifier = sheetIdentifier;
  84. }
  85. public String getBookName() {
  86. return _bookName;
  87. }
  88. public Identifier getSheetIdentifier() {
  89. return _sheetIdentifier;
  90. }
  91. public String toString() {
  92. StringBuffer sb = new StringBuffer(64);
  93. sb.append(getClass().getName());
  94. sb.append(" [");
  95. if (_bookName != null) {
  96. sb.append(" [").append(_sheetIdentifier.getName()).append("]");
  97. }
  98. if (_sheetIdentifier.isQuoted()) {
  99. sb.append("'").append(_sheetIdentifier.getName()).append("'");
  100. } else {
  101. sb.append(_sheetIdentifier.getName());
  102. }
  103. sb.append("]");
  104. return sb.toString();
  105. }
  106. }
  107. private final String _formulaString;
  108. private final int _formulaLength;
  109. /** points at the next character to be read (after the {@link #look} char) */
  110. private int _pointer;
  111. private ParseNode _rootNode;
  112. private static char TAB = '\t';
  113. /**
  114. * Lookahead Character.
  115. * gets value '\0' when the input string is exhausted
  116. */
  117. private char look;
  118. private FormulaParsingWorkbook _book;
  119. private SpreadsheetVersion _ssVersion;
  120. private int _sheetIndex;
  121. /**
  122. * Create the formula parser, with the string that is to be
  123. * parsed against the supplied workbook.
  124. * A later call the parse() method to return ptg list in
  125. * rpn order, then call the getRPNPtg() to retrieve the
  126. * parse results.
  127. * This class is recommended only for single threaded use.
  128. *
  129. * If you only have a usermodel.HSSFWorkbook, and not a
  130. * model.Workbook, then use the convenience method on
  131. * usermodel.HSSFFormulaEvaluator
  132. */
  133. private FormulaParser(String formula, FormulaParsingWorkbook book, int sheetIndex){
  134. _formulaString = formula;
  135. _pointer=0;
  136. _book = book;
  137. _ssVersion = book == null ? SpreadsheetVersion.EXCEL97 : book.getSpreadsheetVersion();
  138. _formulaLength = _formulaString.length();
  139. _sheetIndex = sheetIndex;
  140. }
  141. /**
  142. * Parse a formula into a array of tokens
  143. *
  144. * @param formula the formula to parse
  145. * @param workbook the parent workbook
  146. * @param formulaType the type of the formula, see {@link FormulaType}
  147. * @param sheetIndex the 0-based index of the sheet this formula belongs to.
  148. * The sheet index is required to resolve sheet-level names. <code>-1</code> means that
  149. * the scope of the name will be ignored and the parser will match names only by name
  150. *
  151. * @return array of parsed tokens
  152. * @throws FormulaParseException if the formula has incorrect syntax or is otherwise invalid
  153. */
  154. public static Ptg[] parse(String formula, FormulaParsingWorkbook workbook, int formulaType, int sheetIndex) {
  155. FormulaParser fp = new FormulaParser(formula, workbook, sheetIndex);
  156. fp.parse();
  157. return fp.getRPNPtg(formulaType);
  158. }
  159. /** Read New Character From Input Stream */
  160. private void GetChar() {
  161. // Check to see if we've walked off the end of the string.
  162. if (_pointer > _formulaLength) {
  163. throw new RuntimeException("too far");
  164. }
  165. if (_pointer < _formulaLength) {
  166. look=_formulaString.charAt(_pointer);
  167. } else {
  168. // Just return if so and reset 'look' to something to keep
  169. // SkipWhitespace from spinning
  170. look = (char)0;
  171. }
  172. _pointer++;
  173. //System.out.println("Got char: "+ look);
  174. }
  175. private void resetPointer(int ptr) {
  176. _pointer = ptr;
  177. if (_pointer <= _formulaLength) {
  178. look=_formulaString.charAt(_pointer-1);
  179. } else {
  180. // Just return if so and reset 'look' to something to keep
  181. // SkipWhitespace from spinning
  182. look = (char)0;
  183. }
  184. }
  185. /** Report What Was Expected */
  186. private RuntimeException expected(String s) {
  187. String msg;
  188. if (look == '=' && _formulaString.substring(0, _pointer-1).trim().length() < 1) {
  189. msg = "The specified formula '" + _formulaString
  190. + "' starts with an equals sign which is not allowed.";
  191. } else {
  192. msg = "Parse error near char " + (_pointer-1) + " '" + look + "'"
  193. + " in specified formula '" + _formulaString + "'. Expected "
  194. + s;
  195. }
  196. return new FormulaParseException(msg);
  197. }
  198. /** Recognize an Alpha Character */
  199. private static boolean IsAlpha(char c) {
  200. return Character.isLetter(c) || c == '$' || c=='_';
  201. }
  202. /** Recognize a Decimal Digit */
  203. private static boolean IsDigit(char c) {
  204. return Character.isDigit(c);
  205. }
  206. /** Recognize White Space */
  207. private static boolean IsWhite( char c) {
  208. return c ==' ' || c== TAB;
  209. }
  210. /** Skip Over Leading White Space */
  211. private void SkipWhite() {
  212. while (IsWhite(look)) {
  213. GetChar();
  214. }
  215. }
  216. /**
  217. * Consumes the next input character if it is equal to the one specified otherwise throws an
  218. * unchecked exception. This method does <b>not</b> consume whitespace (before or after the
  219. * matched character).
  220. */
  221. private void Match(char x) {
  222. if (look != x) {
  223. throw expected("'" + x + "'");
  224. }
  225. GetChar();
  226. }
  227. /** Get a Number */
  228. private String GetNum() {
  229. StringBuffer value = new StringBuffer();
  230. while (IsDigit(this.look)){
  231. value.append(this.look);
  232. GetChar();
  233. }
  234. return value.length() == 0 ? null : value.toString();
  235. }
  236. private ParseNode parseRangeExpression() {
  237. ParseNode result = parseRangeable();
  238. boolean hasRange = false;
  239. while (look == ':') {
  240. int pos = _pointer;
  241. GetChar();
  242. ParseNode nextPart = parseRangeable();
  243. // Note - no range simplification here. An expr like "A1:B2:C3:D4:E5" should be
  244. // grouped into area ref pairs like: "(A1:B2):(C3:D4):E5"
  245. // Furthermore, Excel doesn't seem to simplify
  246. // expressions like "Sheet1!A1:Sheet1:B2" into "Sheet1!A1:B2"
  247. checkValidRangeOperand("LHS", pos, result);
  248. checkValidRangeOperand("RHS", pos, nextPart);
  249. ParseNode[] children = { result, nextPart, };
  250. result = new ParseNode(RangePtg.instance, children);
  251. hasRange = true;
  252. }
  253. if (hasRange) {
  254. return augmentWithMemPtg(result);
  255. }
  256. return result;
  257. }
  258. private static ParseNode augmentWithMemPtg(ParseNode root) {
  259. Ptg memPtg;
  260. if (needsMemFunc(root)) {
  261. memPtg = new MemFuncPtg(root.getEncodedSize());
  262. } else {
  263. memPtg = new MemAreaPtg(root.getEncodedSize());
  264. }
  265. return new ParseNode(memPtg, root);
  266. }
  267. /**
  268. * From OOO doc: "Whenever one operand of the reference subexpression is a function,
  269. * a defined name, a 3D reference, or an external reference (and no error occurs),
  270. * a tMemFunc token is used"
  271. *
  272. */
  273. private static boolean needsMemFunc(ParseNode root) {
  274. Ptg token = root.getToken();
  275. if (token instanceof AbstractFunctionPtg) {
  276. return true;
  277. }
  278. if (token instanceof ExternSheetReferenceToken) { // 3D refs
  279. return true;
  280. }
  281. if (token instanceof NamePtg || token instanceof NameXPtg) { // 3D refs
  282. return true;
  283. }
  284. if (token instanceof OperationPtg || token instanceof ParenthesisPtg) {
  285. // expect RangePtg, but perhaps also UnionPtg, IntersectionPtg etc
  286. for(ParseNode child : root.getChildren()) {
  287. if (needsMemFunc(child)) {
  288. return true;
  289. }
  290. }
  291. return false;
  292. }
  293. if (token instanceof OperandPtg) {
  294. return false;
  295. }
  296. if (token instanceof OperationPtg) {
  297. return true;
  298. }
  299. return false;
  300. }
  301. /**
  302. * @param currentParsePosition used to format a potential error message
  303. */
  304. private static void checkValidRangeOperand(String sideName, int currentParsePosition, ParseNode pn) {
  305. if (!isValidRangeOperand(pn)) {
  306. throw new FormulaParseException("The " + sideName
  307. + " of the range operator ':' at position "
  308. + currentParsePosition + " is not a proper reference.");
  309. }
  310. }
  311. /**
  312. * @return <code>false</code> if sub-expression represented the specified ParseNode definitely
  313. * cannot appear on either side of the range (':') operator
  314. */
  315. private static boolean isValidRangeOperand(ParseNode a) {
  316. Ptg tkn = a.getToken();
  317. // Note - order is important for these instance-of checks
  318. if (tkn instanceof OperandPtg) {
  319. // notably cell refs and area refs
  320. return true;
  321. }
  322. // next 2 are special cases of OperationPtg
  323. if (tkn instanceof AbstractFunctionPtg) {
  324. AbstractFunctionPtg afp = (AbstractFunctionPtg) tkn;
  325. byte returnClass = afp.getDefaultOperandClass();
  326. return Ptg.CLASS_REF == returnClass;
  327. }
  328. if (tkn instanceof ValueOperatorPtg) {
  329. return false;
  330. }
  331. if (tkn instanceof OperationPtg) {
  332. return true;
  333. }
  334. // one special case of ControlPtg
  335. if (tkn instanceof ParenthesisPtg) {
  336. // parenthesis Ptg should have only one child
  337. return isValidRangeOperand(a.getChildren()[0]);
  338. }
  339. // one special case of ScalarConstantPtg
  340. if (tkn == ErrPtg.REF_INVALID) {
  341. return true;
  342. }
  343. // All other ControlPtgs and ScalarConstantPtgs cannot be used with ':'
  344. return false;
  345. }
  346. /**
  347. * Parses area refs (things which could be the operand of ':') and simple factors
  348. * Examples
  349. * <pre>
  350. * A$1
  351. * $A$1 : $B1
  352. * A1 ....... C2
  353. * Sheet1 !$A1
  354. * a..b!A1
  355. * 'my sheet'!A1
  356. * .my.sheet!A1
  357. * my.named..range.
  358. * foo.bar(123.456, "abc")
  359. * 123.456
  360. * "abc"
  361. * true
  362. * </pre>
  363. *
  364. */
  365. private ParseNode parseRangeable() {
  366. SkipWhite();
  367. int savePointer = _pointer;
  368. SheetIdentifier sheetIden = parseSheetName();
  369. if (sheetIden == null) {
  370. resetPointer(savePointer);
  371. } else {
  372. SkipWhite();
  373. savePointer = _pointer;
  374. }
  375. SimpleRangePart part1 = parseSimpleRangePart();
  376. if (part1 == null) {
  377. if (sheetIden != null) {
  378. if(look == '#'){ // error ref like MySheet!#REF!
  379. return new ParseNode(ErrPtg.valueOf(parseErrorLiteral()));
  380. } else {
  381. throw new FormulaParseException("Cell reference expected after sheet name at index "
  382. + _pointer + ".");
  383. }
  384. }
  385. return parseNonRange(savePointer);
  386. }
  387. boolean whiteAfterPart1 = IsWhite(look);
  388. if (whiteAfterPart1) {
  389. SkipWhite();
  390. }
  391. if (look == ':') {
  392. int colonPos = _pointer;
  393. GetChar();
  394. SkipWhite();
  395. SimpleRangePart part2 = parseSimpleRangePart();
  396. if (part2 != null && !part1.isCompatibleForArea(part2)) {
  397. // second part is not compatible with an area ref e.g. S!A1:S!B2
  398. // where S might be a sheet name (that looks like a column name)
  399. part2 = null;
  400. }
  401. if (part2 == null) {
  402. // second part is not compatible with an area ref e.g. A1:OFFSET(B2, 1, 2)
  403. // reset and let caller use explicit range operator
  404. resetPointer(colonPos);
  405. if (!part1.isCell()) {
  406. String prefix;
  407. if (sheetIden == null) {
  408. prefix = "";
  409. } else {
  410. prefix = "'" + sheetIden.getSheetIdentifier().getName() + '!';
  411. }
  412. throw new FormulaParseException(prefix + part1.getRep() + "' is not a proper reference.");
  413. }
  414. return createAreaRefParseNode(sheetIden, part1, part2);
  415. }
  416. return createAreaRefParseNode(sheetIden, part1, part2);
  417. }
  418. if (look == '.') {
  419. GetChar();
  420. int dotCount = 1;
  421. while (look =='.') {
  422. dotCount ++;
  423. GetChar();
  424. }
  425. boolean whiteBeforePart2 = IsWhite(look);
  426. SkipWhite();
  427. SimpleRangePart part2 = parseSimpleRangePart();
  428. String part1And2 = _formulaString.substring(savePointer-1, _pointer-1);
  429. if (part2 == null) {
  430. if (sheetIden != null) {
  431. throw new FormulaParseException("Complete area reference expected after sheet name at index "
  432. + _pointer + ".");
  433. }
  434. return parseNonRange(savePointer);
  435. }
  436. if (whiteAfterPart1 || whiteBeforePart2) {
  437. if (part1.isRowOrColumn() || part2.isRowOrColumn()) {
  438. // "A .. B" not valid syntax for "A:B"
  439. // and there's no other valid expression that fits this grammar
  440. throw new FormulaParseException("Dotted range (full row or column) expression '"
  441. + part1And2 + "' must not contain whitespace.");
  442. }
  443. return createAreaRefParseNode(sheetIden, part1, part2);
  444. }
  445. if (dotCount == 1 && part1.isRow() && part2.isRow()) {
  446. // actually, this is looking more like a number
  447. return parseNonRange(savePointer);
  448. }
  449. if (part1.isRowOrColumn() || part2.isRowOrColumn()) {
  450. if (dotCount != 2) {
  451. throw new FormulaParseException("Dotted range (full row or column) expression '" + part1And2
  452. + "' must have exactly 2 dots.");
  453. }
  454. }
  455. return createAreaRefParseNode(sheetIden, part1, part2);
  456. }
  457. if (part1.isCell() && isValidCellReference(part1.getRep())) {
  458. return createAreaRefParseNode(sheetIden, part1, null);
  459. }
  460. if (sheetIden != null) {
  461. throw new FormulaParseException("Second part of cell reference expected after sheet name at index "
  462. + _pointer + ".");
  463. }
  464. return parseNonRange(savePointer);
  465. }
  466. /**
  467. * Parses simple factors that are not primitive ranges or range components
  468. * i.e. '!', ':'(and equiv '...') do not appear
  469. * Examples
  470. * <pre>
  471. * my.named...range.
  472. * foo.bar(123.456, "abc")
  473. * 123.456
  474. * "abc"
  475. * true
  476. * </pre>
  477. */
  478. private ParseNode parseNonRange(int savePointer) {
  479. resetPointer(savePointer);
  480. if (Character.isDigit(look)) {
  481. return new ParseNode(parseNumber());
  482. }
  483. if (look == '"') {
  484. return new ParseNode(new StringPtg(parseStringLiteral()));
  485. }
  486. // from now on we can only be dealing with non-quoted identifiers
  487. // which will either be named ranges or functions
  488. StringBuilder sb = new StringBuilder();
  489. // defined names may begin with a letter or underscore
  490. if (!Character.isLetter(look) && look != '_') {
  491. throw expected("number, string, or defined name");
  492. }
  493. while (isValidDefinedNameChar(look)) {
  494. sb.append(look);
  495. GetChar();
  496. }
  497. SkipWhite();
  498. String name = sb.toString();
  499. if (look == '(') {
  500. return function(name);
  501. }
  502. if (name.equalsIgnoreCase("TRUE") || name.equalsIgnoreCase("FALSE")) {
  503. return new ParseNode(BoolPtg.valueOf(name.equalsIgnoreCase("TRUE")));
  504. }
  505. if (_book == null) {
  506. // Only test cases omit the book (expecting it not to be needed)
  507. throw new IllegalStateException("Need book to evaluate name '" + name + "'");
  508. }
  509. EvaluationName evalName = _book.getName(name, _sheetIndex);
  510. if (evalName == null) {
  511. throw new FormulaParseException("Specified named range '"
  512. + name + "' does not exist in the current workbook.");
  513. }
  514. if (evalName.isRange()) {
  515. return new ParseNode(evalName.createPtg());
  516. }
  517. // TODO - what about NameX ?
  518. throw new FormulaParseException("Specified name '"
  519. + name + "' is not a range as expected.");
  520. }
  521. /**
  522. *
  523. * @return <code>true</code> if the specified character may be used in a defined name
  524. */
  525. private static boolean isValidDefinedNameChar(char ch) {
  526. if (Character.isLetterOrDigit(ch)) {
  527. return true;
  528. }
  529. switch (ch) {
  530. case '.':
  531. case '_':
  532. case '?':
  533. case '\\': // of all things
  534. return true;
  535. }
  536. return false;
  537. }
  538. /**
  539. *
  540. * @param sheetIden may be <code>null</code>
  541. * @param part1
  542. * @param part2 may be <code>null</code>
  543. */
  544. private ParseNode createAreaRefParseNode(SheetIdentifier sheetIden, SimpleRangePart part1,
  545. SimpleRangePart part2) throws FormulaParseException {
  546. int extIx;
  547. if (sheetIden == null) {
  548. extIx = Integer.MIN_VALUE;
  549. } else {
  550. String sName = sheetIden.getSheetIdentifier().getName();
  551. if (sheetIden.getBookName() == null) {
  552. extIx = _book.getExternalSheetIndex(sName);
  553. } else {
  554. extIx = _book.getExternalSheetIndex(sheetIden.getBookName(), sName);
  555. }
  556. }
  557. Ptg ptg;
  558. if (part2 == null) {
  559. CellReference cr = part1.getCellReference();
  560. if (sheetIden == null) {
  561. ptg = new RefPtg(cr);
  562. } else {
  563. ptg = new Ref3DPtg(cr, extIx);
  564. }
  565. } else {
  566. AreaReference areaRef = createAreaRef(part1, part2);
  567. if (sheetIden == null) {
  568. ptg = new AreaPtg(areaRef);
  569. } else {
  570. ptg = new Area3DPtg(areaRef, extIx);
  571. }
  572. }
  573. return new ParseNode(ptg);
  574. }
  575. private static AreaReference createAreaRef(SimpleRangePart part1, SimpleRangePart part2) {
  576. if (!part1.isCompatibleForArea(part2)) {
  577. throw new FormulaParseException("has incompatible parts: '"
  578. + part1.getRep() + "' and '" + part2.getRep() + "'.");
  579. }
  580. if (part1.isRow()) {
  581. return AreaReference.getWholeRow(part1.getRep(), part2.getRep());
  582. }
  583. if (part1.isColumn()) {
  584. return AreaReference.getWholeColumn(part1.getRep(), part2.getRep());
  585. }
  586. return new AreaReference(part1.getCellReference(), part2.getCellReference());
  587. }
  588. /**
  589. * Matches a zero or one letter-runs followed by zero or one digit-runs.
  590. * Either or both runs man optionally be prefixed with a single '$'.
  591. * (copied+modified from {@link org.apache.poi.ss.util.CellReference#CELL_REF_PATTERN})
  592. */
  593. private static final Pattern CELL_REF_PATTERN = Pattern.compile("(\\$?[A-Za-z]+)?(\\$?[0-9]+)?");
  594. /**
  595. * Parses out a potential LHS or RHS of a ':' intended to produce a plain AreaRef. Normally these are
  596. * proper cell references but they could also be row or column refs like "$AC" or "10"
  597. * @return <code>null</code> (and leaves {@link #_pointer} unchanged if a proper range part does not parse out
  598. */
  599. private SimpleRangePart parseSimpleRangePart() {
  600. int ptr = _pointer-1; // TODO avoid StringIndexOutOfBounds
  601. boolean hasDigits = false;
  602. boolean hasLetters = false;
  603. while (ptr < _formulaLength) {
  604. char ch = _formulaString.charAt(ptr);
  605. if (Character.isDigit(ch)) {
  606. hasDigits = true;
  607. } else if (Character.isLetter(ch)) {
  608. hasLetters = true;
  609. } else if (ch =='$' || ch =='_') {
  610. //
  611. } else {
  612. break;
  613. }
  614. ptr++;
  615. }
  616. if (ptr <= _pointer-1) {
  617. return null;
  618. }
  619. String rep = _formulaString.substring(_pointer-1, ptr);
  620. if (!CELL_REF_PATTERN.matcher(rep).matches()) {
  621. return null;
  622. }
  623. // Check range bounds against grid max
  624. if (hasLetters && hasDigits) {
  625. if (!isValidCellReference(rep)) {
  626. return null;
  627. }
  628. } else if (hasLetters) {
  629. if (!CellReference.isColumnWithnRange(rep.replace("$", ""), _ssVersion)) {
  630. return null;
  631. }
  632. } else if (hasDigits) {
  633. int i;
  634. try {
  635. i = Integer.parseInt(rep.replace("$", ""));
  636. } catch (NumberFormatException e) {
  637. return null;
  638. }
  639. if (i<1 || i>65536) {
  640. return null;
  641. }
  642. } else {
  643. // just dollars ? can this happen?
  644. return null;
  645. }
  646. resetPointer(ptr+1); // stepping forward
  647. return new SimpleRangePart(rep, hasLetters, hasDigits);
  648. }
  649. /**
  650. * A1, $A1, A$1, $A$1, A, 1
  651. */
  652. private static final class SimpleRangePart {
  653. private enum Type {
  654. CELL, ROW, COLUMN;
  655. public static Type get(boolean hasLetters, boolean hasDigits) {
  656. if (hasLetters) {
  657. return hasDigits ? CELL : COLUMN;
  658. }
  659. if (!hasDigits) {
  660. throw new IllegalArgumentException("must have either letters or numbers");
  661. }
  662. return ROW;
  663. }
  664. }
  665. private final Type _type;
  666. private final String _rep;
  667. public SimpleRangePart(String rep, boolean hasLetters, boolean hasNumbers) {
  668. _rep = rep;
  669. _type = Type.get(hasLetters, hasNumbers);
  670. }
  671. public boolean isCell() {
  672. return _type == Type.CELL;
  673. }
  674. public boolean isRowOrColumn() {
  675. return _type != Type.CELL;
  676. }
  677. public CellReference getCellReference() {
  678. if (_type != Type.CELL) {
  679. throw new IllegalStateException("Not applicable to this type");
  680. }
  681. return new CellReference(_rep);
  682. }
  683. public boolean isColumn() {
  684. return _type == Type.COLUMN;
  685. }
  686. public boolean isRow() {
  687. return _type == Type.ROW;
  688. }
  689. public String getRep() {
  690. return _rep;
  691. }
  692. /**
  693. * @return <code>true</code> if the two range parts can be combined in an
  694. * {@link AreaPtg} ( Note - the explicit range operator (:) may still be valid
  695. * when this method returns <code>false</code> )
  696. */
  697. public boolean isCompatibleForArea(SimpleRangePart part2) {
  698. return _type == part2._type;
  699. }
  700. @Override
  701. public String toString() {
  702. StringBuilder sb = new StringBuilder(64);
  703. sb.append(getClass().getName()).append(" [");
  704. sb.append(_rep);
  705. sb.append("]");
  706. return sb.toString();
  707. }
  708. }
  709. /**
  710. * Note - caller should reset {@link #_pointer} upon <code>null</code> result
  711. * @return The sheet name as an identifier <code>null</code> if '!' is not found in the right place
  712. */
  713. private SheetIdentifier parseSheetName() {
  714. String bookName;
  715. if (look == '[') {
  716. StringBuilder sb = new StringBuilder();
  717. GetChar();
  718. while (look != ']') {
  719. sb.append(look);
  720. GetChar();
  721. }
  722. GetChar();
  723. bookName = sb.toString();
  724. } else {
  725. bookName = null;
  726. }
  727. if (look == '\'') {
  728. StringBuffer sb = new StringBuffer();
  729. Match('\'');
  730. boolean done = look == '\'';
  731. while(!done) {
  732. sb.append(look);
  733. GetChar();
  734. if(look == '\'')
  735. {
  736. Match('\'');
  737. done = look != '\'';
  738. }
  739. }
  740. Identifier iden = new Identifier(sb.toString(), true);
  741. // quoted identifier - can't concatenate anything more
  742. SkipWhite();
  743. if (look == '!') {
  744. GetChar();
  745. return new SheetIdentifier(bookName, iden);
  746. }
  747. return null;
  748. }
  749. // unquoted sheet names must start with underscore or a letter
  750. if (look =='_' || Character.isLetter(look)) {
  751. StringBuilder sb = new StringBuilder();
  752. // can concatenate idens with dots
  753. while (isUnquotedSheetNameChar(look)) {
  754. sb.append(look);
  755. GetChar();
  756. }
  757. SkipWhite();
  758. if (look == '!') {
  759. GetChar();
  760. return new SheetIdentifier(bookName, new Identifier(sb.toString(), false));
  761. }
  762. return null;
  763. }
  764. return null;
  765. }
  766. /**
  767. * very similar to {@link SheetNameFormatter#isSpecialChar(char)}
  768. */
  769. private static boolean isUnquotedSheetNameChar(char ch) {
  770. if(Character.isLetterOrDigit(ch)) {
  771. return true;
  772. }
  773. switch(ch) {
  774. case '.': // dot is OK
  775. case '_': // underscore is OK
  776. return true;
  777. }
  778. return false;
  779. }
  780. /**
  781. * @return <code>true</code> if the specified name is a valid cell reference
  782. */
  783. private boolean isValidCellReference(String str) {
  784. //check range bounds against grid max
  785. boolean result = CellReference.classifyCellReference(str, _ssVersion) == NameType.CELL;
  786. if(result){
  787. /**
  788. * Check if the argument is a function. Certain names can be either a cell reference or a function name
  789. * depending on the contenxt. Compare the following examples in Excel 2007:
  790. * (a) LOG10(100) + 1
  791. * (b) LOG10 + 1
  792. * In (a) LOG10 is a name of a built-in function. In (b) LOG10 is a cell reference
  793. */
  794. boolean isFunc = FunctionMetadataRegistry.getFunctionByName(str.toUpperCase()) != null;
  795. if(isFunc){
  796. int savePointer = _pointer;
  797. resetPointer(_pointer + str.length());
  798. SkipWhite();
  799. // open bracket indicates that the argument is a function,
  800. // the returning value should be false, i.e. "not a valid cell reference"
  801. result = look != '(';
  802. resetPointer(savePointer);
  803. }
  804. }
  805. return result;
  806. }
  807. /**
  808. * Note - Excel function names are 'case aware but not case sensitive'. This method may end
  809. * up creating a defined name record in the workbook if the specified name is not an internal
  810. * Excel function, and has not been encountered before.
  811. *
  812. * @param name case preserved function name (as it was entered/appeared in the formula).
  813. */
  814. private ParseNode function(String name) {
  815. Ptg nameToken = null;
  816. if(!AbstractFunctionPtg.isBuiltInFunctionName(name)) {
  817. // user defined function
  818. // in the token tree, the name is more or less the first argument
  819. if (_book == null) {
  820. // Only test cases omit the book (expecting it not to be needed)
  821. throw new IllegalStateException("Need book to evaluate name '" + name + "'");
  822. }
  823. EvaluationName hName = _book.getName(name, _sheetIndex);
  824. if (hName == null) {
  825. nameToken = _book.getNameXPtg(name);
  826. if (nameToken == null) {
  827. throw new FormulaParseException("Name '" + name
  828. + "' is completely unknown in the current workbook");
  829. }
  830. } else {
  831. if (!hName.isFunctionName()) {
  832. throw new FormulaParseException("Attempt to use name '" + name
  833. + "' as a function, but defined name in workbook does not refer to a function");
  834. }
  835. // calls to user-defined functions within the workbook
  836. // get a Name token which points to a defined name record
  837. nameToken = hName.createPtg();
  838. }
  839. }
  840. Match('(');
  841. ParseNode[] args = Arguments();
  842. Match(')');
  843. return getFunction(name, nameToken, args);
  844. }
  845. /**
  846. * Generates the variable function ptg for the formula.
  847. * <p>
  848. * For IF Formulas, additional PTGs are added to the tokens
  849. * @param name a {@link NamePtg} or {@link NameXPtg} or <code>null</code>
  850. * @return Ptg a null is returned if we're in an IF formula, it needs extreme manipulation and is handled in this function
  851. */
  852. private ParseNode getFunction(String name, Ptg namePtg, ParseNode[] args) {
  853. FunctionMetadata fm = FunctionMetadataRegistry.getFunctionByName(name.toUpperCase());
  854. int numArgs = args.length;
  855. if(fm == null) {
  856. if (namePtg == null) {
  857. throw new IllegalStateException("NamePtg must be supplied for external functions");
  858. }
  859. // must be external function
  860. ParseNode[] allArgs = new ParseNode[numArgs+1];
  861. allArgs[0] = new ParseNode(namePtg);
  862. System.arraycopy(args, 0, allArgs, 1, numArgs);
  863. return new ParseNode(FuncVarPtg.create(name, numArgs+1), allArgs);
  864. }
  865. if (namePtg != null) {
  866. throw new IllegalStateException("NamePtg no applicable to internal functions");
  867. }
  868. boolean isVarArgs = !fm.hasFixedArgsLength();
  869. int funcIx = fm.getIndex();
  870. if (funcIx == FunctionMetadataRegistry.FUNCTION_INDEX_SUM && args.length == 1) {
  871. // Excel encodes the sum of a single argument as tAttrSum
  872. // POI does the same for consistency, but this is not critical
  873. return new ParseNode(AttrPtg.getSumSingle(), args);
  874. // The code below would encode tFuncVar(SUM) which seems to do no harm
  875. }
  876. validateNumArgs(args.length, fm);
  877. AbstractFunctionPtg retval;
  878. if(isVarArgs) {
  879. retval = FuncVarPtg.create(name, numArgs);
  880. } else {
  881. retval = FuncPtg.create(funcIx);
  882. }
  883. return new ParseNode(retval, args);
  884. }
  885. private void validateNumArgs(int numArgs, FunctionMetadata fm) {
  886. if(numArgs < fm.getMinParams()) {
  887. String msg = "Too few arguments to function '" + fm.getName() + "'. ";
  888. if(fm.hasFixedArgsLength()) {
  889. msg += "Expected " + fm.getMinParams();
  890. } else {
  891. msg += "At least " + fm.getMinParams() + " were expected";
  892. }
  893. msg += " but got " + numArgs + ".";
  894. throw new FormulaParseException(msg);
  895. }
  896. //the maximum number of arguments depends on the Excel version
  897. int maxArgs;
  898. if (fm.hasUnlimitedVarags()) {
  899. if(_book != null) {
  900. maxArgs = _book.getSpreadsheetVersion().getMaxFunctionArgs();
  901. } else {
  902. //_book can be omitted by test cases
  903. maxArgs = fm.getMaxParams(); // just use BIFF8
  904. }
  905. } else {
  906. maxArgs = fm.getMaxParams();
  907. }
  908. if(numArgs > maxArgs) {
  909. String msg = "Too many arguments to function '" + fm.getName() + "'. ";
  910. if(fm.hasFixedArgsLength()) {
  911. msg += "Expected " + maxArgs;
  912. } else {
  913. msg += "At most " + maxArgs + " were expected";
  914. }
  915. msg += " but got " + numArgs + ".";
  916. throw new FormulaParseException(msg);
  917. }
  918. }
  919. private static boolean isArgumentDelimiter(char ch) {
  920. return ch == ',' || ch == ')';
  921. }
  922. /** get arguments to a function */
  923. private ParseNode[] Arguments() {
  924. //average 2 args per function
  925. List<ParseNode> temp = new ArrayList<ParseNode>(2);
  926. SkipWhite();
  927. if(look == ')') {
  928. return ParseNode.EMPTY_ARRAY;
  929. }
  930. boolean missedPrevArg = true;
  931. int numArgs = 0;
  932. while (true) {
  933. SkipWhite();
  934. if (isArgumentDelimiter(look)) {
  935. if (missedPrevArg) {
  936. temp.add(new ParseNode(MissingArgPtg.instance));
  937. numArgs++;
  938. }
  939. if (look == ')') {
  940. break;
  941. }
  942. Match(',');
  943. missedPrevArg = true;
  944. continue;
  945. }
  946. temp.add(comparisonExpression());
  947. numArgs++;
  948. missedPrevArg = false;
  949. SkipWhite();
  950. if (!isArgumentDelimiter(look)) {
  951. throw expected("',' or ')'");
  952. }
  953. }
  954. ParseNode[] result = new ParseNode[temp.size()];
  955. temp.toArray(result);
  956. return result;
  957. }
  958. /** Parse and Translate a Math Factor */
  959. private ParseNode powerFactor() {
  960. ParseNode result = percentFactor();
  961. while(true) {
  962. SkipWhite();
  963. if(look != '^') {
  964. return result;
  965. }
  966. Match('^');
  967. ParseNode other = percentFactor();
  968. result = new ParseNode(PowerPtg.instance, result, other);
  969. }
  970. }
  971. private ParseNode percentFactor() {
  972. ParseNode result = parseSimpleFactor();
  973. while(true) {
  974. SkipWhite();
  975. if(look != '%') {
  976. return result;
  977. }
  978. Match('%');
  979. result = new ParseNode(PercentPtg.instance, result);
  980. }
  981. }
  982. /**
  983. * factors (without ^ or % )
  984. */
  985. private ParseNode parseSimpleFactor() {
  986. SkipWhite();
  987. switch(look) {
  988. case '#':
  989. return new ParseNode(ErrPtg.valueOf(parseErrorLiteral()));
  990. case '-':
  991. Match('-');
  992. return parseUnary(false);
  993. case '+':
  994. Match('+');
  995. return parseUnary(true);
  996. case '(':
  997. Match('(');
  998. ParseNode inside = comparisonExpression();
  999. Match(')');
  1000. return new ParseNode(ParenthesisPtg.instance, inside);
  1001. case '"':
  1002. return new ParseNode(new StringPtg(parseStringLiteral()));
  1003. case '{':
  1004. Match('{');
  1005. ParseNode arrayNode = parseArray();
  1006. Match('}');
  1007. return arrayNode;
  1008. }
  1009. if (IsAlpha(look) || Character.isDigit(look) || look == '\'' || look == '['){
  1010. return parseRangeExpression();
  1011. }
  1012. if (look == '.') {
  1013. return new ParseNode(parseNumber());
  1014. }
  1015. throw expected("cell ref or constant literal");
  1016. }
  1017. private ParseNode parseUnary(boolean isPlus) {
  1018. boolean numberFollows = IsDigit(look) || look=='.';
  1019. ParseNode factor = powerFactor();
  1020. if (numberFollows) {
  1021. // + or - directly next to a number is parsed with the number
  1022. Ptg token = factor.getToken();
  1023. if (token instanceof NumberPtg) {
  1024. if (isPlus) {
  1025. return factor;
  1026. }
  1027. token = new NumberPtg(-((NumberPtg)token).getValue());
  1028. return new ParseNode(token);
  1029. }
  1030. if (token instanceof IntPtg) {
  1031. if (isPlus) {
  1032. return factor;
  1033. }
  1034. int intVal = ((IntPtg)token).getValue();
  1035. // note - cannot use IntPtg for negatives
  1036. token = new NumberPtg(-intVal);
  1037. return new ParseNode(token);
  1038. }
  1039. }
  1040. return new ParseNode(isPlus ? UnaryPlusPtg.instance : UnaryMinusPtg.instance, factor);
  1041. }
  1042. private ParseNode parseArray() {
  1043. List<Object[]> rowsData = new ArrayList<Object[]>();
  1044. while(true) {
  1045. Object[] singleRowData = parseArrayRow();
  1046. rowsData.add(singleRowData);
  1047. if (look == '}') {
  1048. break;
  1049. }
  1050. if (look != ';') {
  1051. throw expected("'}' or ';'");
  1052. }
  1053. Match(';');
  1054. }
  1055. int nRows = rowsData.size();
  1056. Object[][] values2d = new Object[nRows][];
  1057. rowsData.toArray(values2d);
  1058. int nColumns = values2d[0].length;
  1059. checkRowLengths(values2d, nColumns);
  1060. return new ParseNode(new ArrayPtg(values2d));
  1061. }
  1062. private void checkRowLengths(Object[][] values2d, int nColumns) {
  1063. for (int i = 0; i < values2d.length; i++) {
  1064. int rowLen = values2d[i].length;
  1065. if (rowLen != nColumns) {
  1066. throw new FormulaParseException("Array row " + i + " has length " + rowLen
  1067. + " but row 0 has length " + nColumns);
  1068. }
  1069. }
  1070. }
  1071. private Object[] parseArrayRow() {
  1072. List<Object> temp = new ArrayList<Object>();
  1073. while (true) {
  1074. temp.add(parseArrayItem());
  1075. SkipWhite();
  1076. switch(look) {
  1077. case '}':
  1078. case ';':
  1079. break;
  1080. case ',':
  1081. Match(',');
  1082. continue;
  1083. default:
  1084. throw expected("'}' or ','");
  1085. }
  1086. break;
  1087. }
  1088. Object[] result = new Object[temp.size()];
  1089. temp.toArray(result);
  1090. return result;
  1091. }
  1092. private Object parseArrayItem() {
  1093. SkipWhite();
  1094. switch(look) {
  1095. case '"': return parseStringLiteral();
  1096. case '#': return ErrorConstant.valueOf(parseErrorLiteral());
  1097. case 'F': case 'f':
  1098. case 'T': case 't':
  1099. return parseBooleanLiteral();
  1100. case '-':
  1101. Match('-');
  1102. SkipWhite();
  1103. return convertArrayNumber(parseNumber(), false);
  1104. }
  1105. // else assume number
  1106. return convertArrayNumber(parseNumber(), true);
  1107. }
  1108. private Boolean parseBooleanLiteral() {
  1109. String iden = parseUnquotedIdentifier();
  1110. if ("TRUE".equalsIgnoreCase(iden)) {
  1111. return Boolean.TRUE;
  1112. }
  1113. if ("FALSE".equalsIgnoreCase(iden)) {
  1114. return Boolean.FALSE;
  1115. }
  1116. throw expected("'TRUE' or 'FALSE'");
  1117. }
  1118. private static Double convertArrayNumber(Ptg ptg, boolean isPositive) {
  1119. double value;
  1120. if (ptg instanceof IntPtg) {
  1121. value = ((IntPtg)ptg).getValue();
  1122. } else if (ptg instanceof NumberPtg) {
  1123. value = ((NumberPtg)ptg).getValue();
  1124. } else {
  1125. throw new RuntimeException("Unexpected ptg (" + ptg.getClass().getName() + ")");
  1126. }
  1127. if (!isPositive) {
  1128. value = -value;
  1129. }
  1130. return new Double(value);
  1131. }
  1132. private Ptg parseNumber() {
  1133. String number2 = null;
  1134. String exponent = null;
  1135. String number1 = GetNum();
  1136. if (look == '.') {
  1137. GetChar();
  1138. number2 = GetNum();
  1139. }
  1140. if (look == 'E') {
  1141. GetChar();
  1142. String sign = "";
  1143. if (look == '+') {
  1144. GetChar();
  1145. } else if (look == '-') {
  1146. GetChar();
  1147. sign = "-";
  1148. }
  1149. String number = GetNum();
  1150. if (number == null) {
  1151. throw expected("Integer");
  1152. }
  1153. exponent = sign + number;
  1154. }
  1155. if (number1 == null && number2 == null) {
  1156. throw expected("Integer");
  1157. }
  1158. return getNumberPtgFromString(number1, number2, exponent);
  1159. }
  1160. private int parseErrorLiteral() {
  1161. Match('#');
  1162. String part1 = parseUnquotedIdentifier().toUpperCase();
  1163. if (part1 == null) {
  1164. throw expected("remainder of error constant literal");
  1165. }
  1166. switch(part1.charAt(0)) {
  1167. case 'V':
  1168. if(part1.equals("VALUE")) {
  1169. Match('!');
  1170. return HSSFErrorConstants.ERROR_VALUE;
  1171. }
  1172. throw expected("#VALUE!");
  1173. case 'R':
  1174. if(part1.equals("REF")) {
  1175. Match('!');
  1176. return HSSFErrorConstants.ERROR_REF;
  1177. }
  1178. throw expected("#REF!");
  1179. case 'D':
  1180. if(part1.equals("DIV")) {
  1181. Match('/');
  1182. Match('0');
  1183. Match('!');
  1184. return HSSFErrorConstants.ERROR_DIV_0;
  1185. }
  1186. throw expected("#DIV/0!");
  1187. case 'N':
  1188. if(part1.equals("NAME")) {
  1189. Match('?'); // only one that ends in '?'
  1190. return HSSFErrorConstants.ERROR_NAME;
  1191. }
  1192. if(part1.equals("NUM")) {
  1193. Match('!');
  1194. return HSSFErrorConstants.ERROR_NUM;
  1195. }
  1196. if(part1.equals("NULL")) {
  1197. Match('!');
  1198. return HSSFErrorConstants.ERROR_NULL;
  1199. }
  1200. if(part1.equals("N")) {
  1201. Match('/');
  1202. if(look != 'A' && look != 'a') {
  1203. throw expected("#N/A");
  1204. }
  1205. Match(look);
  1206. // Note - no '!' or '?' suffix
  1207. return HSSFErrorConstants.ERROR_NA;
  1208. }
  1209. throw expected("#NAME?, #NUM!, #NULL! or #N/A");
  1210. }
  1211. throw expected("#VALUE!, #REF!, #DIV/0!, #NAME?, #NUM!, #NULL! or #N/A");
  1212. }
  1213. private String parseUnquotedIdentifier() {
  1214. if (look == '\'') {
  1215. throw expected("unquoted identifier");
  1216. }
  1217. StringBuilder sb = new StringBuilder();
  1218. while (Character.isLetterOrDigit(look) || look == '.') {
  1219. sb.append(look);
  1220. GetChar();
  1221. }
  1222. if (sb.length() < 1) {
  1223. return null;
  1224. }
  1225. return sb.toString();
  1226. }
  1227. /**
  1228. * Get a PTG for an integer from its string representation.
  1229. * return Int or Number Ptg based on size of input
  1230. */
  1231. private static Ptg getNumberPtgFromString(String number1, String number2, String exponent) {
  1232. StringBuffer number = new StringBuffer();
  1233. if (number2 == null) {
  1234. number.append(number1);
  1235. if (exponent != null) {
  1236. number.append('E');
  1237. number.append(exponent);
  1238. }
  1239. String numberStr = number.toString();
  1240. int intVal;
  1241. try {
  1242. intVal = Integer.parseInt(numberStr);
  1243. } catch (NumberFormatException e) {
  1244. return new NumberPtg(numberStr);
  1245. }
  1246. if (IntPtg.isInRange(intVal)) {
  1247. return new IntPtg(intVal);
  1248. }
  1249. return new NumberPtg(numberStr);
  1250. }
  1251. if (number1 != null) {
  1252. number.append(number1);
  1253. }
  1254. number.append('.');
  1255. number.append(number2);
  1256. if (exponent != null) {
  1257. number.append('E');
  1258. number.append(exponent);
  1259. }
  1260. return new NumberPtg(number.toString());
  1261. }
  1262. private String parseStringLiteral() {
  1263. Match('"');
  1264. StringBuffer token = new StringBuffer();
  1265. while (true) {
  1266. if (look == '"') {
  1267. GetChar();
  1268. if (look != '"') {
  1269. break;
  1270. }
  1271. }
  1272. token.append(look);
  1273. GetChar();
  1274. }
  1275. return token.toString();
  1276. }
  1277. /** Parse and Translate a Math Term */
  1278. private ParseNode Term() {
  1279. ParseNode result = powerFactor();
  1280. while(true) {
  1281. SkipWhite();
  1282. Ptg operator;
  1283. switch(look) {
  1284. case '*':
  1285. Match('*');
  1286. operator = MultiplyPtg.instance;
  1287. break;
  1288. case '/':
  1289. Match('/');
  1290. operator = DividePtg.instance;
  1291. break;
  1292. default:
  1293. return result; // finished with Term
  1294. }
  1295. ParseNode other = powerFactor();
  1296. result = new ParseNode(operator, result, other);
  1297. }
  1298. }
  1299. private ParseNode unionExpression() {
  1300. ParseNode result = comparisonExpression();
  1301. boolean hasUnions = false;
  1302. while (true) {
  1303. SkipWhite();
  1304. switch(look) {
  1305. case ',':
  1306. GetChar();
  1307. hasUnions = true;
  1308. ParseNode other = comparisonExpression();
  1309. result = new ParseNode(UnionPtg.instance, result, other);
  1310. continue;
  1311. }
  1312. if (hasUnions) {
  1313. return augmentWithMemPtg(result);
  1314. }
  1315. return result;
  1316. }
  1317. }
  1318. private ParseNode comparisonExpression() {
  1319. ParseNode result = concatExpression();
  1320. while (true) {
  1321. SkipWhite();
  1322. switch(look) {
  1323. case '=':
  1324. case '>':
  1325. case '<':
  1326. Ptg comparisonToken = getComparisonToken();
  1327. ParseNode other = concatExpression();
  1328. result = new ParseNode(comparisonToken, result, other);
  1329. continue;
  1330. }
  1331. return result; // finished with predicate expression
  1332. }
  1333. }
  1334. private Ptg getComparisonToken() {
  1335. if(look == '=') {
  1336. Match(look);
  1337. return EqualPtg.instance;
  1338. }
  1339. boolean isGreater = look == '>';
  1340. Match(look);
  1341. if(isGreater) {
  1342. if(look == '=') {
  1343. Match('=');
  1344. return GreaterEqualPtg.instance;
  1345. }
  1346. return GreaterThanPtg.instance;
  1347. }
  1348. switch(look) {
  1349. case '=':
  1350. Match('=');
  1351. return LessEqualPtg.instance;
  1352. case '>':
  1353. Match('>');
  1354. return NotEqualPtg.instance;
  1355. }
  1356. return LessThanPtg.instance;
  1357. }
  1358. private ParseNode concatExpression() {
  1359. ParseNode result = additiveExpression();
  1360. while (true) {
  1361. SkipWhite();
  1362. if(look != '&') {
  1363. break; // finished with concat expression
  1364. }
  1365. Match('&');
  1366. ParseNode other = additiveExpression();
  1367. result = new ParseNode(ConcatPtg.instance, result, other);
  1368. }
  1369. return result;
  1370. }
  1371. /** Parse and Translate an Expression */
  1372. private ParseNode additiveExpression() {
  1373. ParseNode result = Term();
  1374. while (true) {
  1375. SkipWhite();
  1376. Ptg operator;
  1377. switch(look) {
  1378. case '+':
  1379. Match('+');
  1380. operator = AddPtg.instance;
  1381. break;
  1382. case '-':
  1383. Match('-');
  1384. operator = SubtractPtg.instance;
  1385. break;
  1386. default:
  1387. return result; // finished with additive expression
  1388. }
  1389. ParseNode other = Term();
  1390. result = new ParseNode(operator, result, other);
  1391. }
  1392. }
  1393. //{--------------------------------------------------------------}
  1394. //{ Parse and Translate an Assignment Statement }
  1395. /**
  1396. procedure Assignment;
  1397. var Name: string[8];
  1398. begin
  1399. Name := GetName;
  1400. Match('=');
  1401. Expression;
  1402. end;
  1403. **/
  1404. /**
  1405. * API call to execute the parsing of the formula
  1406. *
  1407. */
  1408. private void parse() {
  1409. _pointer=0;
  1410. GetChar();
  1411. _rootNode = unionExpression();
  1412. if(_pointer <= _formulaLength) {
  1413. String msg = "Unused input [" + _formulaString.substring(_pointer-1)
  1414. + "] after attempting to parse the formula [" + _formulaString + "]";
  1415. throw new FormulaParseException(msg);
  1416. }
  1417. }
  1418. private Ptg[] getRPNPtg(int formulaType) {
  1419. OperandClassTransformer oct = new OperandClassTransformer(formulaType);
  1420. // RVA is for 'operand class': 'reference', 'value', 'array'
  1421. oct.transformFormula(_rootNode);
  1422. return ParseNode.toTokenArray(_rootNode);
  1423. }
  1424. }