From 8aa0c858c602a80fc359401d18d024239ebcfce5 Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Sat, 29 Mar 2008 17:19:09 +0000 Subject: [PATCH] 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 --- build.xml | 2 +- src/documentation/content/xdocs/changes.xml | 1 + .../content/xdocs/hwpf/docoverview.xml | 5 +- .../content/xdocs/hwpf/index.xml | 2 +- src/documentation/content/xdocs/status.xml | 1 + .../apache/poi/hssf/model/FormulaParser.java | 26 +- .../poi/hssf/record/ExternalNameRecord.java | 252 +++--- .../record/aggregates/CFRecordsAggregate.java | 219 +++-- .../record/formula/AbstractFunctionPtg.java | 760 +----------------- .../poi/hssf/record/formula/FuncPtg.java | 43 +- .../poi/hssf/record/formula/FuncVarPtg.java | 30 +- .../formula/function/FunctionDataBuilder.java | 89 ++ .../formula/function/FunctionMetadata.java | 58 ++ .../function/FunctionMetadataReader.java | 129 +++ .../function/FunctionMetadataRegistry.java | 82 ++ .../usermodel/HSSFConditionalFormatting.java | 163 +++- .../apache/poi/hssf/usermodel/HSSFRow.java | 6 +- .../apache/poi/hssf/usermodel/HSSFSheet.java | 638 ++++++++------- .../font_metrics.properties | 0 .../function/functionMetadata-asGenerated.txt | 283 +++++++ .../formula/function/functionMetadata.txt | 287 +++++++ .../hssf/record/formula/functions/Pmt.java | 111 +-- .../src/org/apache/poi/hwpf/HWPFDocument.java | 2 +- .../apache/poi/hwpf/model/PicturesTable.java | 36 +- .../AllIndividualFunctionEvaluationTests.java | 3 +- .../record/formula/functions/TestPmt.java | 87 ++ .../org/apache/poi/hwpf/data/Bug44603.doc | Bin 0 -> 30208 bytes .../poi/hwpf/usermodel/TestPictures.java | 29 +- .../org/apache/poi/hssf/data/44693.xls | Bin 0 -> 40448 bytes .../poi/hssf/data/missingFuncs44675.xls | Bin 0 -> 16384 bytes .../poi/hssf/model/TestFormulaParser.java | 15 +- .../poi/hssf/record/AllRecordTests.java | 6 +- .../hssf/record/TestExternalNameRecord.java | 55 ++ .../hssf/record/formula/AllFormulaTests.java | 11 +- .../function/AllFormulaFunctionTests.java | 37 + .../ExcelFileFormatDocFunctionExtractor.java | 503 ++++++++++++ .../TestFunctionMetadataRegistry.java | 43 + .../TestParseMissingBuiltInFuncs.java | 79 ++ .../function/TestReadMissingBuiltInFuncs.java | 142 ++++ .../apache/poi/hssf/usermodel/TestBugs.java | 24 + 40 files changed, 2797 insertions(+), 1462 deletions(-) create mode 100644 src/java/org/apache/poi/hssf/record/formula/function/FunctionDataBuilder.java create mode 100644 src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadata.java create mode 100644 src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataReader.java create mode 100644 src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataRegistry.java rename src/resources/{fontmetrics => main}/font_metrics.properties (100%) create mode 100644 src/resources/main/org/apache/poi/hssf/record/formula/function/functionMetadata-asGenerated.txt create mode 100644 src/resources/main/org/apache/poi/hssf/record/formula/function/functionMetadata.txt create mode 100644 src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestPmt.java create mode 100644 src/scratchpad/testcases/org/apache/poi/hwpf/data/Bug44603.doc create mode 100644 src/testcases/org/apache/poi/hssf/data/44693.xls create mode 100644 src/testcases/org/apache/poi/hssf/data/missingFuncs44675.xls create mode 100644 src/testcases/org/apache/poi/hssf/record/TestExternalNameRecord.java create mode 100644 src/testcases/org/apache/poi/hssf/record/formula/function/AllFormulaFunctionTests.java create mode 100644 src/testcases/org/apache/poi/hssf/record/formula/function/ExcelFileFormatDocFunctionExtractor.java create mode 100644 src/testcases/org/apache/poi/hssf/record/formula/function/TestFunctionMetadataRegistry.java create mode 100644 src/testcases/org/apache/poi/hssf/record/formula/function/TestParseMissingBuiltInFuncs.java create mode 100644 src/testcases/org/apache/poi/hssf/record/formula/function/TestReadMissingBuiltInFuncs.java diff --git a/build.xml b/build.xml index 9dbe0c69a4..75801a4698 100644 --- a/build.xml +++ b/build.xml @@ -70,7 +70,7 @@ under the License. - + diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index b1504a7023..1aca38e385 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -36,6 +36,7 @@ + 44652 / 44603 - Improved handling of Pictures in Word Documents 44636 - Fix formula parsing of RefVPtg, which was causing #VALUE to be shown on subsequent edits 44627 - Improve the thread safety of POILogFactory 30311 - Initial support for Conditional Formatting diff --git a/src/documentation/content/xdocs/hwpf/docoverview.xml b/src/documentation/content/xdocs/hwpf/docoverview.xml index 0e9a0bb0f4..ac3afc50aa 100644 --- a/src/documentation/content/xdocs/hwpf/docoverview.xml +++ b/src/documentation/content/xdocs/hwpf/docoverview.xml @@ -33,8 +33,9 @@

The purpose of this document is to give a brief high level overview of the HWPF document format. This document does not go into in-depth technical - detail and is only meant as a supplement to the Microsoft Word 97 Binary - File Format freely available at Wotsit.org.

+ detail and is only meant as a supplement to the Microsoft Word 97-2007 + Binary File Format freely available from + Microsoft.

The OLE file format is not discussed in this document. It is assumed that the reader has a working knowledge of the POIFS API.

diff --git a/src/documentation/content/xdocs/hwpf/index.xml b/src/documentation/content/xdocs/hwpf/index.xml index f5e8afb2da..c7f58122e3 100644 --- a/src/documentation/content/xdocs/hwpf/index.xml +++ b/src/documentation/content/xdocs/hwpf/index.xml @@ -87,7 +87,7 @@
  • anything else that seems reasonable
  • -

    When you start start coding, you will not yet have write access to the +

    When you start coding, you will not yet have write access to the CVS repository. Please submit your patches to Bugzilla and nag Rainer Klute until he commits diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index d29d57ff8d..6b55026c78 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -33,6 +33,7 @@ + 44652 / 44603 - Improved handling of Pictures in Word Documents 44636 - Fix formula parsing of RefVPtg, which was causing #VALUE to be shown on subsequent edits 44627 - Improve the thread safety of POILogFactory 30311 - Initial support for Conditional Formatting diff --git a/src/java/org/apache/poi/hssf/model/FormulaParser.java b/src/java/org/apache/poi/hssf/model/FormulaParser.java index 07bb51483c..e6eb296702 100644 --- a/src/java/org/apache/poi/hssf/model/FormulaParser.java +++ b/src/java/org/apache/poi/hssf/model/FormulaParser.java @@ -25,6 +25,8 @@ import java.util.regex.Pattern; //import PTG's .. since we need everything, import * import org.apache.poi.hssf.record.formula.*; +import org.apache.poi.hssf.record.formula.function.FunctionMetadata; +import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry; @@ -369,7 +371,23 @@ public final class FormulaParser { */ private AbstractFunctionPtg getFunction(String name, int numArgs, List argumentPointers) { - AbstractFunctionPtg retval = new FuncVarPtg(name, (byte)numArgs); + boolean isVarArgs; + int funcIx; + FunctionMetadata fm = FunctionMetadataRegistry.getFunctionByName(name.toUpperCase()); + if(fm == null) { + // must be external function + isVarArgs = true; + funcIx = FunctionMetadataRegistry.FUNCTION_INDEX_EXTERNAL; + } else { + isVarArgs = !fm.hasFixedArgsLength(); + funcIx = fm.getIndex(); + } + AbstractFunctionPtg retval; + if(isVarArgs) { + retval = new FuncVarPtg(name, (byte)numArgs); + } else { + retval = new FuncPtg(funcIx, (byte)numArgs); + } if (!name.equals(AbstractFunctionPtg.FUNCTION_NAME_IF)) { // early return for everything else besides IF() return retval; @@ -969,6 +987,10 @@ end; // tAttrSpace comes *before* the operand it applies to, which may be consistent // with how the formula text appears but is against the RPN ordering assumed here } + if (attrPtg.isSemiVolatile()) { + // similar to tAttrSpace - RPN is violated + continue; + } } final OperationPtg o = (OperationPtg) ptg; @@ -979,7 +1001,7 @@ end; if(stack.isEmpty()) { String msg = "Too few arguments suppled to operation token (" + o.getClass().getName() + "). Expected (" + nOperands - + ") operands but got (" + (nOperands - j + 1) + ")"; + + ") operands but got (" + (nOperands - j - 1) + ")"; throw new IllegalStateException(msg); } operands[j] = (String) stack.pop(); diff --git a/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java b/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java index 771603c859..99890f805b 100755 --- a/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java +++ b/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java @@ -17,7 +17,6 @@ package org.apache.poi.hssf.record; -import java.util.List; import java.util.Stack; import org.apache.poi.hssf.record.formula.Ptg; @@ -27,153 +26,160 @@ import org.apache.poi.util.StringUtil; /** * EXTERNALNAME

    * - * @author josh micich + * @author Josh Micich */ public final class ExternalNameRecord extends Record { - public final static short sid = 0x23; // as per BIFF8. (some old versions used 0x223) + public final static short sid = 0x23; // as per BIFF8. (some old versions used 0x223) + + private static final int OPT_BUILTIN_NAME = 0x0001; + private static final int OPT_AUTOMATIC_LINK = 0x0002; + private static final int OPT_PICTURE_LINK = 0x0004; + private static final int OPT_STD_DOCUMENT_NAME = 0x0008; + private static final int OPT_OLE_LINK = 0x0010; +// private static final int OPT_CLIP_FORMAT_MASK = 0x7FE0; + private static final int OPT_ICONIFIED_PICTURE_LINK= 0x8000; - private static final int OPT_BUILTIN_NAME = 0x0001; - private static final int OPT_AUTOMATIC_LINK = 0x0002; - private static final int OPT_PICTURE_LINK = 0x0004; - private static final int OPT_STD_DOCUMENT_NAME = 0x0008; - private static final int OPT_OLE_LINK = 0x0010; -// private static final int OPT_CLIP_FORMAT_MASK = 0x7FE0; - private static final int OPT_ICONIFIED_PICTURE_LINK = 0x8000; + private short field_1_option_flag; + private short field_2_index; + private short field_3_not_used; + private String field_4_name; + private Ptg[] field_5_name_definition; // TODO - junits for name definition field - - private short field_1_option_flag; - private short field_2_index; - private short field_3_not_used; - private String field_4_name; - private Stack field_5_name_definition; - - - public ExternalNameRecord(RecordInputStream in) { - super(in); - } + public ExternalNameRecord(RecordInputStream in) { + super(in); + } /** * Convenience Function to determine if the name is a built-in name */ public boolean isBuiltInName() { - return (field_1_option_flag & OPT_BUILTIN_NAME) != 0; + return (field_1_option_flag & OPT_BUILTIN_NAME) != 0; + } + /** + * For OLE and DDE, links can be either 'automatic' or 'manual' + */ + public boolean isAutomaticLink() { + return (field_1_option_flag & OPT_AUTOMATIC_LINK) != 0; + } + /** + * only for OLE and DDE + */ + public boolean isPicureLink() { + return (field_1_option_flag & OPT_PICTURE_LINK) != 0; + } + /** + * DDE links only. If true, this denotes the 'StdDocumentName' + */ + public boolean isStdDocumentNameIdentifier() { + return (field_1_option_flag & OPT_STD_DOCUMENT_NAME) != 0; + } + public boolean isOLELink() { + return (field_1_option_flag & OPT_OLE_LINK) != 0; + } + public boolean isIconifiedPictureLink() { + return (field_1_option_flag & OPT_ICONIFIED_PICTURE_LINK) != 0; + } + /** + * @return the standard String representation of this name + */ + public String getText() { + return field_4_name; } - /** - * For OLE and DDE, links can be either 'automatic' or 'manual' - */ - public boolean isAutomaticLink() { - return (field_1_option_flag & OPT_AUTOMATIC_LINK) != 0; - } - /** - * only for OLE and DDE - */ - public boolean isPicureLink() { - return (field_1_option_flag & OPT_PICTURE_LINK) != 0; - } - /** - * DDE links only. If true, this denotes the 'StdDocumentName' - */ - public boolean isStdDocumentNameIdentifier() { - return (field_1_option_flag & OPT_STD_DOCUMENT_NAME) != 0; - } - public boolean isOLELink() { - return (field_1_option_flag & OPT_OLE_LINK) != 0; - } - public boolean isIconifiedPictureLink() { - return (field_1_option_flag & OPT_ICONIFIED_PICTURE_LINK) != 0; - } - /** - * @return the standard String representation of this name - */ - public String getText() { - return field_4_name; - } - /** - * called by constructor, should throw runtime exception in the event of a - * record passed with a differing ID. - * - * @param id alleged id for this record - */ - protected void validateSid(short id) { - if (id != sid) { - throw new RecordFormatException("NOT A valid ExternalName RECORD"); - } - } + /** + * called by constructor, should throw runtime exception in the event of a + * record passed with a differing ID. + * + * @param id alleged id for this record + */ + protected void validateSid(short id) { + if (id != sid) { + throw new RecordFormatException("NOT A valid ExternalName RECORD"); + } + } - private int getDataSize(){ - return 2 + 2 + field_4_name.length() + 2 + getNameDefinitionSize(); - } + private int getDataSize(){ + return 3 * 2 // 3 short fields + + 2 + field_4_name.length() // nameLen and name + + 2 + getNameDefinitionSize(); // nameDefLen and nameDef + } - /** - * called by the class that is responsible for writing this sucker. - * Subclasses should implement this so that their data is passed back in a - * byte array. - * - * @param offset to begin writing at - * @param data byte array containing instance data - * @return number of bytes written - */ - public int serialize( int offset, byte[] data ) { - // TODO - junit tests - int dataSize = getDataSize(); + /** + * called by the class that is responsible for writing this sucker. + * Subclasses should implement this so that their data is passed back in a + * byte array. + * + * @param offset to begin writing at + * @param data byte array containing instance data + * @return number of bytes written + */ + public int serialize( int offset, byte[] data ) { + int dataSize = getDataSize(); - LittleEndian.putShort( data, 0 + offset, sid ); + LittleEndian.putShort( data, 0 + offset, sid ); LittleEndian.putShort( data, 2 + offset, (short) dataSize ); - LittleEndian.putShort( data, 4 + offset, field_1_option_flag ); - LittleEndian.putShort( data, 6 + offset, field_2_index ); - LittleEndian.putShort( data, 8 + offset, field_3_not_used ); - short nameLen = (short) field_4_name.length(); + LittleEndian.putShort( data, 4 + offset, field_1_option_flag ); + LittleEndian.putShort( data, 6 + offset, field_2_index ); + LittleEndian.putShort( data, 8 + offset, field_3_not_used ); + short nameLen = (short) field_4_name.length(); LittleEndian.putShort( data, 10 + offset, nameLen ); - StringUtil.putCompressedUnicode( field_4_name, data, 10 + offset ); - short defLen = (short) getNameDefinitionSize(); - LittleEndian.putShort( data, 12 + nameLen + offset, defLen ); - Ptg.serializePtgStack(field_5_name_definition, data, 12 + nameLen + offset ); + StringUtil.putCompressedUnicode( field_4_name, data, 12 + offset ); + short defLen = (short) getNameDefinitionSize(); + LittleEndian.putShort( data, 12 + nameLen + offset, defLen ); + Ptg.serializePtgStack(toStack(field_5_name_definition), data, 14 + nameLen + offset ); return dataSize + 4; - } + } - private int getNameDefinitionSize() { - int result = 0; - List list = field_5_name_definition; - - for (int k = 0; k < list.size(); k++) - { - Ptg ptg = ( Ptg ) list.get(k); - - result += ptg.getSize(); - } - return result; - } + private int getNameDefinitionSize() { + int result = 0; + for (int i = 0; i < field_5_name_definition.length; i++) { + result += field_5_name_definition[i].getSize(); + } + return result; + } - public int getRecordSize(){ - return 6 + 2 + field_4_name.length() + 2 + getNameDefinitionSize(); - } + public int getRecordSize(){ + return 4 + getDataSize(); + } - protected void fillFields(RecordInputStream in) { - field_1_option_flag = in.readShort(); - field_2_index = in.readShort(); - field_3_not_used = in.readShort(); - short nameLength = in.readShort(); - field_4_name = in.readCompressedUnicode(nameLength); - short formulaLen = in.readShort(); - field_5_name_definition = Ptg.createParsedExpressionTokens(formulaLen, in); - } + protected void fillFields(RecordInputStream in) { + field_1_option_flag = in.readShort(); + field_2_index = in.readShort(); + field_3_not_used = in.readShort(); + short nameLength = in.readShort(); + field_4_name = in.readCompressedUnicode(nameLength); + short formulaLen = in.readShort(); + field_5_name_definition = toPtgArray(Ptg.createParsedExpressionTokens(formulaLen, in)); + } - public short getSid() { - return sid; - } + private static Ptg[] toPtgArray(Stack s) { + Ptg[] result = new Ptg[s.size()]; + s.toArray(result); + return result; + } + private static Stack toStack(Ptg[] ptgs) { + Stack result = new Stack(); + for (int i = 0; i < ptgs.length; i++) { + result.push(ptgs[i]); + } + return result; + } + + public short getSid() { + return sid; + } - public String toString() { - StringBuffer sb = new StringBuffer(); - sb.append(getClass().getName()).append(" [EXTERNALNAME "); - sb.append(" ").append(field_4_name); - sb.append(" ix=").append(field_2_index); - sb.append("]"); - return sb.toString(); - } + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append(getClass().getName()).append(" [EXTERNALNAME "); + sb.append(" ").append(field_4_name); + sb.append(" ix=").append(field_2_index); + sb.append("]"); + return sb.toString(); + } } diff --git a/src/java/org/apache/poi/hssf/record/aggregates/CFRecordsAggregate.java b/src/java/org/apache/poi/hssf/record/aggregates/CFRecordsAggregate.java index 78d1ecbd33..d6e81c7ea2 100644 --- a/src/java/org/apache/poi/hssf/record/aggregates/CFRecordsAggregate.java +++ b/src/java/org/apache/poi/hssf/record/aggregates/CFRecordsAggregate.java @@ -35,54 +35,55 @@ import org.apache.poi.util.POILogger; * @author Dmitriy Kumshayev * */ -public class CFRecordsAggregate extends Record +public final class CFRecordsAggregate extends Record { - public final static short sid = -2008; + public final static short sid = -2008; // not a real BIFF record - private static POILogger log = POILogFactory.getLogger(CFRecordsAggregate.class); - - private CFHeaderRecord header; + private static POILogger log = POILogFactory.getLogger(CFRecordsAggregate.class); + + private CFHeaderRecord header; + + // List of CFRuleRecord objects + private final List rules; + + public CFRecordsAggregate() + { + header = null; + rules = new ArrayList(3); + } + + /** + * Create CFRecordsAggregate from a list of CF Records + * @param recs - list of {@link Record} objects + * @param offset - position of {@link CFHeaderRecord} object in the list of Record objects + * @return CFRecordsAggregate object + */ + public static CFRecordsAggregate createCFAggregate(List recs, int pOffset) + { + + int offset = pOffset; + CFRecordsAggregate cfRecords = new CFRecordsAggregate(); + ArrayList records = new ArrayList(4); + + Record rec = ( Record ) recs.get(offset++); + + if (rec.getSid() == CFHeaderRecord.sid) + { + records.add(rec); + cfRecords.header = (CFHeaderRecord)rec; + + int nRules = cfRecords.header.getNumberOfConditionalFormats(); + int rulesCount = 0; + while( offset0 ) { header.setNumberOfConditionalFormats(rules.size()); - pos += (( Record ) header).serialize(pos, data); + pos += (( Record ) header).serialize(pos, data); - for(Iterator itr = rules.iterator(); itr.hasNext();) - { - pos += (( Record ) itr.next()).serialize(pos, data); - } + for(Iterator itr = rules.iterator(); itr.hasNext();) + { + pos += (( Record ) itr.next()).serialize(pos, data); + } } - return pos - offset; + return pos - offset; } - + protected void validateSid(short id) { // do nothing here @@ -179,21 +180,21 @@ public class CFRecordsAggregate extends Record */ public int getRecordSize() { - int size = 0; - if( header != null) - { - size += header.getRecordSize(); - } - if( rules != null) - { - for(Iterator irecs = rules.iterator(); irecs.hasNext(); ) - { - size += (( Record ) irecs.next()).getRecordSize(); - } - } - return size; - } - + int size = 0; + if( header != null) + { + size += header.getRecordSize(); + } + if( rules != null) + { + for(Iterator irecs = rules.iterator(); irecs.hasNext(); ) + { + size += (( Record ) irecs.next()).getRecordSize(); + } + } + return size; + } + /** * String representation of CFRecordsAggregate */ @@ -206,19 +207,15 @@ public class CFRecordsAggregate extends Record { buffer.append(header.toString()); } - if( rules != null ) + for(int i=0; i").append("\n") - .append(" field_1_num_args=").append(field_1_num_args).append("\n") - .append(" name =").append(lookupName(field_2_fnc_index)).append("\n") - .append(" field_2_fnc_index=").append(field_2_fnc_index).append("\n") - .append(""); - return buffer.toString(); + StringBuffer sb = new StringBuffer(64); + sb.append(getClass().getName()).append(" ["); + sb.append(field_2_fnc_index).append(" ").append(field_1_num_args); + sb.append("]"); + return sb.toString(); } public int getType() { @@ -117,11 +113,19 @@ public abstract class AbstractFunctionPtg extends OperationPtg { * false if the name should be assumed to be an external function. */ public static final boolean isInternalFunctionName(String name) { - return map.containsValue(name.toUpperCase()); + short ix = FunctionMetadataRegistry.lookupIndexByName(name.toUpperCase()); + return ix >= 0; } protected String lookupName(short index) { - return ((String)map.get(new Integer(index))); + if(index == FunctionMetadataRegistry.FUNCTION_INDEX_EXTERNAL) { + return "#external#"; + } + FunctionMetadata fm = FunctionMetadataRegistry.getFunctionByIndex(index); + if(fm == null) { + throw new RuntimeException("bad function index (" + index + ")"); + } + return fm.getName(); } /** @@ -131,729 +135,11 @@ public abstract class AbstractFunctionPtg extends OperationPtg { * @return the standard worksheet function index if found, otherwise FUNCTION_INDEX_EXTERNAL */ protected static short lookupIndex(String name) { - Integer index = (Integer) map.getKeyForValue(name.toUpperCase()); - if (index != null) return index.shortValue(); - return FUNCTION_INDEX_EXTERNAL; - } - - /** - * Produces the function table hashmap - */ - private static BinaryTree produceHash() { - BinaryTree dmap = new BinaryTree(); - - dmap.put(new Integer(0),"COUNT"); - dmap.put(new Integer(1),FUNCTION_NAME_IF); - dmap.put(new Integer(2),"ISNA"); - dmap.put(new Integer(3),"ISERROR"); - dmap.put(new Integer(4),"SUM"); - dmap.put(new Integer(5),"AVERAGE"); - dmap.put(new Integer(6),"MIN"); - dmap.put(new Integer(7),"MAX"); - dmap.put(new Integer(8),"ROW"); - dmap.put(new Integer(9),"COLUMN"); - dmap.put(new Integer(10),"NA"); - dmap.put(new Integer(11),"NPV"); - dmap.put(new Integer(12),"STDEV"); - dmap.put(new Integer(13),"DOLLAR"); - dmap.put(new Integer(14),"FIXED"); - dmap.put(new Integer(15),"SIN"); - dmap.put(new Integer(16),"COS"); - dmap.put(new Integer(17),"TAN"); - dmap.put(new Integer(18),"ATAN"); - dmap.put(new Integer(19),"PI"); - dmap.put(new Integer(20),"SQRT"); - dmap.put(new Integer(21),"EXP"); - dmap.put(new Integer(22),"LN"); - dmap.put(new Integer(23),"LOG10"); - dmap.put(new Integer(24),"ABS"); - dmap.put(new Integer(25),"INT"); - dmap.put(new Integer(26),"SIGN"); - dmap.put(new Integer(27),"ROUND"); - dmap.put(new Integer(28),"LOOKUP"); - dmap.put(new Integer(29),"INDEX"); - dmap.put(new Integer(30),"REPT"); - dmap.put(new Integer(31),"MID"); - dmap.put(new Integer(32),"LEN"); - dmap.put(new Integer(33),"VALUE"); - dmap.put(new Integer(34),"TRUE"); - dmap.put(new Integer(35),"FALSE"); - dmap.put(new Integer(36),"AND"); - dmap.put(new Integer(37),"OR"); - dmap.put(new Integer(38),"NOT"); - dmap.put(new Integer(39),"MOD"); - dmap.put(new Integer(40),"DCOUNT"); - dmap.put(new Integer(41),"DSUM"); - dmap.put(new Integer(42),"DAVERAGE"); - dmap.put(new Integer(43),"DMIN"); - dmap.put(new Integer(44),"DMAX"); - dmap.put(new Integer(45),"DSTDEV"); - dmap.put(new Integer(46),"VAR"); - dmap.put(new Integer(47),"DVAR"); - dmap.put(new Integer(48),"TEXT"); - dmap.put(new Integer(49),"LINEST"); - dmap.put(new Integer(50),"TREND"); - dmap.put(new Integer(51),"LOGEST"); - dmap.put(new Integer(52),"GROWTH"); - dmap.put(new Integer(53),"GOTO"); - dmap.put(new Integer(54),"HALT"); - dmap.put(new Integer(56),"PV"); - dmap.put(new Integer(57),"FV"); - dmap.put(new Integer(58),"NPER"); - dmap.put(new Integer(59),"PMT"); - dmap.put(new Integer(60),"RATE"); - dmap.put(new Integer(61),"MIRR"); - dmap.put(new Integer(62),"IRR"); - dmap.put(new Integer(63),"RAND"); - dmap.put(new Integer(64),"MATCH"); - dmap.put(new Integer(65),"DATE"); - dmap.put(new Integer(66),"TIME"); - dmap.put(new Integer(67),"DAY"); - dmap.put(new Integer(68),"MONTH"); - dmap.put(new Integer(69),"YEAR"); - dmap.put(new Integer(70),"WEEKDAY"); - dmap.put(new Integer(71),"HOUR"); - dmap.put(new Integer(72),"MINUTE"); - dmap.put(new Integer(73),"SECOND"); - dmap.put(new Integer(74),"NOW"); - dmap.put(new Integer(75),"AREAS"); - dmap.put(new Integer(76),"ROWS"); - dmap.put(new Integer(77),"COLUMNS"); - dmap.put(new Integer(78),"OFFSET"); - dmap.put(new Integer(79),"ABSREF"); - dmap.put(new Integer(80),"RELREF"); - dmap.put(new Integer(81),"ARGUMENT"); - dmap.put(new Integer(82),"SEARCH"); - dmap.put(new Integer(83),"TRANSPOSE"); - dmap.put(new Integer(84),"ERROR"); - dmap.put(new Integer(85),"STEP"); - dmap.put(new Integer(86),"TYPE"); - dmap.put(new Integer(87),"ECHO"); - dmap.put(new Integer(88),"SETNAME"); - dmap.put(new Integer(89),"CALLER"); - dmap.put(new Integer(90),"DEREF"); - dmap.put(new Integer(91),"WINDOWS"); - dmap.put(new Integer(92),"SERIES"); - dmap.put(new Integer(93),"DOCUMENTS"); - dmap.put(new Integer(94),"ACTIVECELL"); - dmap.put(new Integer(95),"SELECTION"); - dmap.put(new Integer(96),"RESULT"); - dmap.put(new Integer(97),"ATAN2"); - dmap.put(new Integer(98),"ASIN"); - dmap.put(new Integer(99),"ACOS"); - dmap.put(new Integer(100),"CHOOSE"); - dmap.put(new Integer(101),"HLOOKUP"); - dmap.put(new Integer(102),"VLOOKUP"); - dmap.put(new Integer(103),"LINKS"); - dmap.put(new Integer(104),"INPUT"); - dmap.put(new Integer(105),"ISREF"); - dmap.put(new Integer(106),"GETFORMULA"); - dmap.put(new Integer(107),"GETNAME"); - dmap.put(new Integer(108),"SETVALUE"); - dmap.put(new Integer(109),"LOG"); - dmap.put(new Integer(110),"EXEC"); - dmap.put(new Integer(111),"CHAR"); - dmap.put(new Integer(112),"LOWER"); - dmap.put(new Integer(113),"UPPER"); - dmap.put(new Integer(114),"PROPER"); - dmap.put(new Integer(115),"LEFT"); - dmap.put(new Integer(116),"RIGHT"); - dmap.put(new Integer(117),"EXACT"); - dmap.put(new Integer(118),"TRIM"); - dmap.put(new Integer(119),"REPLACE"); - dmap.put(new Integer(120),"SUBSTITUTE"); - dmap.put(new Integer(121),"CODE"); - dmap.put(new Integer(122),"NAMES"); - dmap.put(new Integer(123),"DIRECTORY"); - dmap.put(new Integer(124),"FIND"); - dmap.put(new Integer(125),"CELL"); - dmap.put(new Integer(126),"ISERR"); - dmap.put(new Integer(127),"ISTEXT"); - dmap.put(new Integer(128),"ISNUMBER"); - dmap.put(new Integer(129),"ISBLANK"); - dmap.put(new Integer(130),"T"); - dmap.put(new Integer(131),"N"); - dmap.put(new Integer(132),"FOPEN"); - dmap.put(new Integer(133),"FCLOSE"); - dmap.put(new Integer(134),"FSIZE"); - dmap.put(new Integer(135),"FREADLN"); - dmap.put(new Integer(136),"FREAD"); - dmap.put(new Integer(137),"FWRITELN"); - dmap.put(new Integer(138),"FWRITE"); - dmap.put(new Integer(139),"FPOS"); - dmap.put(new Integer(140),"DATEVALUE"); - dmap.put(new Integer(141),"TIMEVALUE"); - dmap.put(new Integer(142),"SLN"); - dmap.put(new Integer(143),"SYD"); - dmap.put(new Integer(144),"DDB"); - dmap.put(new Integer(145),"GETDEF"); - dmap.put(new Integer(146),"REFTEXT"); - dmap.put(new Integer(147),"TEXTREF"); - dmap.put(new Integer(148),"INDIRECT"); - dmap.put(new Integer(149),"REGISTER"); - dmap.put(new Integer(150),"CALL"); - dmap.put(new Integer(151),"ADDBAR"); - dmap.put(new Integer(152),"ADDMENU"); - dmap.put(new Integer(153),"ADDCOMMAND"); - dmap.put(new Integer(154),"ENABLECOMMAND"); - dmap.put(new Integer(155),"CHECKCOMMAND"); - dmap.put(new Integer(156),"RENAMECOMMAND"); - dmap.put(new Integer(157),"SHOWBAR"); - dmap.put(new Integer(158),"DELETEMENU"); - dmap.put(new Integer(159),"DELETECOMMAND"); - dmap.put(new Integer(160),"GETCHARTITEM"); - dmap.put(new Integer(161),"DIALOGBOX"); - dmap.put(new Integer(162),"CLEAN"); - dmap.put(new Integer(163),"MDETERM"); - dmap.put(new Integer(164),"MINVERSE"); - dmap.put(new Integer(165),"MMULT"); - dmap.put(new Integer(166),"FILES"); - dmap.put(new Integer(167),"IPMT"); - dmap.put(new Integer(168),"PPMT"); - dmap.put(new Integer(169),"COUNTA"); - dmap.put(new Integer(170),"CANCELKEY"); - dmap.put(new Integer(175),"INITIATE"); - dmap.put(new Integer(176),"REQUEST"); - dmap.put(new Integer(177),"POKE"); - dmap.put(new Integer(178),"EXECUTE"); - dmap.put(new Integer(179),"TERMINATE"); - dmap.put(new Integer(180),"RESTART"); - dmap.put(new Integer(181),"HELP"); - dmap.put(new Integer(182),"GETBAR"); - dmap.put(new Integer(183),"PRODUCT"); - dmap.put(new Integer(184),"FACT"); - dmap.put(new Integer(185),"GETCELL"); - dmap.put(new Integer(186),"GETWORKSPACE"); - dmap.put(new Integer(187),"GETWINDOW"); - dmap.put(new Integer(188),"GETDOCUMENT"); - dmap.put(new Integer(189),"DPRODUCT"); - dmap.put(new Integer(190),"ISNONTEXT"); - dmap.put(new Integer(191),"GETNOTE"); - dmap.put(new Integer(192),"NOTE"); - dmap.put(new Integer(193),"STDEVP"); - dmap.put(new Integer(194),"VARP"); - dmap.put(new Integer(195),"DSTDEVP"); - dmap.put(new Integer(196),"DVARP"); - dmap.put(new Integer(197),"TRUNC"); - dmap.put(new Integer(198),"ISLOGICAL"); - dmap.put(new Integer(199),"DCOUNTA"); - dmap.put(new Integer(200),"DELETEBAR"); - dmap.put(new Integer(201),"UNREGISTER"); - dmap.put(new Integer(204),"USDOLLAR"); - dmap.put(new Integer(205),"FINDB"); - dmap.put(new Integer(206),"SEARCHB"); - dmap.put(new Integer(207),"REPLACEB"); - dmap.put(new Integer(208),"LEFTB"); - dmap.put(new Integer(209),"RIGHTB"); - dmap.put(new Integer(210),"MIDB"); - dmap.put(new Integer(211),"LENB"); - dmap.put(new Integer(212),"ROUNDUP"); - dmap.put(new Integer(213),"ROUNDDOWN"); - dmap.put(new Integer(214),"ASC"); - dmap.put(new Integer(215),"DBCS"); - dmap.put(new Integer(216),"RANK"); - dmap.put(new Integer(219),"ADDRESS"); - dmap.put(new Integer(220),"DAYS360"); - dmap.put(new Integer(221),"TODAY"); - dmap.put(new Integer(222),"VDB"); - dmap.put(new Integer(227),"MEDIAN"); - dmap.put(new Integer(228),"SUMPRODUCT"); - dmap.put(new Integer(229),"SINH"); - dmap.put(new Integer(230),"COSH"); - dmap.put(new Integer(231),"TANH"); - dmap.put(new Integer(232),"ASINH"); - dmap.put(new Integer(233),"ACOSH"); - dmap.put(new Integer(234),"ATANH"); - dmap.put(new Integer(235),"DGET"); - dmap.put(new Integer(236),"CREATEOBJECT"); - dmap.put(new Integer(237),"VOLATILE"); - dmap.put(new Integer(238),"LASTERROR"); - dmap.put(new Integer(239),"CUSTOMUNDO"); - dmap.put(new Integer(240),"CUSTOMREPEAT"); - dmap.put(new Integer(241),"FORMULACONVERT"); - dmap.put(new Integer(242),"GETLINKINFO"); - dmap.put(new Integer(243),"TEXTBOX"); - dmap.put(new Integer(244),"INFO"); - dmap.put(new Integer(245),"GROUP"); - dmap.put(new Integer(246),"GETOBJECT"); - dmap.put(new Integer(247),"DB"); - dmap.put(new Integer(248),"PAUSE"); - dmap.put(new Integer(250),"RESUME"); - dmap.put(new Integer(252),"FREQUENCY"); - dmap.put(new Integer(253),"ADDTOOLBAR"); - dmap.put(new Integer(254),"DELETETOOLBAR"); - dmap.put(new Integer(FUNCTION_INDEX_EXTERNAL),"externalflag"); - dmap.put(new Integer(256),"RESETTOOLBAR"); - dmap.put(new Integer(257),"EVALUATE"); - dmap.put(new Integer(258),"GETTOOLBAR"); - dmap.put(new Integer(259),"GETTOOL"); - dmap.put(new Integer(260),"SPELLINGCHECK"); - dmap.put(new Integer(261),"ERROR.TYPE"); - dmap.put(new Integer(262),"APPTITLE"); - dmap.put(new Integer(263),"WINDOWTITLE"); - dmap.put(new Integer(264),"SAVETOOLBAR"); - dmap.put(new Integer(265),"ENABLETOOL"); - dmap.put(new Integer(266),"PRESSTOOL"); - dmap.put(new Integer(267),"REGISTERID"); - dmap.put(new Integer(268),"GETWORKBOOK"); - dmap.put(new Integer(269),"AVEDEV"); - dmap.put(new Integer(270),"BETADIST"); - dmap.put(new Integer(271),"GAMMALN"); - dmap.put(new Integer(272),"BETAINV"); - dmap.put(new Integer(273),"BINOMDIST"); - dmap.put(new Integer(274),"CHIDIST"); - dmap.put(new Integer(275),"CHIINV"); - dmap.put(new Integer(276),"COMBIN"); - dmap.put(new Integer(277),"CONFIDENCE"); - dmap.put(new Integer(278),"CRITBINOM"); - dmap.put(new Integer(279),"EVEN"); - dmap.put(new Integer(280),"EXPONDIST"); - dmap.put(new Integer(281),"FDIST"); - dmap.put(new Integer(282),"FINV"); - dmap.put(new Integer(283),"FISHER"); - dmap.put(new Integer(284),"FISHERINV"); - dmap.put(new Integer(285),"FLOOR"); - dmap.put(new Integer(286),"GAMMADIST"); - dmap.put(new Integer(287),"GAMMAINV"); - dmap.put(new Integer(288),"CEILING"); - dmap.put(new Integer(289),"HYPGEOMDIST"); - dmap.put(new Integer(290),"LOGNORMDIST"); - dmap.put(new Integer(291),"LOGINV"); - dmap.put(new Integer(292),"NEGBINOMDIST"); - dmap.put(new Integer(293),"NORMDIST"); - dmap.put(new Integer(294),"NORMSDIST"); - dmap.put(new Integer(295),"NORMINV"); - dmap.put(new Integer(296),"NORMSINV"); - dmap.put(new Integer(297),"STANDARDIZE"); - dmap.put(new Integer(298),"ODD"); - dmap.put(new Integer(299),"PERMUT"); - dmap.put(new Integer(300),"POISSON"); - dmap.put(new Integer(301),"TDIST"); - dmap.put(new Integer(302),"WEIBULL"); - dmap.put(new Integer(303),"SUMXMY2"); - dmap.put(new Integer(304),"SUMX2MY2"); - dmap.put(new Integer(305),"SUMX2PY2"); - dmap.put(new Integer(306),"CHITEST"); - dmap.put(new Integer(307),"CORREL"); - dmap.put(new Integer(308),"COVAR"); - dmap.put(new Integer(309),"FORECAST"); - dmap.put(new Integer(310),"FTEST"); - dmap.put(new Integer(311),"INTERCEPT"); - dmap.put(new Integer(312),"PEARSON"); - dmap.put(new Integer(313),"RSQ"); - dmap.put(new Integer(314),"STEYX"); - dmap.put(new Integer(315),"SLOPE"); - dmap.put(new Integer(316),"TTEST"); - dmap.put(new Integer(317),"PROB"); - dmap.put(new Integer(318),"DEVSQ"); - dmap.put(new Integer(319),"GEOMEAN"); - dmap.put(new Integer(320),"HARMEAN"); - dmap.put(new Integer(321),"SUMSQ"); - dmap.put(new Integer(322),"KURT"); - dmap.put(new Integer(323),"SKEW"); - dmap.put(new Integer(324),"ZTEST"); - dmap.put(new Integer(325),"LARGE"); - dmap.put(new Integer(326),"SMALL"); - dmap.put(new Integer(327),"QUARTILE"); - dmap.put(new Integer(328),"PERCENTILE"); - dmap.put(new Integer(329),"PERCENTRANK"); - dmap.put(new Integer(330),"MODE"); - dmap.put(new Integer(331),"TRIMMEAN"); - dmap.put(new Integer(332),"TINV"); - dmap.put(new Integer(334),"MOVIECOMMAND"); - dmap.put(new Integer(335),"GETMOVIE"); - dmap.put(new Integer(336),"CONCATENATE"); - dmap.put(new Integer(337),"POWER"); - dmap.put(new Integer(338),"PIVOTADDDATA"); - dmap.put(new Integer(339),"GETPIVOTTABLE"); - dmap.put(new Integer(340),"GETPIVOTFIELD"); - dmap.put(new Integer(341),"GETPIVOTITEM"); - dmap.put(new Integer(342),"RADIANS"); - dmap.put(new Integer(343),"DEGREES"); - dmap.put(new Integer(344),"SUBTOTAL"); - dmap.put(new Integer(345),"SUMIF"); - dmap.put(new Integer(346),"COUNTIF"); - dmap.put(new Integer(347),"COUNTBLANK"); - dmap.put(new Integer(348),"SCENARIOGET"); - dmap.put(new Integer(349),"OPTIONSLISTSGET"); - dmap.put(new Integer(350),"ISPMT"); - dmap.put(new Integer(351),"DATEDIF"); - dmap.put(new Integer(352),"DATESTRING"); - dmap.put(new Integer(353),"NUMBERSTRING"); - dmap.put(new Integer(354),"ROMAN"); - dmap.put(new Integer(355),"OPENDIALOG"); - dmap.put(new Integer(356),"SAVEDIALOG"); - dmap.put(new Integer(357),"VIEWGET"); - dmap.put(new Integer(358),"GETPIVOTDATA"); - dmap.put(new Integer(359),"HYPERLINK"); - dmap.put(new Integer(360),"PHONETIC"); - dmap.put(new Integer(361),"AVERAGEA"); - dmap.put(new Integer(362),"MAXA"); - dmap.put(new Integer(363),"MINA"); - dmap.put(new Integer(364),"STDEVPA"); - dmap.put(new Integer(365),"VARPA"); - dmap.put(new Integer(366),"STDEVA"); - dmap.put(new Integer(367),"VARA"); - - return dmap; - } - - private static Object[][] produceFunctionData() { - Object [][] functionData = new Object[368][3]; - //return Class // Param Class //Num Params - functionData[0][0]=new Byte(Ptg.CLASS_VALUE);functionData[0][1]=new byte[] {Ptg.CLASS_REF};functionData[0][2]=new Integer(-1); - functionData[2][0]=new Byte(Ptg.CLASS_VALUE);functionData[2][1]=new byte[] {Ptg.CLASS_VALUE};functionData[2][2]=new Integer(1); - functionData[3][0]=new Byte(Ptg.CLASS_VALUE);functionData[3][1]=new byte[] {Ptg.CLASS_VALUE};functionData[3][2]=new Integer(1); - functionData[4][0]=new Byte(Ptg.CLASS_VALUE);functionData[4][1]=new byte[] {Ptg.CLASS_REF};functionData[4][2]=new Integer(-1); - functionData[5][0]=new Byte(Ptg.CLASS_VALUE);functionData[5][1]=new byte[] {Ptg.CLASS_REF};functionData[5][2]=new Integer(-1); - functionData[6][0]=new Byte(Ptg.CLASS_VALUE);functionData[6][1]=new byte[] {Ptg.CLASS_REF};functionData[6][2]=new Integer(-1); - functionData[7][0]=new Byte(Ptg.CLASS_VALUE);functionData[7][1]=new byte[] {Ptg.CLASS_REF};functionData[7][2]=new Integer(-1); - functionData[8][0]=new Byte(Ptg.CLASS_VALUE);functionData[8][1]=new byte[] {Ptg.CLASS_REF};functionData[8][2]=new Integer(-1); - functionData[9][0]=new Byte(Ptg.CLASS_VALUE);functionData[9][1]=new byte[] {Ptg.CLASS_REF};functionData[9][2]=new Integer(-1); - functionData[10][0]=new Byte(Ptg.CLASS_VALUE);functionData[10][1]=new byte[] {Ptg.CLASS_VALUE};functionData[10][2]=new Integer(0); - functionData[11][0]=new Byte(Ptg.CLASS_VALUE);functionData[11][1]=new byte[] {Ptg.CLASS_REF};functionData[11][2]=new Integer(-1); - functionData[12][0]=new Byte(Ptg.CLASS_VALUE);functionData[12][1]=new byte[] {Ptg.CLASS_REF};functionData[12][2]=new Integer(-1); - functionData[13][0]=new Byte(Ptg.CLASS_VALUE);functionData[13][1]=new byte[] {Ptg.CLASS_VALUE};functionData[13][2]=new Integer(-1); - functionData[14][0]=new Byte(Ptg.CLASS_VALUE);functionData[14][1]=new byte[] {Ptg.CLASS_VALUE};functionData[14][2]=new Integer(-1); - functionData[15][0]=new Byte(Ptg.CLASS_VALUE);functionData[15][1]=new byte[] {Ptg.CLASS_VALUE};functionData[15][2]=new Integer(1); - functionData[16][0]=new Byte(Ptg.CLASS_VALUE);functionData[16][1]=new byte[] {Ptg.CLASS_VALUE};functionData[16][2]=new Integer(1); - functionData[17][0]=new Byte(Ptg.CLASS_VALUE);functionData[17][1]=new byte[] {Ptg.CLASS_VALUE};functionData[17][2]=new Integer(1); - functionData[18][0]=new Byte(Ptg.CLASS_VALUE);functionData[18][1]=new byte[] {Ptg.CLASS_VALUE};functionData[18][2]=new Integer(1); - functionData[19][0]=new Byte(Ptg.CLASS_VALUE);functionData[19][1]=new byte[] {Ptg.CLASS_VALUE};functionData[19][2]=new Integer(0); - functionData[20][0]=new Byte(Ptg.CLASS_VALUE);functionData[20][1]=new byte[] {Ptg.CLASS_VALUE};functionData[20][2]=new Integer(1); - functionData[21][0]=new Byte(Ptg.CLASS_VALUE);functionData[21][1]=new byte[] {Ptg.CLASS_VALUE};functionData[21][2]=new Integer(1); - functionData[22][0]=new Byte(Ptg.CLASS_VALUE);functionData[22][1]=new byte[] {Ptg.CLASS_VALUE};functionData[22][2]=new Integer(1); - functionData[23][0]=new Byte(Ptg.CLASS_VALUE);functionData[23][1]=new byte[] {Ptg.CLASS_VALUE};functionData[23][2]=new Integer(1); - functionData[24][0]=new Byte(Ptg.CLASS_VALUE);functionData[24][1]=new byte[] {Ptg.CLASS_VALUE};functionData[24][2]=new Integer(1); - functionData[25][0]=new Byte(Ptg.CLASS_VALUE);functionData[25][1]=new byte[] {Ptg.CLASS_VALUE};functionData[25][2]=new Integer(1); - functionData[26][0]=new Byte(Ptg.CLASS_VALUE);functionData[26][1]=new byte[] {Ptg.CLASS_VALUE};functionData[26][2]=new Integer(1); - functionData[27][0]=new Byte(Ptg.CLASS_VALUE);functionData[27][1]=new byte[] {Ptg.CLASS_VALUE};functionData[27][2]=new Integer(2); - functionData[28][0]=new Byte(Ptg.CLASS_VALUE);functionData[28][1]=new byte[] {Ptg.CLASS_VALUE, Ptg.CLASS_REF};functionData[28][2]=new Integer(-1); - functionData[29][0]=new Byte(Ptg.CLASS_VALUE);functionData[29][1]=new byte[] {Ptg.CLASS_REF};functionData[29][2]=new Integer(-1); - functionData[30][0]=new Byte(Ptg.CLASS_VALUE);functionData[30][1]=new byte[] {Ptg.CLASS_VALUE};functionData[30][2]=new Integer(2); - functionData[31][0]=new Byte(Ptg.CLASS_VALUE);functionData[31][1]=new byte[] {Ptg.CLASS_VALUE};functionData[31][2]=new Integer(3); - functionData[32][0]=new Byte(Ptg.CLASS_VALUE);functionData[32][1]=new byte[] {Ptg.CLASS_VALUE};functionData[32][2]=new Integer(1); - functionData[33][0]=new Byte(Ptg.CLASS_VALUE);functionData[33][1]=new byte[] {Ptg.CLASS_VALUE};functionData[33][2]=new Integer(1); - functionData[34][0]=new Byte(Ptg.CLASS_VALUE);functionData[34][1]=new byte[] {Ptg.CLASS_VALUE};functionData[34][2]=new Integer(0); - functionData[35][0]=new Byte(Ptg.CLASS_VALUE);functionData[35][1]=new byte[] {Ptg.CLASS_VALUE};functionData[35][2]=new Integer(0); - functionData[36][0]=new Byte(Ptg.CLASS_VALUE);functionData[36][1]=new byte[] {Ptg.CLASS_REF};functionData[36][2]=new Integer(-1); - functionData[37][0]=new Byte(Ptg.CLASS_VALUE);functionData[37][1]=new byte[] {Ptg.CLASS_REF};functionData[37][2]=new Integer(-1); - functionData[38][0]=new Byte(Ptg.CLASS_VALUE);functionData[38][1]=new byte[] {Ptg.CLASS_VALUE};functionData[38][2]=new Integer(1); - functionData[39][0]=new Byte(Ptg.CLASS_VALUE);functionData[39][1]=new byte[] {Ptg.CLASS_VALUE};functionData[39][2]=new Integer(2); - functionData[40][0]=new Byte(Ptg.CLASS_VALUE);functionData[40][1]=new byte[] {Ptg.CLASS_REF};functionData[40][2]=new Integer(3); - functionData[41][0]=new Byte(Ptg.CLASS_VALUE);functionData[41][1]=new byte[] {Ptg.CLASS_REF};functionData[41][2]=new Integer(3); - functionData[42][0]=new Byte(Ptg.CLASS_VALUE);functionData[42][1]=new byte[] {Ptg.CLASS_REF};functionData[42][2]=new Integer(3); - functionData[43][0]=new Byte(Ptg.CLASS_VALUE);functionData[43][1]=new byte[] {Ptg.CLASS_REF};functionData[43][2]=new Integer(3); - functionData[44][0]=new Byte(Ptg.CLASS_VALUE);functionData[44][1]=new byte[] {Ptg.CLASS_REF};functionData[44][2]=new Integer(3); - functionData[45][0]=new Byte(Ptg.CLASS_VALUE);functionData[45][1]=new byte[] {Ptg.CLASS_REF};functionData[45][2]=new Integer(3); - functionData[46][0]=new Byte(Ptg.CLASS_VALUE);functionData[46][1]=new byte[] {Ptg.CLASS_REF};functionData[46][2]=new Integer(-1); - functionData[47][0]=new Byte(Ptg.CLASS_VALUE);functionData[47][1]=new byte[] {Ptg.CLASS_REF};functionData[47][2]=new Integer(3); - functionData[48][0]=new Byte(Ptg.CLASS_VALUE);functionData[48][1]=new byte[] {Ptg.CLASS_VALUE};functionData[48][2]=new Integer(2); - functionData[49][0]=new Byte(Ptg.CLASS_VALUE);functionData[49][1]=new byte[] {Ptg.CLASS_REF};functionData[49][2]=new Integer(-1); - functionData[50][0]=new Byte(Ptg.CLASS_VALUE);functionData[50][1]=new byte[] {Ptg.CLASS_REF};functionData[50][2]=new Integer(-1); - functionData[51][0]=new Byte(Ptg.CLASS_VALUE);functionData[51][1]=new byte[] {Ptg.CLASS_REF};functionData[51][2]=new Integer(-1); - functionData[52][0]=new Byte(Ptg.CLASS_VALUE);functionData[52][1]=new byte[] {Ptg.CLASS_REF};functionData[52][2]=new Integer(-1); - - - functionData[56][0]=new Byte(Ptg.CLASS_VALUE);functionData[56][1]=new byte[] {Ptg.CLASS_VALUE};functionData[56][2]=new Integer(-1); - functionData[57][0]=new Byte(Ptg.CLASS_VALUE);functionData[57][1]=new byte[] {Ptg.CLASS_VALUE};functionData[57][2]=new Integer(-1); - functionData[58][0]=new Byte(Ptg.CLASS_VALUE);functionData[58][1]=new byte[] {Ptg.CLASS_VALUE};functionData[58][2]=new Integer(-1); - functionData[59][0]=new Byte(Ptg.CLASS_VALUE);functionData[59][1]=new byte[] {Ptg.CLASS_VALUE};functionData[59][2]=new Integer(-1); - functionData[60][0]=new Byte(Ptg.CLASS_VALUE);functionData[60][1]=new byte[] {Ptg.CLASS_VALUE};functionData[60][2]=new Integer(-1); - functionData[61][0]=new Byte(Ptg.CLASS_VALUE);functionData[61][1]=new byte[] {Ptg.CLASS_VALUE};functionData[61][2]=new Integer(3); - functionData[62][0]=new Byte(Ptg.CLASS_VALUE);functionData[62][1]=new byte[] {Ptg.CLASS_REF};functionData[62][2]=new Integer(-1); - functionData[63][0]=new Byte(Ptg.CLASS_VALUE);functionData[63][1]=new byte[] {Ptg.CLASS_REF};functionData[63][2]=new Integer(1); - functionData[64][0]=new Byte(Ptg.CLASS_VALUE);functionData[64][1]=new byte[] {Ptg.CLASS_VALUE, Ptg.CLASS_REF};functionData[64][2]=new Integer(-1); - functionData[65][0]=new Byte(Ptg.CLASS_VALUE);functionData[65][1]=new byte[] {Ptg.CLASS_VALUE};functionData[65][2]=new Integer(3); - functionData[66][0]=new Byte(Ptg.CLASS_VALUE);functionData[66][1]=new byte[] {Ptg.CLASS_VALUE};functionData[66][2]=new Integer(3); - functionData[67][0]=new Byte(Ptg.CLASS_VALUE);functionData[67][1]=new byte[] {Ptg.CLASS_VALUE};functionData[67][2]=new Integer(1); - functionData[68][0]=new Byte(Ptg.CLASS_VALUE);functionData[68][1]=new byte[] {Ptg.CLASS_VALUE};functionData[68][2]=new Integer(1); - functionData[69][0]=new Byte(Ptg.CLASS_VALUE);functionData[69][1]=new byte[] {Ptg.CLASS_VALUE};functionData[69][2]=new Integer(1); - functionData[70][0]=new Byte(Ptg.CLASS_VALUE);functionData[70][1]=new byte[] {Ptg.CLASS_VALUE};functionData[70][2]=new Integer(-1); - functionData[71][0]=new Byte(Ptg.CLASS_VALUE);functionData[71][1]=new byte[] {Ptg.CLASS_VALUE};functionData[71][2]=new Integer(1); - functionData[72][0]=new Byte(Ptg.CLASS_VALUE);functionData[72][1]=new byte[] {Ptg.CLASS_VALUE};functionData[72][2]=new Integer(1); - functionData[73][0]=new Byte(Ptg.CLASS_VALUE);functionData[73][1]=new byte[] {Ptg.CLASS_VALUE};functionData[73][2]=new Integer(1); - functionData[74][0]=new Byte(Ptg.CLASS_VALUE);functionData[74][1]=new byte[] {Ptg.CLASS_REF};functionData[74][2]=new Integer(1); - functionData[75][0]=new Byte(Ptg.CLASS_VALUE);functionData[75][1]=new byte[] {Ptg.CLASS_REF};functionData[75][2]=new Integer(1); - functionData[76][0]=new Byte(Ptg.CLASS_VALUE);functionData[76][1]=new byte[] {Ptg.CLASS_REF};functionData[76][2]=new Integer(1); - functionData[77][0]=new Byte(Ptg.CLASS_VALUE);functionData[77][1]=new byte[] {Ptg.CLASS_REF};functionData[77][2]=new Integer(1); - functionData[78][0]=new Byte(Ptg.CLASS_VALUE);functionData[78][1]=new byte[] {Ptg.CLASS_VALUE};functionData[78][2]=new Integer(-1); - - - - functionData[82][0]=new Byte(Ptg.CLASS_VALUE);functionData[82][1]=new byte[] {Ptg.CLASS_VALUE};functionData[82][2]=new Integer(-1); - functionData[83][0]=new Byte(Ptg.CLASS_VALUE);functionData[83][1]=new byte[] {Ptg.CLASS_VALUE};functionData[83][2]=new Integer(1); - - - functionData[86][0]=new Byte(Ptg.CLASS_VALUE);functionData[86][1]=new byte[] {Ptg.CLASS_VALUE};functionData[86][2]=new Integer(1); - - - - - - - - - - - functionData[97][0]=new Byte(Ptg.CLASS_VALUE);functionData[97][1]=new byte[] {Ptg.CLASS_VALUE};functionData[97][2]=new Integer(2); - functionData[98][0]=new Byte(Ptg.CLASS_VALUE);functionData[98][1]=new byte[] {Ptg.CLASS_VALUE};functionData[98][2]=new Integer(1); - functionData[99][0]=new Byte(Ptg.CLASS_VALUE);functionData[99][1]=new byte[] {Ptg.CLASS_VALUE};functionData[99][2]=new Integer(1); - - functionData[101][0]=new Byte(Ptg.CLASS_VALUE);functionData[101][1]=new byte[] {Ptg.CLASS_REF};functionData[101][2]=new Integer(-1); - functionData[102][0]=new Byte(Ptg.CLASS_VALUE);functionData[102][1]=new byte[] {Ptg.CLASS_REF};functionData[102][2]=new Integer(-1); - - - functionData[105][0]=new Byte(Ptg.CLASS_VALUE);functionData[105][1]=new byte[] {Ptg.CLASS_REF};functionData[105][2]=new Integer(1); - - - - functionData[109][0]=new Byte(Ptg.CLASS_VALUE);functionData[109][1]=new byte[] {Ptg.CLASS_VALUE};functionData[109][2]=new Integer(-1); - - functionData[111][0]=new Byte(Ptg.CLASS_VALUE);functionData[111][1]=new byte[] {Ptg.CLASS_VALUE};functionData[111][2]=new Integer(1); - functionData[112][0]=new Byte(Ptg.CLASS_VALUE);functionData[112][1]=new byte[] {Ptg.CLASS_VALUE};functionData[112][2]=new Integer(1); - functionData[113][0]=new Byte(Ptg.CLASS_VALUE);functionData[113][1]=new byte[] {Ptg.CLASS_VALUE};functionData[113][2]=new Integer(1); - functionData[114][0]=new Byte(Ptg.CLASS_VALUE);functionData[114][1]=new byte[] {Ptg.CLASS_VALUE};functionData[114][2]=new Integer(1); - functionData[115][0]=new Byte(Ptg.CLASS_VALUE);functionData[115][1]=new byte[] {Ptg.CLASS_VALUE};functionData[115][2]=new Integer(-1); - functionData[116][0]=new Byte(Ptg.CLASS_VALUE);functionData[116][1]=new byte[] {Ptg.CLASS_VALUE};functionData[116][2]=new Integer(-1); - functionData[117][0]=new Byte(Ptg.CLASS_VALUE);functionData[117][1]=new byte[] {Ptg.CLASS_VALUE};functionData[117][2]=new Integer(2); - functionData[118][0]=new Byte(Ptg.CLASS_VALUE);functionData[118][1]=new byte[] {Ptg.CLASS_VALUE};functionData[118][2]=new Integer(1); - functionData[119][0]=new Byte(Ptg.CLASS_VALUE);functionData[119][1]=new byte[] {Ptg.CLASS_VALUE};functionData[119][2]=new Integer(4); - functionData[120][0]=new Byte(Ptg.CLASS_VALUE);functionData[120][1]=new byte[] {Ptg.CLASS_VALUE};functionData[120][2]=new Integer(-1); - functionData[121][0]=new Byte(Ptg.CLASS_VALUE);functionData[121][1]=new byte[] {Ptg.CLASS_VALUE};functionData[121][2]=new Integer(1); - - - functionData[124][0]=new Byte(Ptg.CLASS_VALUE);functionData[124][1]=new byte[] {Ptg.CLASS_VALUE};functionData[124][2]=new Integer(-1); - functionData[125][0]=new Byte(Ptg.CLASS_VALUE);functionData[125][1]=new byte[] {Ptg.CLASS_VALUE};functionData[125][2]=new Integer(-1); - functionData[126][0]=new Byte(Ptg.CLASS_VALUE);functionData[126][1]=new byte[] {Ptg.CLASS_VALUE};functionData[126][2]=new Integer(1); - functionData[127][0]=new Byte(Ptg.CLASS_VALUE);functionData[127][1]=new byte[] {Ptg.CLASS_VALUE};functionData[127][2]=new Integer(1); - functionData[128][0]=new Byte(Ptg.CLASS_VALUE);functionData[128][1]=new byte[] {Ptg.CLASS_VALUE};functionData[128][2]=new Integer(1); - functionData[129][0]=new Byte(Ptg.CLASS_VALUE);functionData[129][1]=new byte[] {Ptg.CLASS_VALUE};functionData[129][2]=new Integer(1); - functionData[130][0]=new Byte(Ptg.CLASS_VALUE);functionData[130][1]=new byte[] {Ptg.CLASS_REF};functionData[130][2]=new Integer(1); - functionData[131][0]=new Byte(Ptg.CLASS_VALUE);functionData[131][1]=new byte[] {Ptg.CLASS_REF};functionData[131][2]=new Integer(1); - - - - - - - - - functionData[140][0]=new Byte(Ptg.CLASS_VALUE);functionData[140][1]=new byte[] {Ptg.CLASS_VALUE};functionData[140][2]=new Integer(1); - functionData[141][0]=new Byte(Ptg.CLASS_VALUE);functionData[141][1]=new byte[] {Ptg.CLASS_VALUE};functionData[141][2]=new Integer(1); - functionData[142][0]=new Byte(Ptg.CLASS_VALUE);functionData[142][1]=new byte[] {Ptg.CLASS_VALUE};functionData[142][2]=new Integer(3); - - - - - - functionData[148][0]=new Byte(Ptg.CLASS_VALUE);functionData[148][1]=new byte[] {Ptg.CLASS_VALUE};functionData[148][2]=new Integer(-1); - - functionData[150][0]=new Byte(Ptg.CLASS_VALUE);functionData[150][1]=new byte[] {Ptg.CLASS_VALUE};functionData[150][2]=new Integer(-1); - - - - - - - - - - - - functionData[162][0]=new Byte(Ptg.CLASS_VALUE);functionData[162][1]=new byte[] {Ptg.CLASS_VALUE};functionData[162][2]=new Integer(1); - functionData[163][0]=new Byte(Ptg.CLASS_VALUE);functionData[163][1]=new byte[] {Ptg.CLASS_ARRAY};functionData[163][2]=new Integer(1); - functionData[164][0]=new Byte(Ptg.CLASS_VALUE);functionData[164][1]=new byte[] {Ptg.CLASS_ARRAY};functionData[164][2]=new Integer(1); - functionData[165][0]=new Byte(Ptg.CLASS_VALUE);functionData[165][1]=new byte[] {Ptg.CLASS_ARRAY};functionData[165][2]=new Integer(2); - functionData[166][0]=new Byte(Ptg.CLASS_VALUE);functionData[166][1]=new byte[] {Ptg.CLASS_VALUE};functionData[166][2]=new Integer(-1); - functionData[167][0]=new Byte(Ptg.CLASS_VALUE);functionData[167][1]=new byte[] {Ptg.CLASS_VALUE};functionData[167][2]=new Integer(-1); - functionData[168][0]=new Byte(Ptg.CLASS_VALUE);functionData[168][1]=new byte[] {Ptg.CLASS_REF};functionData[168][2]=new Integer(-1); - - - - - - - - - - - functionData[183][0]=new Byte(Ptg.CLASS_VALUE);functionData[183][1]=new byte[] {Ptg.CLASS_REF};functionData[183][2]=new Integer(-1); - functionData[184][0]=new Byte(Ptg.CLASS_VALUE);functionData[184][1]=new byte[] {Ptg.CLASS_VALUE};functionData[184][2]=new Integer(1); - - - - - functionData[189][0]=new Byte(Ptg.CLASS_VALUE);functionData[189][1]=new byte[] {Ptg.CLASS_REF};functionData[189][2]=new Integer(3); - functionData[190][0]=new Byte(Ptg.CLASS_VALUE);functionData[190][1]=new byte[] {Ptg.CLASS_VALUE};functionData[190][2]=new Integer(1); - - - functionData[193][0]=new Byte(Ptg.CLASS_VALUE);functionData[193][1]=new byte[] {Ptg.CLASS_REF};functionData[193][2]=new Integer(-1); - functionData[194][0]=new Byte(Ptg.CLASS_VALUE);functionData[194][1]=new byte[] {Ptg.CLASS_REF};functionData[194][2]=new Integer(-1); - functionData[195][0]=new Byte(Ptg.CLASS_VALUE);functionData[195][1]=new byte[] {Ptg.CLASS_REF};functionData[195][2]=new Integer(3); - functionData[196][0]=new Byte(Ptg.CLASS_VALUE);functionData[196][1]=new byte[] {Ptg.CLASS_REF};functionData[196][2]=new Integer(3); - functionData[197][0]=new Byte(Ptg.CLASS_VALUE);functionData[197][1]=new byte[] {Ptg.CLASS_VALUE};functionData[197][2]=new Integer(-1); - functionData[198][0]=new Byte(Ptg.CLASS_VALUE);functionData[198][1]=new byte[] {Ptg.CLASS_VALUE};functionData[198][2]=new Integer(1); - functionData[199][0]=new Byte(Ptg.CLASS_VALUE);functionData[199][1]=new byte[] {Ptg.CLASS_REF};functionData[199][2]=new Integer(3); - - - functionData[204][0]=new Byte(Ptg.CLASS_VALUE);functionData[204][1]=new byte[] {Ptg.CLASS_VALUE};functionData[204][2]=new Integer(-1); - functionData[205][0]=new Byte(Ptg.CLASS_VALUE);functionData[205][1]=new byte[] {Ptg.CLASS_VALUE};functionData[205][2]=new Integer(-1); - functionData[206][0]=new Byte(Ptg.CLASS_VALUE);functionData[206][1]=new byte[] {Ptg.CLASS_VALUE};functionData[206][2]=new Integer(-1); - functionData[207][0]=new Byte(Ptg.CLASS_VALUE);functionData[207][1]=new byte[] {Ptg.CLASS_VALUE};functionData[207][2]=new Integer(3); - functionData[208][0]=new Byte(Ptg.CLASS_VALUE);functionData[208][1]=new byte[] {Ptg.CLASS_VALUE};functionData[208][2]=new Integer(1); - functionData[209][0]=new Byte(Ptg.CLASS_VALUE);functionData[209][1]=new byte[] {Ptg.CLASS_VALUE};functionData[209][2]=new Integer(2); - functionData[210][0]=new Byte(Ptg.CLASS_VALUE);functionData[210][1]=new byte[] {Ptg.CLASS_VALUE};functionData[210][2]=new Integer(2); - functionData[211][0]=new Byte(Ptg.CLASS_VALUE);functionData[211][1]=new byte[] {Ptg.CLASS_VALUE};functionData[211][2]=new Integer(1); - functionData[212][0]=new Byte(Ptg.CLASS_VALUE);functionData[212][1]=new byte[] {Ptg.CLASS_VALUE};functionData[212][2]=new Integer(2); - functionData[213][0]=new Byte(Ptg.CLASS_VALUE);functionData[213][1]=new byte[] {Ptg.CLASS_REF};functionData[213][2]=new Integer(2); - functionData[214][0]=new Byte(Ptg.CLASS_VALUE);functionData[214][1]=new byte[] {Ptg.CLASS_VALUE};functionData[214][2]=new Integer(-1); - - - - - functionData[221][0]=new Byte(Ptg.CLASS_VALUE);functionData[221][1]=new byte[] {Ptg.CLASS_REF};functionData[221][2]=new Integer(1); - functionData[222][0]=new Byte(Ptg.CLASS_VALUE);functionData[222][1]=new byte[] {Ptg.CLASS_VALUE};functionData[222][2]=new Integer(-1); - functionData[227][0]=new Byte(Ptg.CLASS_VALUE);functionData[227][1]=new byte[] {Ptg.CLASS_REF};functionData[227][2]=new Integer(-1); - functionData[228][0]=new Byte(Ptg.CLASS_VALUE);functionData[228][1]=new byte[] {Ptg.CLASS_ARRAY};functionData[228][2]=new Integer(-1); - functionData[229][0]=new Byte(Ptg.CLASS_VALUE);functionData[229][1]=new byte[] {Ptg.CLASS_VALUE};functionData[229][2]=new Integer(1); - functionData[230][0]=new Byte(Ptg.CLASS_VALUE);functionData[230][1]=new byte[] {Ptg.CLASS_VALUE};functionData[230][2]=new Integer(1); - functionData[231][0]=new Byte(Ptg.CLASS_VALUE);functionData[231][1]=new byte[] {Ptg.CLASS_VALUE};functionData[231][2]=new Integer(1); - functionData[232][0]=new Byte(Ptg.CLASS_VALUE);functionData[232][1]=new byte[] {Ptg.CLASS_VALUE};functionData[232][2]=new Integer(1); - functionData[233][0]=new Byte(Ptg.CLASS_VALUE);functionData[233][1]=new byte[] {Ptg.CLASS_VALUE};functionData[233][2]=new Integer(1); - functionData[234][0]=new Byte(Ptg.CLASS_VALUE);functionData[234][1]=new byte[] {Ptg.CLASS_VALUE};functionData[234][2]=new Integer(1); - functionData[235][0]=new Byte(Ptg.CLASS_VALUE);functionData[235][1]=new byte[] {Ptg.CLASS_REF};functionData[235][2]=new Integer(3); - - - - - - - - - functionData[244][0]=new Byte(Ptg.CLASS_VALUE);functionData[244][1]=new byte[] {Ptg.CLASS_VALUE};functionData[244][2]=new Integer(2); - - - - - - functionData[252][0]=new Byte(Ptg.CLASS_VALUE);functionData[252][1]=new byte[] {Ptg.CLASS_REF};functionData[252][2]=new Integer(2); - - - - - - - - functionData[261][0]=new Byte(Ptg.CLASS_VALUE);functionData[261][1]=new byte[] {Ptg.CLASS_VALUE};functionData[261][2]=new Integer(1); - - - - - - - - functionData[269][0]=new Byte(Ptg.CLASS_VALUE);functionData[269][1]=new byte[] {Ptg.CLASS_REF};functionData[269][2]=new Integer(-1); - functionData[270][0]=new Byte(Ptg.CLASS_VALUE);functionData[270][1]=new byte[] {Ptg.CLASS_VALUE};functionData[270][2]=new Integer(-1); - functionData[271][0]=new Byte(Ptg.CLASS_VALUE);functionData[271][1]=new byte[] {Ptg.CLASS_VALUE};functionData[271][2]=new Integer(1); - functionData[272][0]=new Byte(Ptg.CLASS_VALUE);functionData[272][1]=new byte[] {Ptg.CLASS_VALUE};functionData[272][2]=new Integer(-1); - functionData[273][0]=new Byte(Ptg.CLASS_VALUE);functionData[273][1]=new byte[] {Ptg.CLASS_VALUE};functionData[273][2]=new Integer(4); - functionData[274][0]=new Byte(Ptg.CLASS_VALUE);functionData[274][1]=new byte[] {Ptg.CLASS_VALUE};functionData[274][2]=new Integer(2); - functionData[275][0]=new Byte(Ptg.CLASS_VALUE);functionData[275][1]=new byte[] {Ptg.CLASS_VALUE};functionData[275][2]=new Integer(2); - functionData[276][0]=new Byte(Ptg.CLASS_VALUE);functionData[276][1]=new byte[] {Ptg.CLASS_VALUE};functionData[276][2]=new Integer(2); - functionData[277][0]=new Byte(Ptg.CLASS_VALUE);functionData[277][1]=new byte[] {Ptg.CLASS_VALUE};functionData[277][2]=new Integer(3); - functionData[278][0]=new Byte(Ptg.CLASS_VALUE);functionData[278][1]=new byte[] {Ptg.CLASS_VALUE};functionData[278][2]=new Integer(3); - functionData[279][0]=new Byte(Ptg.CLASS_VALUE);functionData[279][1]=new byte[] {Ptg.CLASS_VALUE};functionData[279][2]=new Integer(1); - functionData[280][0]=new Byte(Ptg.CLASS_VALUE);functionData[280][1]=new byte[] {Ptg.CLASS_VALUE};functionData[280][2]=new Integer(3); - functionData[281][0]=new Byte(Ptg.CLASS_VALUE);functionData[281][1]=new byte[] {Ptg.CLASS_VALUE};functionData[281][2]=new Integer(3); - functionData[282][0]=new Byte(Ptg.CLASS_VALUE);functionData[282][1]=new byte[] {Ptg.CLASS_VALUE};functionData[282][2]=new Integer(3); - functionData[283][0]=new Byte(Ptg.CLASS_VALUE);functionData[283][1]=new byte[] {Ptg.CLASS_VALUE};functionData[283][2]=new Integer(1); - functionData[284][0]=new Byte(Ptg.CLASS_VALUE);functionData[284][1]=new byte[] {Ptg.CLASS_VALUE};functionData[284][2]=new Integer(1); - functionData[285][0]=new Byte(Ptg.CLASS_VALUE);functionData[285][1]=new byte[] {Ptg.CLASS_VALUE};functionData[285][2]=new Integer(2); - functionData[286][0]=new Byte(Ptg.CLASS_VALUE);functionData[286][1]=new byte[] {Ptg.CLASS_VALUE};functionData[286][2]=new Integer(4); - functionData[287][0]=new Byte(Ptg.CLASS_VALUE);functionData[287][1]=new byte[] {Ptg.CLASS_VALUE};functionData[287][2]=new Integer(3); - functionData[288][0]=new Byte(Ptg.CLASS_VALUE);functionData[288][1]=new byte[] {Ptg.CLASS_VALUE};functionData[288][2]=new Integer(2); - functionData[289][0]=new Byte(Ptg.CLASS_VALUE);functionData[289][1]=new byte[] {Ptg.CLASS_VALUE};functionData[289][2]=new Integer(4); - functionData[290][0]=new Byte(Ptg.CLASS_VALUE);functionData[290][1]=new byte[] {Ptg.CLASS_VALUE};functionData[290][2]=new Integer(3); - functionData[291][0]=new Byte(Ptg.CLASS_VALUE);functionData[291][1]=new byte[] {Ptg.CLASS_VALUE};functionData[291][2]=new Integer(3); - functionData[292][0]=new Byte(Ptg.CLASS_VALUE);functionData[292][1]=new byte[] {Ptg.CLASS_VALUE};functionData[292][2]=new Integer(3); - functionData[293][0]=new Byte(Ptg.CLASS_VALUE);functionData[293][1]=new byte[] {Ptg.CLASS_VALUE};functionData[293][2]=new Integer(4); - functionData[294][0]=new Byte(Ptg.CLASS_VALUE);functionData[294][1]=new byte[] {Ptg.CLASS_VALUE};functionData[294][2]=new Integer(1); - functionData[295][0]=new Byte(Ptg.CLASS_VALUE);functionData[295][1]=new byte[] {Ptg.CLASS_VALUE};functionData[295][2]=new Integer(3); - functionData[296][0]=new Byte(Ptg.CLASS_VALUE);functionData[296][1]=new byte[] {Ptg.CLASS_VALUE};functionData[296][2]=new Integer(1); - functionData[297][0]=new Byte(Ptg.CLASS_VALUE);functionData[297][1]=new byte[] {Ptg.CLASS_VALUE};functionData[297][2]=new Integer(3); - functionData[298][0]=new Byte(Ptg.CLASS_VALUE);functionData[298][1]=new byte[] {Ptg.CLASS_VALUE};functionData[298][2]=new Integer(1); - functionData[299][0]=new Byte(Ptg.CLASS_VALUE);functionData[299][1]=new byte[] {Ptg.CLASS_VALUE};functionData[299][2]=new Integer(2); - functionData[300][0]=new Byte(Ptg.CLASS_VALUE);functionData[300][1]=new byte[] {Ptg.CLASS_VALUE};functionData[300][2]=new Integer(3); - functionData[301][0]=new Byte(Ptg.CLASS_VALUE);functionData[301][1]=new byte[] {Ptg.CLASS_VALUE};functionData[301][2]=new Integer(3); - functionData[302][0]=new Byte(Ptg.CLASS_VALUE);functionData[302][1]=new byte[] {Ptg.CLASS_VALUE};functionData[302][2]=new Integer(4); - functionData[303][0]=new Byte(Ptg.CLASS_VALUE);functionData[303][1]=new byte[] {Ptg.CLASS_ARRAY};functionData[303][2]=new Integer(2); - functionData[304][0]=new Byte(Ptg.CLASS_VALUE);functionData[304][1]=new byte[] {Ptg.CLASS_ARRAY};functionData[304][2]=new Integer(2); - functionData[305][0]=new Byte(Ptg.CLASS_VALUE);functionData[305][1]=new byte[] {Ptg.CLASS_ARRAY};functionData[305][2]=new Integer(2); - functionData[306][0]=new Byte(Ptg.CLASS_VALUE);functionData[306][1]=new byte[] {Ptg.CLASS_ARRAY};functionData[306][2]=new Integer(2); - functionData[307][0]=new Byte(Ptg.CLASS_VALUE);functionData[307][1]=new byte[] {Ptg.CLASS_ARRAY};functionData[307][2]=new Integer(2); - functionData[308][0]=new Byte(Ptg.CLASS_VALUE);functionData[308][1]=new byte[] {Ptg.CLASS_ARRAY};functionData[308][2]=new Integer(2); - functionData[309][0]=new Byte(Ptg.CLASS_VALUE);functionData[309][1]=new byte[] {Ptg.CLASS_ARRAY};functionData[309][2]=new Integer(3); - functionData[310][0]=new Byte(Ptg.CLASS_VALUE);functionData[310][1]=new byte[] {Ptg.CLASS_ARRAY};functionData[310][2]=new Integer(2); - functionData[311][0]=new Byte(Ptg.CLASS_VALUE);functionData[311][1]=new byte[] {Ptg.CLASS_ARRAY};functionData[311][2]=new Integer(2); - functionData[312][0]=new Byte(Ptg.CLASS_VALUE);functionData[312][1]=new byte[] {Ptg.CLASS_ARRAY};functionData[312][2]=new Integer(2); - functionData[313][0]=new Byte(Ptg.CLASS_VALUE);functionData[313][1]=new byte[] {Ptg.CLASS_ARRAY};functionData[313][2]=new Integer(2); - functionData[314][0]=new Byte(Ptg.CLASS_VALUE);functionData[314][1]=new byte[] {Ptg.CLASS_ARRAY};functionData[314][2]=new Integer(2); - functionData[315][0]=new Byte(Ptg.CLASS_VALUE);functionData[315][1]=new byte[] {Ptg.CLASS_ARRAY};functionData[315][2]=new Integer(2); - functionData[316][0]=new Byte(Ptg.CLASS_VALUE);functionData[316][1]=new byte[] {Ptg.CLASS_VALUE};functionData[316][2]=new Integer(4); - functionData[317][0]=new Byte(Ptg.CLASS_VALUE);functionData[317][1]=new byte[] {Ptg.CLASS_VALUE};functionData[317][2]=new Integer(-1); - functionData[318][0]=new Byte(Ptg.CLASS_VALUE);functionData[318][1]=new byte[] {Ptg.CLASS_REF};functionData[318][2]=new Integer(-1); - functionData[319][0]=new Byte(Ptg.CLASS_VALUE);functionData[319][1]=new byte[] {Ptg.CLASS_REF};functionData[319][2]=new Integer(-1); - functionData[320][0]=new Byte(Ptg.CLASS_VALUE);functionData[320][1]=new byte[] {Ptg.CLASS_REF};functionData[320][2]=new Integer(-1); - functionData[321][0]=new Byte(Ptg.CLASS_VALUE);functionData[321][1]=new byte[] {Ptg.CLASS_REF};functionData[321][2]=new Integer(-1); - functionData[322][0]=new Byte(Ptg.CLASS_VALUE);functionData[322][1]=new byte[] {Ptg.CLASS_REF};functionData[322][2]=new Integer(-1); - functionData[323][0]=new Byte(Ptg.CLASS_VALUE);functionData[323][1]=new byte[] {Ptg.CLASS_REF};functionData[323][2]=new Integer(-1); - functionData[324][0]=new Byte(Ptg.CLASS_VALUE);functionData[324][1]=new byte[] {Ptg.CLASS_VALUE};functionData[324][2]=new Integer(-1); - functionData[325][0]=new Byte(Ptg.CLASS_VALUE);functionData[325][1]=new byte[] {Ptg.CLASS_VALUE};functionData[325][2]=new Integer(2); - functionData[326][0]=new Byte(Ptg.CLASS_VALUE);functionData[326][1]=new byte[] {Ptg.CLASS_VALUE};functionData[326][2]=new Integer(2); - functionData[327][0]=new Byte(Ptg.CLASS_VALUE);functionData[327][1]=new byte[] {Ptg.CLASS_VALUE};functionData[327][2]=new Integer(2); - functionData[328][0]=new Byte(Ptg.CLASS_VALUE);functionData[328][1]=new byte[] {Ptg.CLASS_VALUE};functionData[328][2]=new Integer(2); - functionData[329][0]=new Byte(Ptg.CLASS_VALUE);functionData[329][1]=new byte[] {Ptg.CLASS_VALUE};functionData[329][2]=new Integer(-1); - functionData[330][0]=new Byte(Ptg.CLASS_VALUE);functionData[330][1]=new byte[] {Ptg.CLASS_ARRAY};functionData[330][2]=new Integer(-1); - functionData[331][0]=new Byte(Ptg.CLASS_VALUE);functionData[331][1]=new byte[] {Ptg.CLASS_VALUE};functionData[331][2]=new Integer(2); - functionData[332][0]=new Byte(Ptg.CLASS_VALUE);functionData[332][1]=new byte[] {Ptg.CLASS_VALUE};functionData[332][2]=new Integer(2); - - - functionData[336][0]=new Byte(Ptg.CLASS_VALUE);functionData[336][1]=new byte[] {Ptg.CLASS_VALUE};functionData[336][2]=new Integer(-1); - functionData[337][0]=new Byte(Ptg.CLASS_VALUE);functionData[337][1]=new byte[] {Ptg.CLASS_VALUE};functionData[337][2]=new Integer(2); - - - - - functionData[342][0]=new Byte(Ptg.CLASS_VALUE);functionData[342][1]=new byte[] {Ptg.CLASS_VALUE};functionData[342][2]=new Integer(1); - functionData[343][0]=new Byte(Ptg.CLASS_VALUE);functionData[343][1]=new byte[] {Ptg.CLASS_VALUE};functionData[343][2]=new Integer(1); - functionData[344][0]=new Byte(Ptg.CLASS_VALUE);functionData[344][1]=new byte[] {Ptg.CLASS_REF};functionData[344][2]=new Integer(-1); - functionData[345][0]=new Byte(Ptg.CLASS_VALUE);functionData[345][1]=new byte[] {Ptg.CLASS_REF};functionData[345][2]=new Integer(-1); - functionData[346][0]=new Byte(Ptg.CLASS_VALUE);functionData[346][1]=new byte[] {Ptg.CLASS_VALUE};functionData[346][2]=new Integer(2); - functionData[347][0]=new Byte(Ptg.CLASS_VALUE);functionData[347][1]=new byte[] {Ptg.CLASS_REF};functionData[347][2]=new Integer(1); - - - functionData[350][0]=new Byte(Ptg.CLASS_VALUE);functionData[350][1]=new byte[] {Ptg.CLASS_VALUE};functionData[350][2]=new Integer(4); - - functionData[352][0]=new Byte(Ptg.CLASS_VALUE);functionData[352][1]=new byte[] {Ptg.CLASS_VALUE};functionData[352][2]=new Integer(1); - - functionData[354][0]=new Byte(Ptg.CLASS_VALUE);functionData[354][1]=new byte[] {Ptg.CLASS_VALUE};functionData[354][2]=new Integer(-1); - - - - functionData[358][0]=new Byte(Ptg.CLASS_VALUE);functionData[358][1]=new byte[] {Ptg.CLASS_VALUE};functionData[358][2]=new Integer(2); - functionData[359][0]=new Byte(Ptg.CLASS_VALUE);functionData[359][1]=new byte[] {Ptg.CLASS_VALUE};functionData[359][2]=new Integer(-1); - functionData[360][0]=new Byte(Ptg.CLASS_VALUE);functionData[360][1]=new byte[] {Ptg.CLASS_REF};functionData[360][2]=new Integer(1); - functionData[361][0]=new Byte(Ptg.CLASS_VALUE);functionData[361][1]=new byte[] {Ptg.CLASS_REF};functionData[361][2]=new Integer(-1); - functionData[362][0]=new Byte(Ptg.CLASS_VALUE);functionData[362][1]=new byte[] {Ptg.CLASS_REF};functionData[362][2]=new Integer(-1); - functionData[363][0]=new Byte(Ptg.CLASS_VALUE);functionData[363][1]=new byte[] {Ptg.CLASS_REF};functionData[363][2]=new Integer(-1); - functionData[364][0]=new Byte(Ptg.CLASS_VALUE);functionData[364][1]=new byte[] {Ptg.CLASS_REF};functionData[364][2]=new Integer(-1); - functionData[365][0]=new Byte(Ptg.CLASS_VALUE);functionData[365][1]=new byte[] {Ptg.CLASS_REF};functionData[365][2]=new Integer(-1); - functionData[366][0]=new Byte(Ptg.CLASS_VALUE);functionData[366][1]=new byte[] {Ptg.CLASS_REF};functionData[366][2]=new Integer(-1); - functionData[367][0]=new Byte(Ptg.CLASS_VALUE);functionData[367][1]=new byte[] {Ptg.CLASS_REF};functionData[367][2]=new Integer(-1); - - - return functionData; + short ix = FunctionMetadataRegistry.lookupIndexByName(name.toUpperCase()); + if (ix < 0) { + return FUNCTION_INDEX_EXTERNAL; + } + return ix; } public byte getDefaultOperandClass() { diff --git a/src/java/org/apache/poi/hssf/record/formula/FuncPtg.java b/src/java/org/apache/poi/hssf/record/formula/FuncPtg.java index 1123fc803a..a94355a0f0 100644 --- a/src/java/org/apache/poi/hssf/record/formula/FuncPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/FuncPtg.java @@ -15,17 +15,18 @@ limitations under the License. ==================================================================== */ - package org.apache.poi.hssf.record.formula; import org.apache.poi.util.LittleEndian; import org.apache.poi.hssf.record.RecordInputStream; +import org.apache.poi.hssf.record.formula.function.FunctionMetadata; +import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry; /** * @author aviks * @author Jason Height (jheight at chariot dot net dot au) * @author Danny Mui (dmui at apache dot org) (Leftover handling) */ -public class FuncPtg extends AbstractFunctionPtg{ +public final class FuncPtg extends AbstractFunctionPtg { public final static byte sid = 0x21; public final static int SIZE = 3; @@ -50,34 +51,24 @@ public class FuncPtg extends AbstractFunctionPtg{ //field_1_num_args = data[ offset + 0 ]; field_2_fnc_index = in.readShort(); - /* - if (data.length - offset > 2) { //save left overs if there are any - leftOvers = new byte[2]; - System.arraycopy(data, offset+1, leftOvers, 0, leftOvers.length); + FunctionMetadata fm = FunctionMetadataRegistry.getFunctionByIndex(field_2_fnc_index); + if(fm == null) { + throw new RuntimeException("Invalid built-in function index (" + field_2_fnc_index + ")"); } - */ - try { - numParams = ( (Integer)functionData[field_2_fnc_index][2]).intValue(); - } catch (NullPointerException npe) { - numParams=0; - } - + numParams = fm.getMinParams(); } public FuncPtg(int functionIndex, int numberOfParameters) { field_2_fnc_index = (short) functionIndex; numParams = numberOfParameters; + paramClass = new byte[] { Ptg.CLASS_VALUE, }; // TODO } - public void writeBytes(byte[] array, int offset) { + public void writeBytes(byte[] array, int offset) { array[offset+0]= (byte) (sid + ptgClass); - //array[offset+1]=field_1_num_args; LittleEndian.putShort(array,offset+1,field_2_fnc_index); - /**if (leftOvers != null) { - System.arraycopy(leftOvers, 0, array, offset+2, leftOvers.length); - }**/ } - public int getNumberOfOperands() { + public int getNumberOfOperands() { return numParams; } @@ -94,13 +85,11 @@ public class FuncPtg extends AbstractFunctionPtg{ } public String toString() { - StringBuffer buffer = new StringBuffer(); - buffer - .append("").append("\n") - .append(" numArgs(internal)=").append(this.numParams).append("\n") - .append(" name =").append(lookupName(field_2_fnc_index)).append("\n") - .append(" field_2_fnc_index=").append(field_2_fnc_index).append("\n") - .append(""); - return buffer.toString(); + StringBuffer sb = new StringBuffer(64); + sb.append(getClass().getName()).append(" ["); + sb.append(lookupName(field_2_fnc_index)); + sb.append(" nArgs=").append(numParams); + sb.append("]"); + return sb.toString(); } } \ No newline at end of file diff --git a/src/java/org/apache/poi/hssf/record/formula/FuncVarPtg.java b/src/java/org/apache/poi/hssf/record/formula/FuncVarPtg.java index 8501f9e79a..b4732c7287 100644 --- a/src/java/org/apache/poi/hssf/record/formula/FuncVarPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/FuncVarPtg.java @@ -19,12 +19,14 @@ package org.apache.poi.hssf.record.formula; import org.apache.poi.util.LittleEndian; import org.apache.poi.hssf.record.RecordInputStream; +import org.apache.poi.hssf.record.formula.function.FunctionMetadata; +import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry; /** * * @author Jason Height (jheight at chariot dot net dot au) */ -public class FuncVarPtg extends AbstractFunctionPtg{ +public final class FuncVarPtg extends AbstractFunctionPtg{ public final static byte sid = 0x22; private final static int SIZE = 4; @@ -47,10 +49,12 @@ public class FuncVarPtg extends AbstractFunctionPtg{ public FuncVarPtg(String pName, byte pNumOperands) { field_1_num_args = pNumOperands; field_2_fnc_index = lookupIndex(pName); - try{ - returnClass = ( (Byte) functionData[field_2_fnc_index][0]).byteValue(); - paramClass = (byte[]) functionData[field_2_fnc_index][1]; - } catch (NullPointerException npe ) { + FunctionMetadata fm = FunctionMetadataRegistry.getFunctionByIndex(field_2_fnc_index); + if(fm == null) { + // Happens only as a result of a call to FormulaParser.parse(), with a non-built-in function name + returnClass = Ptg.CLASS_VALUE; + paramClass = new byte[] {Ptg.CLASS_VALUE}; + } else { returnClass = Ptg.CLASS_VALUE; paramClass = new byte[] {Ptg.CLASS_VALUE}; } @@ -79,15 +83,11 @@ public class FuncVarPtg extends AbstractFunctionPtg{ } public String toString() { - StringBuffer buffer = new StringBuffer(); - buffer - .append("").append("\n") - .append(" field_1_num_args=").append(field_1_num_args).append("\n") - .append(" name =").append(lookupName(field_2_fnc_index)).append("\n") - .append(" field_2_fnc_index=").append(field_2_fnc_index).append("\n") - .append(""); - return buffer.toString(); + StringBuffer sb = new StringBuffer(64); + sb.append(getClass().getName()).append(" ["); + sb.append(lookupName(field_2_fnc_index)); + sb.append(" nArgs=").append(field_1_num_args); + sb.append("]"); + return sb.toString(); } - - } diff --git a/src/java/org/apache/poi/hssf/record/formula/function/FunctionDataBuilder.java b/src/java/org/apache/poi/hssf/record/formula/function/FunctionDataBuilder.java new file mode 100644 index 0000000000..b304ec3d42 --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/formula/function/FunctionDataBuilder.java @@ -0,0 +1,89 @@ +/* ==================================================================== + 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.function; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * Temporarily collects FunctionMetadata instances for creation of a + * FunctionMetadataRegistry. + * + * @author Josh Micich + */ +final class FunctionDataBuilder { + private int _maxFunctionIndex; + private final Map _functionDataByName; + private final Map _functionDataByIndex; + /** stores indexes of all functions with footnotes (i.e. whose definitions might change) */ + private final Set _mutatingFunctionIndexes; + + public FunctionDataBuilder(int sizeEstimate) { + _maxFunctionIndex = -1; + _functionDataByName = new HashMap(sizeEstimate * 3 / 2); + _functionDataByIndex = new HashMap(sizeEstimate * 3 / 2); + _mutatingFunctionIndexes = new HashSet(); + } + + public void add(int functionIndex, String functionName, int minParams, int maxParams, boolean hasFootnote) { + FunctionMetadata fm = new FunctionMetadata(functionIndex, functionName, minParams, maxParams); + + Integer indexKey = new Integer(functionIndex); + + + if(functionIndex > _maxFunctionIndex) { + _maxFunctionIndex = functionIndex; + } + // allow function definitions to change only if both previous and the new items have footnotes + FunctionMetadata prevFM; + prevFM = (FunctionMetadata) _functionDataByName.get(functionName); + if(prevFM != null) { + if(!hasFootnote || !_mutatingFunctionIndexes.contains(indexKey)) { + throw new RuntimeException("Multiple entries for function name '" + functionName + "'"); + } + _functionDataByIndex.remove(new Integer(prevFM.getIndex())); + } + prevFM = (FunctionMetadata) _functionDataByIndex.get(indexKey); + if(prevFM != null) { + if(!hasFootnote || !_mutatingFunctionIndexes.contains(indexKey)) { + throw new RuntimeException("Multiple entries for function index (" + functionIndex + ")"); + } + _functionDataByName.remove(prevFM.getName()); + } + if(hasFootnote) { + _mutatingFunctionIndexes.add(indexKey); + } + _functionDataByIndex.put(indexKey, fm); + _functionDataByName.put(functionName, fm); + } + + public FunctionMetadataRegistry build() { + + FunctionMetadata[] jumbledArray = new FunctionMetadata[_functionDataByName.size()]; + _functionDataByName.values().toArray(jumbledArray); + FunctionMetadata[] fdIndexArray = new FunctionMetadata[_maxFunctionIndex+1]; + for (int i = 0; i < jumbledArray.length; i++) { + FunctionMetadata fd = jumbledArray[i]; + fdIndexArray[fd.getIndex()] = fd; + } + + return new FunctionMetadataRegistry(fdIndexArray, _functionDataByName); + } +} \ No newline at end of file diff --git a/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadata.java b/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadata.java new file mode 100644 index 0000000000..9df2db93ca --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadata.java @@ -0,0 +1,58 @@ +/* ==================================================================== + 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.function; +/** + * + * @author Josh Micich + */ +public final class FunctionMetadata { + + private final int _index; + private final String _name; + private final int _minParams; + private final int _maxParams; + + /* package */ FunctionMetadata(int index, String name, int minParams, int maxParams) { + _index = index; + _name = name; + _minParams = minParams; + _maxParams = maxParams; + } + public int getIndex() { + return _index; + } + public String getName() { + return _name; + } + public int getMinParams() { + return _minParams; + } + public int getMaxParams() { + return _maxParams; + } + public boolean hasFixedArgsLength() { + return _minParams == _maxParams; + } + public String toString() { + StringBuffer sb = new StringBuffer(64); + sb.append(getClass().getName()).append(" ["); + sb.append(_index).append(" ").append(_name); + sb.append("]"); + return sb.toString(); + } +} \ No newline at end of file diff --git a/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataReader.java b/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataReader.java new file mode 100644 index 0000000000..bd50bf04d5 --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataReader.java @@ -0,0 +1,129 @@ +/* ==================================================================== + 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.function; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Pattern; + +/** + * Converts the text meta-data file into a FunctionMetadataRegistry + * + * @author Josh Micich + */ +final class FunctionMetadataReader { + + private static final String METADATA_FILE_NAME = "functionMetadata.txt"; + + private static final Pattern TAB_DELIM_PATTERN = Pattern.compile("\t"); + + private static final String[] DIGIT_ENDING_FUNCTION_NAMES = { + // Digits at the end of a function might be due to a left-over footnote marker. + // except in these cases + "LOG10", "ATAN2", "DAYS360", "SUMXMY2", "SUMX2MY2", "SUMX2PY2", + }; + private static final Set DIGIT_ENDING_FUNCTION_NAMES_SET = new HashSet(Arrays.asList(DIGIT_ENDING_FUNCTION_NAMES)); + + public static FunctionMetadataRegistry createRegistry() { + InputStream is = FunctionMetadataReader.class.getResourceAsStream(METADATA_FILE_NAME); + if(is == null) { + throw new RuntimeException("resource '" + METADATA_FILE_NAME + "' not found"); + } + + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + FunctionDataBuilder fdb = new FunctionDataBuilder(400); + + try { + while (true) { + String line = br.readLine(); + if (line == null) { + break; + } + if (line.length() < 1 || line.charAt(0) == '#') { + continue; + } + String trimLine = line.trim(); + if (trimLine.length() < 1) { + continue; + } + processLine(fdb, line); + } + br.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + + return fdb.build(); + } + + private static void processLine(FunctionDataBuilder fdb, String line) { + + String[] parts = TAB_DELIM_PATTERN.split(line, -2); + if(parts.length != 8) { + throw new RuntimeException("Bad line format '" + line + "' - expected 8 data fields"); + } + int functionIndex = parseInt(parts[0]); + String functionName = parts[1]; + int minParams = parseInt(parts[2]); + int maxParams = parseInt(parts[3]); + // 4 returnClass + // 5 parameterClasses + // 6 isVolatile + boolean hasNote = parts[7].length() > 0; + + validateFunctionName(functionName); + // TODO - make POI use returnClass, parameterClasses, isVolatile + fdb.add(functionIndex, functionName, minParams, maxParams, hasNote); + } + + /** + * Makes sure that footnote digits from the original OOO document have not been accidentally + * left behind + */ + private static void validateFunctionName(String functionName) { + int len = functionName.length(); + int ix = len - 1; + if (!Character.isDigit(functionName.charAt(ix))) { + return; + } + while(ix >= 0) { + if (!Character.isDigit(functionName.charAt(ix))) { + break; + } + ix--; + } + if(DIGIT_ENDING_FUNCTION_NAMES_SET.contains(functionName)) { + return; + } + throw new RuntimeException("Invalid function name '" + functionName + + "' (is footnote number incorrectly appended)"); + } + + private static int parseInt(String valStr) { + try { + return Integer.parseInt(valStr); + } catch (NumberFormatException e) { + throw new RuntimeException("Value '" + valStr + "' could not be parsed as an integer"); + } + } +} diff --git a/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataRegistry.java b/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataRegistry.java new file mode 100644 index 0000000000..0cc8de37d6 --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataRegistry.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.record.formula.function; + +import java.util.Map; +import java.util.Set; + +public final class FunctionMetadataRegistry { + /** + * The name of the IF function (i.e. "IF"). Extracted as a constant for clarity. + */ + public static final String FUNCTION_NAME_IF = "IF"; + + public static final short FUNCTION_INDEX_EXTERNAL = 255; + private static FunctionMetadataRegistry _instance; + + private final FunctionMetadata[] _functionDataByIndex; + private final Map _functionDataByName; + + private static FunctionMetadataRegistry getInstance() { + if (_instance == null) { + _instance = FunctionMetadataReader.createRegistry(); +// _instance = POIFunctionMetadataCreator.createInstance(); + } + return _instance; + } + + /* package */ FunctionMetadataRegistry(FunctionMetadata[] functionDataByIndex, Map functionDataByName) { + _functionDataByIndex = functionDataByIndex; + _functionDataByName = functionDataByName; + } + + /* package */ Set getAllFunctionNames() { + return _functionDataByName.keySet(); + } + + + public static FunctionMetadata getFunctionByIndex(int index) { + return getInstance().getFunctionByIndexInternal(index); + } + + private FunctionMetadata getFunctionByIndexInternal(int index) { + return _functionDataByIndex[index]; + } + /** + * Resolves a built-in function index. + * @param name uppercase function name + * @return a negative value if the function name is not found. + * This typically occurs for external functions. + */ + public static short lookupIndexByName(String name) { + FunctionMetadata fd = getInstance().getFunctionByNameInternal(name); + if (fd == null) { + return -1; + } + return (short) fd.getIndex(); + } + + private FunctionMetadata getFunctionByNameInternal(String name) { + return (FunctionMetadata) _functionDataByName.get(name); + } + + + public static FunctionMetadata getFunctionByName(String name) { + return getInstance().getFunctionByNameInternal(name); + } +} diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFConditionalFormatting.java b/src/java/org/apache/poi/hssf/usermodel/HSSFConditionalFormatting.java index e5fbb1d300..c7018a0e1a 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFConditionalFormatting.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFConditionalFormatting.java @@ -25,23 +25,88 @@ import org.apache.poi.hssf.record.aggregates.CFRecordsAggregate; import org.apache.poi.hssf.record.cf.CellRange; import org.apache.poi.hssf.util.Region; -public class HSSFConditionalFormatting +/** + * HSSFConditionalFormatting class encapsulates all settings of Conditional Formatting. + * + * The class can be used + * + *

      + *
    • + * to make a copy HSSFConditionalFormatting settings. + *
    • + * + * + * For example: + *
      + * HSSFConditionalFormatting cf = sheet.getConditionalFormattingAt(index);
      + * newSheet.addConditionalFormatting(cf);
      + * 
      + * + *
    • + * or to modify existing Conditional Formatting settings (formatting regions and/or rules). + *
    • + *
    + * + * Use {@link HSSFSheet#getConditionalFormattingAt(int)} to get access to an instance of this class. + *

    + * To create a new Conditional Formatting set use the following approach: + * + *

    + * // Create pattern with red background
    + * HSSFPatternFormatting patternFormatting = new HSSFPatternFormatting();
    + * patternFormatting.setFillBackgroundColor(HSSFColor.RED.index);
    + * 
    + * Region [] regions =
    + * {
    + *     // Define a region containing first column
    + *     new Region(1,(short)1,-1,(short)1)
    + * };
    + *     
    + * HSSFConditionalFormattingRule[] rules = 
    + * {
    + *     // Define a Conditional Formatting rule, which triggers formatting
    + *     // when cell's value is greater or equal than 100.0 and
    + *     // applies patternFormatting defined above.
    + *         
    + *     sheet.createConditionalFormattingRule(
    + *             HSSFConditionalFormattingRule.COMPARISON_OPERATOR_GE, 
    + *             "100.0", // 1st formula 
    + *             null,    // 2nd formula is not used for comparison operator GE
    + *             null,    // do not override Font Formatting
    + *             null,    // do not override Border Formatting
    + *             patternFormatting
    + *     )
    + * };
    + *     
    + * // Apply Conditional Formatting rules defined above to the regions  
    + * sheet.addConditionalFormatting(regions, rules);
    + * 
    + * + * @author Dmitriy Kumshayev + */ +public final class HSSFConditionalFormatting { - HSSFSheet sheet; - CFRecordsAggregate cfAggregate; - - protected HSSFConditionalFormatting(HSSFSheet sheet) - { - this.sheet = sheet; - this.cfAggregate = new CFRecordsAggregate(); + private final HSSFSheet sheet; + private final CFRecordsAggregate cfAggregate; + + HSSFConditionalFormatting(HSSFSheet sheet) { + this(sheet, new CFRecordsAggregate()); } - - protected HSSFConditionalFormatting(HSSFSheet sheet, CFRecordsAggregate cfAggregate) + + HSSFConditionalFormatting(HSSFSheet sheet, CFRecordsAggregate cfAggregate) { + if(sheet == null) { + throw new IllegalArgumentException("sheet must not be null"); + } + if(cfAggregate == null) { + throw new IllegalArgumentException("cfAggregate must not be null"); + } this.sheet = sheet; this.cfAggregate = cfAggregate; } - + CFRecordsAggregate getCFRecordsAggregate() { + return cfAggregate; + } public void setFormattingRegions(Region[] regions) { @@ -52,35 +117,65 @@ public class HSSFConditionalFormatting } } + /** + * @return array of Regions. never null + */ public Region[] getFormattingRegions() { CFHeaderRecord cfh = cfAggregate.getHeader(); - + List cellRanges = cfh.getCellRanges(); - - if (cellRanges != null) - { - return toRegionArray(cellRanges); - } - return null; + + return toRegionArray(cellRanges); } - - public void setConditionalFormat(int idx, HSSFConditionalFormattingRule cfRule) + + /** + * set a Conditional Formatting rule at position idx. + * Excel allows to create up to 3 Conditional Formatting rules. + * This method can be useful to modify existing Conditional Formatting rules. + * + * @param idx position of the rule. Should be between 0 and 2. + * @param cfRule - Conditional Formatting rule + */ + public void setRule(int idx, HSSFConditionalFormattingRule cfRule) { + if (idx < 0 || idx > 2) { + throw new IllegalArgumentException("idx must be between 0 and 2 but was (" + + idx + ")"); + } cfAggregate.getRules().set(idx, cfRule); } - public void addConditionalFormat(HSSFConditionalFormattingRule cfRule) + /** + * add a Conditional Formatting rule. + * Excel allows to create up to 3 Conditional Formatting rules. + * @param cfRule - Conditional Formatting rule + */ + public void addRule(HSSFConditionalFormattingRule cfRule) { cfAggregate.getRules().add(cfRule); } - - public HSSFConditionalFormattingRule getConditionalFormat(int idx) + + /** + * get a Conditional Formatting rule at position idx. + * @param idx + * @return a Conditional Formatting rule at position idx. + */ + public HSSFConditionalFormattingRule getRule(int idx) { CFRuleRecord ruleRecord = (CFRuleRecord)cfAggregate.getRules().get(idx); return new HSSFConditionalFormattingRule(sheet.workbook, ruleRecord); } - + + /** + * @return number of Conditional Formatting rules. + */ + public int getNumbOfRules() + { + return cfAggregate.getRules().size(); + } + + /** * Do all possible cell merges between cells of the list so that:
    *
  • if a cell range is completely inside of another cell range, it gets removed from the list @@ -91,11 +186,11 @@ public class HSSFConditionalFormatting private static List mergeCellRanges(List cellRangeList) { boolean merged = false; - + do { merged = false; - + if( cellRangeList.size()>1 ) { for( int i=0; i protection enabled; false => protection disabled - */ - public boolean getProtect() { - return getSheet().isProtected()[0]; - } + /** + * Answer whether protection is enabled or disabled + * @return true => protection enabled; false => protection disabled + */ + public boolean getProtect() { + return getSheet().isProtected()[0]; + } - /** - * @return hashed password - */ - public short getPassword() { - return getSheet().getPassword().getPassword(); - } + /** + * @return hashed password + */ + public short getPassword() { + return getSheet().getPassword().getPassword(); + } - /** - * Answer whether object protection is enabled or disabled - * @return true => protection enabled; false => protection disabled - */ - public boolean getObjectProtect() { - return getSheet().isProtected()[1]; - } + /** + * Answer whether object protection is enabled or disabled + * @return true => protection enabled; false => protection disabled + */ + public boolean getObjectProtect() { + return getSheet().isProtected()[1]; + } - /** - * Answer whether scenario protection is enabled or disabled - * @return true => protection enabled; false => protection disabled - */ - public boolean getScenarioProtect() { - return getSheet().isProtected()[2]; - } + /** + * Answer whether scenario protection is enabled or disabled + * @return true => protection enabled; false => protection disabled + */ + public boolean getScenarioProtect() { + return getSheet().isProtected()[2]; + } - /** - * Sets the protection on enabled or disabled - * @param protect true => protection enabled; false => protection disabled + /** + * Sets the protection on enabled or disabled + * @param protect true => protection enabled; false => protection disabled * @deprecated use protectSheet(String, boolean, boolean) - */ - public void setProtect(boolean protect) { - getSheet().getProtect().setProtect(protect); - } + */ + public void setProtect(boolean protect) { + getSheet().getProtect().setProtect(protect); + } /** * Sets the protection enabled as well as the password @@ -1083,29 +1078,29 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet sclRecord.setDenominator((short)denominator); getSheet().setSCLRecord(sclRecord); } - + /** - * The top row in the visible view when the sheet is - * first viewed after opening it in a viewer + * The top row in the visible view when the sheet is + * first viewed after opening it in a viewer * @return short indicating the rownum (0 based) of the top row */ - public short getTopRow() + public short getTopRow() { - return sheet.getTopRow(); + return sheet.getTopRow(); } - + /** - * The left col in the visible view when the sheet is - * first viewed after opening it in a viewer + * The left col in the visible view when the sheet is + * first viewed after opening it in a viewer * @return short indicating the rownum (0 based) of the top row */ - public short getLeftCol() + public short getLeftCol() { - return sheet.getLeftCol(); + return sheet.getLeftCol(); } - + /** - * Sets desktop window pane display area, when the + * Sets desktop window pane display area, when the * file is first opened in a viewer. * @param toprow the top row to show in desktop window pane * @param leftcol the left column to show in desktop window pane @@ -1115,49 +1110,49 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet this.sheet.setLeftCol((short)leftcol); } - /** - * Shifts the merged regions left or right depending on mode - *

    - * TODO: MODE , this is only row specific - * @param startRow - * @param endRow - * @param n - * @param isRow - */ - protected void shiftMerged(int startRow, int endRow, int n, boolean isRow) { - List shiftedRegions = new ArrayList(); - //move merged regions completely if they fall within the new region boundaries when they are shifted - for (int i = 0; i < this.getNumMergedRegions(); i++) { - Region merged = this.getMergedRegionAt(i); + /** + * Shifts the merged regions left or right depending on mode + *

    + * TODO: MODE , this is only row specific + * @param startRow + * @param endRow + * @param n + * @param isRow + */ + protected void shiftMerged(int startRow, int endRow, int n, boolean isRow) { + List shiftedRegions = new ArrayList(); + //move merged regions completely if they fall within the new region boundaries when they are shifted + for (int i = 0; i < this.getNumMergedRegions(); i++) { + Region merged = this.getMergedRegionAt(i); - boolean inStart = (merged.getRowFrom() >= startRow || merged.getRowTo() >= startRow); - boolean inEnd = (merged.getRowTo() <= endRow || merged.getRowFrom() <= endRow); + boolean inStart = (merged.getRowFrom() >= startRow || merged.getRowTo() >= startRow); + boolean inEnd = (merged.getRowTo() <= endRow || merged.getRowFrom() <= endRow); - //dont check if it's not within the shifted area - if (! (inStart && inEnd)) continue; + //dont check if it's not within the shifted area + if (! (inStart && inEnd)) continue; - //only shift if the region outside the shifted rows is not merged too - if (!merged.contains(startRow-1, (short)0) && !merged.contains(endRow+1, (short)0)){ - merged.setRowFrom(merged.getRowFrom()+n); - merged.setRowTo(merged.getRowTo()+n); - //have to remove/add it back - shiftedRegions.add(merged); - this.removeMergedRegion(i); - i = i -1; // we have to back up now since we removed one + //only shift if the region outside the shifted rows is not merged too + if (!merged.contains(startRow-1, (short)0) && !merged.contains(endRow+1, (short)0)){ + merged.setRowFrom(merged.getRowFrom()+n); + merged.setRowTo(merged.getRowTo()+n); + //have to remove/add it back + shiftedRegions.add(merged); + this.removeMergedRegion(i); + i = i -1; // we have to back up now since we removed one - } + } - } + } - //readd so it doesn't get shifted again - Iterator iterator = shiftedRegions.iterator(); - while (iterator.hasNext()) { - Region region = (Region)iterator.next(); + //readd so it doesn't get shifted again + Iterator iterator = shiftedRegions.iterator(); + while (iterator.hasNext()) { + Region region = (Region)iterator.next(); - this.addMergedRegion(region); - } + this.addMergedRegion(region); + } - } + } /** * Shifts rows between startRow and endRow n number of rows. @@ -1174,7 +1169,7 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet * @param n the number of rows to shift */ public void shiftRows( int startRow, int endRow, int n ) { - shiftRows(startRow, endRow, n, false, false); + shiftRows(startRow, endRow, n, false, false); } /** @@ -1211,7 +1206,7 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet shiftMerged(startRow, endRow, n, true); sheet.shiftRowBreaks(startRow, endRow, n); - + for ( int rowNum = s; rowNum >= startRow && rowNum <= endRow && rowNum >= 0 && rowNum < 65536; rowNum += inc ) { HSSFRow row = getRow( rowNum ); @@ -1224,23 +1219,23 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet - // Removes the cells before over writting them. + // Removes the cells before over writting them. for ( short col = row2Replace.getFirstCellNum(); col <= row2Replace.getLastCellNum(); col++ ) { cell = row2Replace.getCell( col ); if ( cell != null ) row2Replace.removeCell( cell ); } - if (row == null) continue; // Nothing to do for this row - else { - if (copyRowHeight) { - row2Replace.setHeight(row.getHeight()); - } + if (row == null) continue; // Nothing to do for this row + else { + if (copyRowHeight) { + row2Replace.setHeight(row.getHeight()); + } - if (resetOriginalRowHeight) { - row.setHeight((short)0xff); - } - } + if (resetOriginalRowHeight) { + row.setHeight((short)0xff); + } + } for ( short col = row.getFirstCellNum(); col <= row.getLastCellNum(); col++ ) { cell = row.getCell( col ); @@ -1262,55 +1257,55 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet } if ( endRow == lastrow || endRow + n > lastrow ) lastrow = Math.min( endRow + n, 65535 ); if ( startRow == firstrow || startRow + n < firstrow ) firstrow = Math.max( startRow + n, 0 ); - + // Update any formulas on this sheet that point to // rows which have been moved updateFormulasAfterShift(startRow, endRow, n); } - + /** * Called by shiftRows to update formulas on this sheet * to point to the new location of moved rows */ private void updateFormulasAfterShift(int startRow, int endRow, int n) { - // Need to look at every cell on the sheet - // Not just those that were moved + // Need to look at every cell on the sheet + // Not just those that were moved Iterator ri = rowIterator(); while(ri.hasNext()) { - HSSFRow r = (HSSFRow)ri.next(); - Iterator ci = r.cellIterator(); - while(ci.hasNext()) { - HSSFCell c = (HSSFCell)ci.next(); - if(c.getCellType() == HSSFCell.CELL_TYPE_FORMULA) { - // Since it's a formula cell, process the - // formula string, and look to see if - // it contains any references - FormulaParser fp = new FormulaParser(c.getCellFormula(), workbook.getWorkbook()); - fp.parse(); - - // Look for references, and update if needed - Ptg[] ptgs = fp.getRPNPtg(); - boolean changed = false; - for(int i=0; i 0) { - int[] returnValue = new int[count]; - Iterator iterator = sheet.getRowBreaks(); - int i = 0; - while (iterator.hasNext()) { - PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next(); - returnValue[i++] = (int)breakItem.main; - } - return returnValue; - } - return null; + //we can probably cache this information, but this should be a sparsely used function + int count = sheet.getNumRowBreaks(); + if (count > 0) { + int[] returnValue = new int[count]; + Iterator iterator = sheet.getRowBreaks(); + int i = 0; + while (iterator.hasNext()) { + PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next(); + returnValue[i++] = (int)breakItem.main; + } + return returnValue; + } + return null; } /** @@ -1471,29 +1466,29 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet * @return all the vertical page breaks, or null if there are no column page breaks */ public short[] getColumnBreaks(){ - //we can probably cache this information, but this should be a sparsely used function - int count = sheet.getNumColumnBreaks(); - if (count > 0) { - short[] returnValue = new short[count]; - Iterator iterator = sheet.getColumnBreaks(); - int i = 0; - while (iterator.hasNext()) { - PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next(); - returnValue[i++] = breakItem.main; - } - return returnValue; - } - return null; + //we can probably cache this information, but this should be a sparsely used function + int count = sheet.getNumColumnBreaks(); + if (count > 0) { + short[] returnValue = new short[count]; + Iterator iterator = sheet.getColumnBreaks(); + int i = 0; + while (iterator.hasNext()) { + PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next(); + returnValue[i++] = breakItem.main; + } + return returnValue; + } + return null; } - - + + /** * Sets a page break at the indicated column * @param column */ public void setColumnBreak(short column) { - validateColumn(column); - sheet.setColumnBreak(column, (short)0, (short)65535); + validateColumn(column); + sheet.setColumnBreak(column, (short)0, (short)65535); } /** @@ -1502,33 +1497,33 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet * @return FIXME: Document this! */ public boolean isColumnBroken(short column) { - return sheet.isColumnBroken(column); + return sheet.isColumnBroken(column); } - + /** * Removes a page break at the indicated column * @param column */ public void removeColumnBreak(short column) { - sheet.removeColumnBreak(column); + sheet.removeColumnBreak(column); } - + /** * Runs a bounds check for row numbers * @param row */ protected void validateRow(int row) { - if (row > 65535) throw new IllegalArgumentException("Maximum row number is 65535"); - if (row < 0) throw new IllegalArgumentException("Minumum row number is 0"); + if (row > 65535) throw new IllegalArgumentException("Maximum row number is 65535"); + if (row < 0) throw new IllegalArgumentException("Minumum row number is 0"); } - + /** * Runs a bounds check for column numbers * @param column */ protected void validateColumn(short column) { - if (column > 255) throw new IllegalArgumentException("Maximum column number is 255"); - if (column < 0) throw new IllegalArgumentException("Minimum column number is 0"); + if (column > 255) throw new IllegalArgumentException("Maximum column number is 255"); + if (column < 0) throw new IllegalArgumentException("Minimum column number is 0"); } /** @@ -1573,7 +1568,7 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet agg.setPatriarch(patriarch); return patriarch; } - + /** * Returns the top-level drawing patriach, if there is * one. @@ -1587,32 +1582,32 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet * start from scratch! */ public HSSFPatriarch getDrawingPatriarch() { - book.findDrawingGroup(); - - // If there's now no drawing manager, then there's - // no drawing escher records on the workbook - if(book.getDrawingManager() == null) { - return null; - } - - int found = sheet.aggregateDrawingRecords( - book.getDrawingManager(), false - ); - if(found == -1) { - // Workbook has drawing stuff, but this sheet doesn't - return null; - } - - // Grab our aggregate record, and wire it up + book.findDrawingGroup(); + + // If there's now no drawing manager, then there's + // no drawing escher records on the workbook + if(book.getDrawingManager() == null) { + return null; + } + + int found = sheet.aggregateDrawingRecords( + book.getDrawingManager(), false + ); + if(found == -1) { + // Workbook has drawing stuff, but this sheet doesn't + return null; + } + + // Grab our aggregate record, and wire it up EscherAggregate agg = (EscherAggregate) sheet.findFirstRecordBySid(EscherAggregate.sid); HSSFPatriarch patriarch = new HSSFPatriarch(this, agg); agg.setPatriarch(patriarch); - + // Have it process the records into high level objects // as best it can do (this step may eat anything // that isn't supported, you were warned...) agg.convertRecordsToUserModel(); - + // Return what we could cope with return patriarch; } @@ -1666,7 +1661,7 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet * @param style the style to set */ public void setDefaultColumnStyle(short column, CellStyle style) { - sheet.setColumn(column, new Short(((HSSFCellStyle) style).getIndex()), null, null, null, null); + sheet.setColumn(column, new Short(((HSSFCellStyle) style).getIndex()), null, null, null, null); } /** @@ -1687,13 +1682,13 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet * '0' looks to be a good choice. */ char defaultChar = '0'; - + /** * This is the multiple that the font height is scaled by when determining the * boundary of rotated text. */ double fontHeightMultiple = 2.0; - + FontRenderContext frc = new FontRenderContext(null, true, true); HSSFWorkbook wb = new HSSFWorkbook(book); @@ -1814,7 +1809,7 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet /** * Copy text attributes from the supplied HSSFFont to Java2D AttributedString */ - private void copyAttributes(HSSFFont font, AttributedString str, int startIdx, int endIdx){ + private void copyAttributes(HSSFFont font, AttributedString str, int startIdx, int endIdx) { str.addAttribute(TextAttribute.FAMILY, font.getFontName(), startIdx, endIdx); str.addAttribute(TextAttribute.SIZE, new Float(font.getFontHeightInPoints())); if (font.getBoldweight() == HSSFFont.BOLDWEIGHT_BOLD) str.addAttribute(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD, startIdx, endIdx); @@ -1847,86 +1842,86 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet /** - * A factory method allowing to create a conditional formatting rule - * with a cell comparison operator and + * A factory method allowing to create a conditional formatting rule + * with a cell comparison operator and * formatting rules such as font format, border format and pattern format - * + * * @param comparisonOperation - one of the following values:

    - *

  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_BETWEEN}
  • - *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_NOT_BETWEEN}
  • - *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_EQUAL}
  • - *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_NOT_EQUAL}
  • - *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_GT}
  • - *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_LT}
  • - *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_GE}
  • - *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_LE}
  • - *

    + *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_BETWEEN}
  • + *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_NOT_BETWEEN}
  • + *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_EQUAL}
  • + *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_NOT_EQUAL}
  • + *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_GT}
  • + *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_LT}
  • + *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_GE}
  • + *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_LE}
  • + *

    * @param formula1 - formula for the valued, compared with the cell - * @param formula2 - second formula (only used with + * @param formula2 - second formula (only used with * {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_BETWEEN}) and - * {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_NOT_BETWEEN} operations) + * {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_NOT_BETWEEN} operations) * @param fontFmt - font formatting rules * @param bordFmt - border formatting rules * @param patternFmt - pattern formatting rules * @return - * + * */ public HSSFConditionalFormattingRule createConditionalFormattingRule( - byte comparisonOperation, - String formula1, - String formula2, - HSSFFontFormatting fontFmt, - HSSFBorderFormatting bordFmt, - HSSFPatternFormatting patternFmt) + byte comparisonOperation, + String formula1, + String formula2, + HSSFFontFormatting fontFmt, + HSSFBorderFormatting bordFmt, + HSSFPatternFormatting patternFmt) { - HSSFConditionalFormattingRule cf = new HSSFConditionalFormattingRule(workbook); - cf.setFontFormatting(fontFmt); - cf.setBorderFormatting(bordFmt); - cf.setPatternFormatting(patternFmt); - cf.setCellComparisonCondition(comparisonOperation, formula1, formula2); - return cf; + HSSFConditionalFormattingRule cf = new HSSFConditionalFormattingRule(workbook); + cf.setFontFormatting(fontFmt); + cf.setBorderFormatting(bordFmt); + cf.setPatternFormatting(patternFmt); + cf.setCellComparisonCondition(comparisonOperation, formula1, formula2); + return cf; } /** - * A factory method allowing to create a conditional formatting rule with a formula + * A factory method allowing to create a conditional formatting rule with a formula * and formatting rules such as font format, border format and pattern format.
    - * - * The formatting rules are applied by Excel when the value of the formula not equal to 0. - * + * + * The formatting rules are applied by Excel when the value of the formula not equal to 0. + * * @param comparisonOperation - one of the following values:

    - *

  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_BETWEEN}
  • - *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_NOT_BETWEEN}
  • - *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_EQUAL}
  • - *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_NOT_EQUAL}
  • - *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_GT}
  • - *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_LT}
  • - *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_GE}
  • - *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_LE}
  • - *

    + *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_BETWEEN}
  • + *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_NOT_BETWEEN}
  • + *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_EQUAL}
  • + *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_NOT_EQUAL}
  • + *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_GT}
  • + *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_LT}
  • + *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_GE}
  • + *
  • {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_LE}
  • + *

    * @param formula1 - formula for the valued, compared with the cell - * @param formula2 - second formula (only used with + * @param formula2 - second formula (only used with * {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_BETWEEN}) and - * {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_NOT_BETWEEN} operations) + * {@link HSSFConditionalFormattingRule#COMPARISON_OPERATOR_NOT_BETWEEN} operations) * @param fontFmt - font formatting rules * @param bordFmt - border formatting rules * @param patternFmt - pattern formatting rules * @return - * + * */ public HSSFConditionalFormattingRule createConditionalFormattingRule( - String formula, - HSSFFontFormatting fontFmt, - HSSFBorderFormatting bordFmt, - HSSFPatternFormatting patternFmt) + String formula, + HSSFFontFormatting fontFmt, + HSSFBorderFormatting bordFmt, + HSSFPatternFormatting patternFmt) { - HSSFConditionalFormattingRule cf = new HSSFConditionalFormattingRule(workbook); - cf.setFontFormatting(fontFmt); - cf.setBorderFormatting(bordFmt); - cf.setPatternFormatting(patternFmt); - cf.setFormulaCondition(formula); - return cf; + HSSFConditionalFormattingRule cf = new HSSFConditionalFormattingRule(workbook); + cf.setFontFormatting(fontFmt); + cf.setBorderFormatting(bordFmt); + cf.setPatternFormatting(patternFmt); + cf.setFormulaCondition(formula); + return cf; } - + /** * Adds a copy of HSSFConditionalFormatting object to the sheet *

    This method could be used to copy HSSFConditionalFormatting object @@ -1934,72 +1929,71 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet *

           * HSSFConditionalFormatting cf = sheet.getConditionalFormattingAt(index);
           * newSheet.addConditionalFormatting(cf);
    -      * 
    - * + * + * * @param cf HSSFConditionalFormatting object * @return index of the new Conditional Formatting object */ public int addConditionalFormatting( HSSFConditionalFormatting cf ) { - HSSFConditionalFormatting cfClone = new HSSFConditionalFormatting(this,cf.cfAggregate.cloneCFAggregate()); - cfClone.sheet=this; - return sheet.addConditionalFormatting(cfClone.cfAggregate); + CFRecordsAggregate cfraClone = cf.getCFRecordsAggregate().cloneCFAggregate(); + + return sheet.addConditionalFormatting(cfraClone); } /** * Allows to add a new Conditional Formatting set to the sheet. - * - * @param regions - list of rectangular regions to apply conditional formatting rules + * + * @param regions - list of rectangular regions to apply conditional formatting rules * @param cfRules - set of up to three conditional formatting rules - * + * * @return index of the newly created Conditional Formatting object */ - + public int addConditionalFormatting( Region [] regions, HSSFConditionalFormattingRule [] cfRules ) { - HSSFConditionalFormatting cf = new HSSFConditionalFormatting(this); - cf.setFormattingRegions(regions); - if( cfRules != null ) - { - for( int i=0; i!= cfRules.length; i++ ) - { - cf.addConditionalFormat(cfRules[i]); - } - } - return sheet.addConditionalFormatting(cf.cfAggregate); + HSSFConditionalFormatting cf = new HSSFConditionalFormatting(this); + cf.setFormattingRegions(regions); + if( cfRules != null ) + { + for( int i=0; i!= cfRules.length; i++ ) + { + cf.addRule(cfRules[i]); + } + } + return sheet.addConditionalFormatting(cf.getCFRecordsAggregate()); } - - /** - * gets Conditional Formatting object at a particular index - * @param index of the Conditional Formatting object to fetch - * @return Conditional Formatting object - */ - - public HSSFConditionalFormatting getConditionalFormattingAt(int index) - { - CFRecordsAggregate cf = sheet.getCFRecordsAggregateAt(index); - if( cf != null ) - { - return new HSSFConditionalFormatting(this,cf); - } - return null; - } - /** - * @return number of Conditional Formatting objects of the sheet - */ - public int getNumConditionalFormattings() - { - return sheet.getNumConditionalFormattings(); - } + /** + * gets Conditional Formatting object at a particular index + * @param index of the Conditional Formatting object to fetch + * @return Conditional Formatting object + */ - /** - * removes a Conditional Formatting object by index - * @param index of a Conditional Formatting object to remove - */ - public void removeConditionalFormatting(int index) - { - sheet.removeConditionalFormatting(index); - } + public HSSFConditionalFormatting getConditionalFormattingAt(int index) + { + CFRecordsAggregate cf = sheet.getCFRecordsAggregateAt(index); + if( cf != null ) + { + return new HSSFConditionalFormatting(this,cf); + } + return null; + } + /** + * @return number of Conditional Formatting objects of the sheet + */ + public int getNumConditionalFormattings() + { + return sheet.getNumConditionalFormattings(); + } + + /** + * removes a Conditional Formatting object by index + * @param index of a Conditional Formatting object to remove + */ + public void removeConditionalFormatting(int index) + { + sheet.removeConditionalFormatting(index); + } } diff --git a/src/resources/fontmetrics/font_metrics.properties b/src/resources/main/font_metrics.properties similarity index 100% rename from src/resources/fontmetrics/font_metrics.properties rename to src/resources/main/font_metrics.properties diff --git a/src/resources/main/org/apache/poi/hssf/record/formula/function/functionMetadata-asGenerated.txt b/src/resources/main/org/apache/poi/hssf/record/formula/function/functionMetadata-asGenerated.txt new file mode 100644 index 0000000000..60d2f1fe8b --- /dev/null +++ b/src/resources/main/org/apache/poi/hssf/record/formula/function/functionMetadata-asGenerated.txt @@ -0,0 +1,283 @@ +# 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 by (org.apache.poi.hssf.record.formula.function.ExcelFileFormatDocFunctionExtractor) +# from source file 'excelfileformat.odt' (size=355750, crc=0x2FAEA65A) +# +#Columns: (index, name, minParams, maxParams, returnClass, paramClasses, isVolatile, hasFootnote ) + +# Built-In Sheet Functions in BIFF2 +0 COUNT 0 30 V R +1 IF 2 3 R V R R +2 ISNA 1 1 V V +3 ISERROR 1 1 V V +4 SUM 0 30 V R +5 AVERAGE 1 30 V R +6 MIN 1 30 V R +7 MAX 1 30 V R +8 ROW 0 1 V R +9 COLUMN 0 1 V R +10 NA 0 0 V – +11 NPV 2 30 V V R +12 STDEV 1 30 V R +13 DOLLAR 1 2 V V V +14 FIXED 2 2 V V V x +15 SIN 1 1 V V +16 COS 1 1 V V +17 TAN 1 1 V V +18 ARCTAN 1 1 V V +19 PI 0 0 V – +20 SQRT 1 1 V V +21 EXP 1 1 V V +22 LN 1 1 V V +23 LOG10 1 1 V V +24 ABS 1 1 V V +25 INT 1 1 V V +26 SIGN 1 1 V V +27 ROUND 2 2 V V V +28 LOOKUP 2 3 V V R R +29 INDEX 2 4 R R V V V +30 REPT 2 2 V V V +31 MID 3 3 V V V V +32 LEN 1 1 V V +33 VALUE 1 1 V V +34 TRUE 0 0 V – +35 FALSE 0 0 V – +36 AND 1 30 V R +37 OR 1 30 V R +38 NOT 1 1 V V +39 MOD 2 2 V V V +40 DCOUNT 3 3 V R R R +41 DSUM 3 3 V R R R +42 DAVERAGE 3 3 V R R R +43 DMIN 3 3 V R R R +44 DMAX 3 3 V R R R +45 DSTDEV 3 3 V R R R +46 VAR 1 30 V R +47 DVAR 3 3 V R R R +48 TEXT 2 2 V V V +49 LINEST 1 2 A R R x +50 TREND 1 3 A R R R x +51 LOGEST 1 2 A R R x +52 GROWTH 1 3 A R R R x +56 PV 3 5 V V V V V V +# Built-In Sheet Functions in BIFF2 +57 FV 3 5 V V V V V V +58 NPER 3 5 V V V V V V +59 PMT 3 5 V V V V V V +60 RATE 3 6 V V V V V V V +61 MIRR 3 3 V R V V +62 IRR 1 2 V R V +63 RAND 0 0 V – x +64 MATCH 2 3 V V R R +65 DATE 3 3 V V V V +66 TIME 3 3 V V V V +67 DAY 1 1 V V +68 MONTH 1 1 V V +69 YEAR 1 1 V V +70 WEEKDAY 1 1 V V x +71 HOUR 1 1 V V +72 MINUTE 1 1 V V +73 SECOND 1 1 V V +74 NOW 0 0 V – x +75 AREAS 1 1 V R +76 ROWS 1 1 V R +77 COLUMNS 1 1 V R +78 OFFSET 3 5 R R V V V V x +82 SEARCH 2 3 V V V V +83 TRANSPOSE 1 1 A A +86 TYPE 1 1 V V +97 ATAN2 2 2 V V V +98 ASIN 1 1 V V +99 ACOS 1 1 V V +100 CHOOSE 2 30 R V R +101 HLOOKUP 3 3 V V R R x +102 VLOOKUP 3 3 V V R R x +105 ISREF 1 1 V R +109 LOG 1 2 V V V +111 CHAR 1 1 V V +112 LOWER 1 1 V V +113 UPPER 1 1 V V +114 PROPER 1 1 V V +115 LEFT 1 2 V V V +116 RIGHT 1 2 V V V +117 EXACT 2 2 V V V +118 TRIM 1 1 V V +119 REPLACE 4 4 V V V V V +120 SUBSTITUTE 3 4 V V V V V +121 CODE 1 1 V V +124 FIND 2 3 V V V V +125 CELL 1 2 V V R x +126 ISERR 1 1 V V +127 ISTEXT 1 1 V V +128 ISNUMBER 1 1 V V +129 ISBLANK 1 1 V V +130 T 1 1 V R +131 N 1 1 V R +140 DATEVALUE 1 1 V V +141 TIMEVALUE 1 1 V V +142 SLN 3 3 V V V V +143 SYD 4 4 V V V V V +144 DDB 4 5 V V V V V V +148 INDIRECT 1 2 R V V x +162 CLEAN 1 1 V V +163 MDETERM 1 1 V A +164 MINVERSE 1 1 A A +165 MMULT 2 2 A A A +167 IPMT 4 6 V V V V V V V +168 PPMT 4 6 V V V V V V V +169 COUNTA 0 30 V R +183 PRODUCT 0 30 V R +184 FACT 1 1 V V +191 DPRODUCT 3 3 V R R R +192 ISNONTEXT 1 1 V V +193 STDEVP 1 30 V R +194 VARP 1 30 V R +195 DSTDEVP 3 3 V R R R +196 DVARP 3 3 V R R R +197 TRUNC 1 1 V V x +198 ISLOGICAL 1 1 V V +199 DCOUNTA 3 3 V R R R +# New Built-In Sheet Functions in BIFF3 +49 LINEST 1 4 A R R V V x +50 TREND 1 4 A R R R V x +51 LOGEST 1 4 A R R V V x +52 GROWTH 1 4 A R R R V x +197 TRUNC 1 2 V V V x +204 YEN 1 2 V V V x +205 FINDB 2 3 V V V V +206 SEARCHB 2 3 V V V V +207 REPLACEB 4 4 V V V V V +208 LEFTB 1 2 V V V +209 RIGHTB 1 2 V V V +210 MIDB 3 3 V V V V +211 LENB 1 1 V V +212 ROUNDUP 2 2 V V V +213 ROUNDDOWN 2 2 V V V +214 ASC 1 1 V V +215 JIS 1 1 V V x +219 ADDRESS 2 5 V V V V V V +220 DAYS360 2 2 V V V x +221 TODAY 0 0 V – x +222 VDB 5 7 V V V V V V V V +227 MEDIAN 1 30 V R … +228 SUMPRODUCT 1 30 V A … +229 SINH 1 1 V V +230 COSH 1 1 V V +231 TANH 1 1 V V +232 ASINH 1 1 V V +233 ACOSH 1 1 V V +234 ATANH 1 1 V V +235 DGET 3 3 V R R R +244 INFO 1 1 V V +# New Built-In Sheet Functions in BIFF4 +14 FIXED 2 3 V V V V x +216 RANK 2 3 V V R V +247 DB 4 5 V V V V V V +252 FREQUENCY 2 2 A R R +261 ERROR.TYPE 1 1 V V +269 AVEDEV 1 30 V R … +270 BETADIST 3 5 V V V V V V +271 GAMMALN 1 1 V V +272 BETAINV 3 5 V V V V V V +273 BINOMDIST 4 4 V V V V V +274 CHIDIST 2 2 V V V +275 CHIINV 2 2 V V V +276 COMBIN 2 2 V V V +277 CONFIDENCE 3 3 V V V V +278 CRITBINOM 3 3 V V V V +279 EVEN 1 1 V V +280 EXPONDIST 3 3 V V V V +281 FDIST 3 3 V V V V +282 FINV 3 3 V V V V +283 FISHER 1 1 V V +284 FISHERINV 1 1 V V +285 FLOOR 2 2 V V V +286 GAMMADIST 4 4 V V V V V +287 GAMMAINV 3 3 V V V V +288 CEILING 2 2 V V V +289 HYPGEOMDIST 4 4 V V V V V +290 LOGNORMDIST 3 3 V V V V +291 LOGINV 3 3 V V V V +292 NEGBINOMDIST 3 3 V V V V +293 NORMDIST 4 4 V V V V V +294 NORMSDIST 1 1 V V +295 NORMINV 3 3 V V V V +296 NORMSINV 1 1 V V +297 STANDARDIZE 3 3 V V V V +298 ODD 1 1 V V +299 PERMUT 2 2 V V V +300 POISSON 3 3 V V V V +301 TDIST 3 3 V V V V +302 WEIBULL 4 4 V V V V V +303 SUMXMY2 2 2 V A A +304 SUMX2MY2 2 2 V A A +305 SUMX2PY2 2 2 V A A +306 CHITEST 2 2 V A A +307 CORREL 2 2 V A A +308 COVAR 2 2 V A A +309 FORECAST 3 3 V V A A +310 FTEST 2 2 V A A +311 INTERCEPT 2 2 V A A +312 PEARSON 2 2 V A A +313 RSQ 2 2 V A A +314 STEYX 2 2 V A A +315 SLOPE 2 2 V A A +316 TTEST 4 4 V A A V V +317 PROB 3 4 V A A V V +318 DEVSQ 1 30 V R … +319 GEOMEAN 1 30 V R … +320 HARMEAN 1 30 V R … +321 SUMSQ 0 30 V R … +322 KURT 1 30 V R … +323 SKEW 1 30 V R … +324 ZTEST 2 3 V R V V +325 LARGE 2 2 V R V +326 SMALL 2 2 V R V +327 QUARTILE 2 2 V R V +328 PERCENTILE 2 2 V R V +329 PERCENTRANK 2 3 V R V V +330 MODE 1 30 V A +331 TRIMMEAN 2 2 V R V +332 TINV 2 2 V V V +# New Built-In Sheet Functions in BIFF5 +70 WEEKDAY 1 2 V V V x +101 HLOOKUP 3 4 V V R R V x +102 VLOOKUP 3 4 V V R R V x +220 DAYS360 2 3 V V V V x +336 CONCATENATE 0 30 V V +337 POWER 2 2 V V V +342 RADIANS 1 1 V V +343 DEGREES 1 1 V V +344 SUBTOTAL 2 30 V V R +345 SUMIF 2 3 V R V R +346 COUNTIF 2 2 V R V +347 COUNTBLANK 1 1 V R +350 ISPMT 4 4 V V V V V +351 DATEDIF 3 3 V V V V +352 DATESTRING 1 1 V V +353 NUMBERSTRING 2 2 V V V +354 ROMAN 1 2 V V V +# New Built-In Sheet Functions in BIFF8 +358 GETPIVOTDATA 2 30 +359 HYPERLINK 1 2 V V V +360 PHONETIC 1 1 V R +361 AVERAGEA 1 30 V R … +362 MAXA 1 30 V R … +363 MINA 1 30 V R … +364 STDEVPA 1 30 V R … +365 VARPA 1 30 V R … +366 STDEVA 1 30 V R … +367 VARA 1 30 V R … diff --git a/src/resources/main/org/apache/poi/hssf/record/formula/function/functionMetadata.txt b/src/resources/main/org/apache/poi/hssf/record/formula/function/functionMetadata.txt new file mode 100644 index 0000000000..237fbf094f --- /dev/null +++ b/src/resources/main/org/apache/poi/hssf/record/formula/function/functionMetadata.txt @@ -0,0 +1,287 @@ +# 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 by (org.apache.poi.hssf.record.formula.function.ExcelFileFormatDocFunctionExtractor) +# from source file 'excelfileformat.odt' (size=355750, crc=0x2FAEA65A) +# +#Columns: (index, name, minParams, maxParams, returnClass, paramClasses, isVolatile, hasFootnote ) +# +# + some manual edits ! + +# Built-In Sheet Functions in BIFF2 +0 COUNT 0 30 V R +1 IF 2 3 R V R R +2 ISNA 1 1 V V +3 ISERROR 1 1 V V +4 SUM 0 30 V R +5 AVERAGE 1 30 V R +6 MIN 1 30 V R +7 MAX 1 30 V R +8 ROW 0 1 V R +9 COLUMN 0 1 V R +10 NA 0 0 V – +11 NPV 2 30 V V R +12 STDEV 1 30 V R +13 DOLLAR 1 2 V V V +14 FIXED 2 2 V V V x +15 SIN 1 1 V V +16 COS 1 1 V V +17 TAN 1 1 V V +18 ATAN 1 1 V V +19 PI 0 0 V – +20 SQRT 1 1 V V +21 EXP 1 1 V V +22 LN 1 1 V V +23 LOG10 1 1 V V +24 ABS 1 1 V V +25 INT 1 1 V V +26 SIGN 1 1 V V +27 ROUND 2 2 V V V +28 LOOKUP 2 3 V V R R +29 INDEX 2 4 R R V V V +30 REPT 2 2 V V V +31 MID 3 3 V V V V +32 LEN 1 1 V V +33 VALUE 1 1 V V +34 TRUE 0 0 V – +35 FALSE 0 0 V – +36 AND 1 30 V R +37 OR 1 30 V R +38 NOT 1 1 V V +39 MOD 2 2 V V V +40 DCOUNT 3 3 V R R R +41 DSUM 3 3 V R R R +42 DAVERAGE 3 3 V R R R +43 DMIN 3 3 V R R R +44 DMAX 3 3 V R R R +45 DSTDEV 3 3 V R R R +46 VAR 1 30 V R +47 DVAR 3 3 V R R R +48 TEXT 2 2 V V V +49 LINEST 1 2 A R R x +50 TREND 1 3 A R R R x +51 LOGEST 1 2 A R R x +52 GROWTH 1 3 A R R R x +56 PV 3 5 V V V V V V +# Built-In Sheet Functions in BIFF2 +57 FV 3 5 V V V V V V +58 NPER 3 5 V V V V V V +59 PMT 3 5 V V V V V V +60 RATE 3 6 V V V V V V V +61 MIRR 3 3 V R V V +62 IRR 1 2 V R V +63 RAND 0 0 V – x +64 MATCH 2 3 V V R R +65 DATE 3 3 V V V V +66 TIME 3 3 V V V V +67 DAY 1 1 V V +68 MONTH 1 1 V V +69 YEAR 1 1 V V +70 WEEKDAY 1 1 V V x +71 HOUR 1 1 V V +72 MINUTE 1 1 V V +73 SECOND 1 1 V V +74 NOW 0 0 V – x +75 AREAS 1 1 V R +76 ROWS 1 1 V R +77 COLUMNS 1 1 V R +78 OFFSET 3 5 R R V V V V x +82 SEARCH 2 3 V V V V +83 TRANSPOSE 1 1 A A +86 TYPE 1 1 V V +97 ATAN2 2 2 V V V +98 ASIN 1 1 V V +99 ACOS 1 1 V V +100 CHOOSE 2 30 R V R +101 HLOOKUP 3 3 V V R R x +102 VLOOKUP 3 3 V V R R x +105 ISREF 1 1 V R +109 LOG 1 2 V V V +111 CHAR 1 1 V V +112 LOWER 1 1 V V +113 UPPER 1 1 V V +114 PROPER 1 1 V V +115 LEFT 1 2 V V V +116 RIGHT 1 2 V V V +117 EXACT 2 2 V V V +118 TRIM 1 1 V V +119 REPLACE 4 4 V V V V V +120 SUBSTITUTE 3 4 V V V V V +121 CODE 1 1 V V +124 FIND 2 3 V V V V +125 CELL 1 2 V V R x +126 ISERR 1 1 V V +127 ISTEXT 1 1 V V +128 ISNUMBER 1 1 V V +129 ISBLANK 1 1 V V +130 T 1 1 V R +131 N 1 1 V R +140 DATEVALUE 1 1 V V +141 TIMEVALUE 1 1 V V +142 SLN 3 3 V V V V +143 SYD 4 4 V V V V V +144 DDB 4 5 V V V V V V +148 INDIRECT 1 2 R V V x +162 CLEAN 1 1 V V +163 MDETERM 1 1 V A +164 MINVERSE 1 1 A A +165 MMULT 2 2 A A A +167 IPMT 4 6 V V V V V V V +168 PPMT 4 6 V V V V V V V +169 COUNTA 0 30 V R +183 PRODUCT 0 30 V R +184 FACT 1 1 V V +190 ISNONTEXT 1 1 V V +191 DPRODUCT 3 3 V R R R +193 STDEVP 1 30 V R +194 VARP 1 30 V R +195 DSTDEVP 3 3 V R R R +196 DVARP 3 3 V R R R +197 TRUNC 1 1 V V x +198 ISLOGICAL 1 1 V V +199 DCOUNTA 3 3 V R R R +# New Built-In Sheet Functions in BIFF3 +49 LINEST 1 4 A R R V V x +50 TREND 1 4 A R R R V x +51 LOGEST 1 4 A R R V V x +52 GROWTH 1 4 A R R R V x +197 TRUNC 1 2 V V V x +204 YEN 1 2 V V V x +205 FINDB 2 3 V V V V +206 SEARCHB 2 3 V V V V +207 REPLACEB 4 4 V V V V V +208 LEFTB 1 2 V V V +209 RIGHTB 1 2 V V V +210 MIDB 3 3 V V V V +211 LENB 1 1 V V +212 ROUNDUP 2 2 V V V +213 ROUNDDOWN 2 2 V V V +214 ASC 1 1 V V +215 JIS 1 1 V V x +219 ADDRESS 2 5 V V V V V V +220 DAYS360 2 2 V V V x +221 TODAY 0 0 V – x +222 VDB 5 7 V V V V V V V V +227 MEDIAN 1 30 V R … +228 SUMPRODUCT 1 30 V A … +229 SINH 1 1 V V +230 COSH 1 1 V V +231 TANH 1 1 V V +232 ASINH 1 1 V V +233 ACOSH 1 1 V V +234 ATANH 1 1 V V +235 DGET 3 3 V R R R +244 INFO 1 1 V V +# New Built-In Sheet Functions in BIFF4 +14 FIXED 2 3 V V V V x +204 USDOLLAR 1 1 V V x +215 DBCS 1 1 V V x +216 RANK 2 3 V V R V +247 DB 4 5 V V V V V V +252 FREQUENCY 2 2 A R R +261 ERROR.TYPE 1 1 V V +269 AVEDEV 1 30 V R … +270 BETADIST 3 5 V V V V V V +271 GAMMALN 1 1 V V +272 BETAINV 3 5 V V V V V V +273 BINOMDIST 4 4 V V V V V +274 CHIDIST 2 2 V V V +275 CHIINV 2 2 V V V +276 COMBIN 2 2 V V V +277 CONFIDENCE 3 3 V V V V +278 CRITBINOM 3 3 V V V V +279 EVEN 1 1 V V +280 EXPONDIST 3 3 V V V V +281 FDIST 3 3 V V V V +282 FINV 3 3 V V V V +283 FISHER 1 1 V V +284 FISHERINV 1 1 V V +285 FLOOR 2 2 V V V +286 GAMMADIST 4 4 V V V V V +287 GAMMAINV 3 3 V V V V +288 CEILING 2 2 V V V +289 HYPGEOMDIST 4 4 V V V V V +290 LOGNORMDIST 3 3 V V V V +291 LOGINV 3 3 V V V V +292 NEGBINOMDIST 3 3 V V V V +293 NORMDIST 4 4 V V V V V +294 NORMSDIST 1 1 V V +295 NORMINV 3 3 V V V V +296 NORMSINV 1 1 V V +297 STANDARDIZE 3 3 V V V V +298 ODD 1 1 V V +299 PERMUT 2 2 V V V +300 POISSON 3 3 V V V V +301 TDIST 3 3 V V V V +302 WEIBULL 4 4 V V V V V +303 SUMXMY2 2 2 V A A +304 SUMX2MY2 2 2 V A A +305 SUMX2PY2 2 2 V A A +306 CHITEST 2 2 V A A +307 CORREL 2 2 V A A +308 COVAR 2 2 V A A +309 FORECAST 3 3 V V A A +310 FTEST 2 2 V A A +311 INTERCEPT 2 2 V A A +312 PEARSON 2 2 V A A +313 RSQ 2 2 V A A +314 STEYX 2 2 V A A +315 SLOPE 2 2 V A A +316 TTEST 4 4 V A A V V +317 PROB 3 4 V A A V V +318 DEVSQ 1 30 V R … +319 GEOMEAN 1 30 V R … +320 HARMEAN 1 30 V R … +321 SUMSQ 0 30 V R … +322 KURT 1 30 V R … +323 SKEW 1 30 V R … +324 ZTEST 2 3 V R V V +325 LARGE 2 2 V R V +326 SMALL 2 2 V R V +327 QUARTILE 2 2 V R V +328 PERCENTILE 2 2 V R V +329 PERCENTRANK 2 3 V R V V +330 MODE 1 30 V A +331 TRIMMEAN 2 2 V R V +332 TINV 2 2 V V V +# New Built-In Sheet Functions in BIFF5 +70 WEEKDAY 1 2 V V V x +101 HLOOKUP 3 4 V V R R V x +102 VLOOKUP 3 4 V V R R V x +220 DAYS360 2 3 V V V V x +336 CONCATENATE 0 30 V V +337 POWER 2 2 V V V +342 RADIANS 1 1 V V +343 DEGREES 1 1 V V +344 SUBTOTAL 2 30 V V R +345 SUMIF 2 3 V R V R +346 COUNTIF 2 2 V R V +347 COUNTBLANK 1 1 V R +350 ISPMT 4 4 V V V V V +351 DATEDIF 3 3 V V V V +352 DATESTRING 1 1 V V +353 NUMBERSTRING 2 2 V V V +354 ROMAN 1 2 V V V +# New Built-In Sheet Functions in BIFF8 +358 GETPIVOTDATA 2 30 +359 HYPERLINK 1 2 V V V +360 PHONETIC 1 1 V R +361 AVERAGEA 1 30 V R … +362 MAXA 1 30 V R … +363 MINA 1 30 V R … +364 STDEVPA 1 30 V R … +365 VARPA 1 30 V R … +366 STDEVA 1 30 V R … +367 VARA 1 30 V R … diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Pmt.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Pmt.java index 0ea6a7f107..58628053c0 100644 --- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Pmt.java +++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Pmt.java @@ -14,61 +14,78 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * Created on May 15, 2005 - * - */ package org.apache.poi.hssf.record.formula.functions; import org.apache.poi.hssf.record.formula.eval.BoolEval; import org.apache.poi.hssf.record.formula.eval.ErrorEval; import org.apache.poi.hssf.record.formula.eval.Eval; +import org.apache.poi.hssf.record.formula.eval.EvaluationException; import org.apache.poi.hssf.record.formula.eval.NumberEval; import org.apache.poi.hssf.record.formula.eval.NumericValueEval; import org.apache.poi.hssf.record.formula.eval.ValueEval; -public class Pmt extends FinanceFunction { +/** + * Implementation for the PMT() Excel function.

    + * + * Syntax:
    + * PMT(rate, nper, pv, fv, type)

    + * + * Returns the constant repayment amount required for a loan assuming a constant interest rate.

    + * + * rate the loan interest rate.
    + * nper the number of loan repayments.
    + * pv the present value of the future payments (or principle).
    + * fv the future value (default zero) surplus cash at the end of the loan lifetime.
    + * type whether payments are due at the beginning(1) or end(0 - default) of each payment period.
    + * + */ +public final class Pmt extends FinanceFunction { - public Eval evaluate(Eval[] operands, int srcRow, short srcCol) { - double rate = 0, fv = 0, nper = 0, pv = 0, d = 0; - boolean type = false; - ValueEval retval = null; - ValueEval ve = null; - - switch (operands.length) { - default: - retval = ErrorEval.VALUE_INVALID; - break; - case 5: - ve = singleOperandNumericAsBoolean(operands[4], srcRow, srcCol); - if (ve instanceof ErrorEval) { retval = ErrorEval.VALUE_INVALID; break; } - type = ((BoolEval) ve).getBooleanValue(); - case 4: - ve = singleOperandEvaluate(operands[0], srcRow, srcCol); - if (ve instanceof NumericValueEval) rate = ((NumericValueEval) ve).getNumberValue(); - else { retval = ErrorEval.VALUE_INVALID; break; } - - ve = singleOperandEvaluate(operands[1], srcRow, srcCol); - if (ve instanceof NumericValueEval) nper = ((NumericValueEval) ve).getNumberValue(); - else { retval = ErrorEval.VALUE_INVALID; break; } - - ve = singleOperandEvaluate(operands[2], srcRow, srcCol); - if (ve instanceof NumericValueEval) pv = ((NumericValueEval) ve).getNumberValue(); - else { retval = ErrorEval.VALUE_INVALID; break; } - - ve = singleOperandEvaluate(operands[3], srcRow, srcCol); - if (ve instanceof NumericValueEval) fv = ((NumericValueEval) ve).getNumberValue(); - else { retval = ErrorEval.VALUE_INVALID; break; } - } - - if (retval == null) { - d = FinanceLib.pmt(rate, nper, pv, fv, type); - retval = (Double.isNaN(d)) - ? (ValueEval) ErrorEval.VALUE_INVALID - : (Double.isInfinite(d)) - ? (ValueEval) ErrorEval.NUM_ERROR - : new NumberEval(d); - } - return retval; - } + public Eval evaluate(Eval[] args, int srcRow, short srcCol) { + + if(args.length < 3 || args.length > 5) { + return ErrorEval.VALUE_INVALID; + } + + try { + // evaluate first three (always present) args + double rate = evalArg(args[0], srcRow, srcCol); + double nper = evalArg(args[1], srcRow, srcCol); + double pv = evalArg(args[2], srcRow, srcCol); + double fv = 0; + boolean arePaymentsAtPeriodBeginning = false; + + switch (args.length) { + case 5: + ValueEval ve = singleOperandNumericAsBoolean(args[4], srcRow, srcCol); + if (ve instanceof ErrorEval) { + return ve; + } + arePaymentsAtPeriodBeginning = ((BoolEval) ve).getBooleanValue(); + case 4: + fv = evalArg(args[3], srcRow, srcCol); + } + double d = FinanceLib.pmt(rate, nper, pv, fv, arePaymentsAtPeriodBeginning); + if (Double.isNaN(d)) { + return (ValueEval) ErrorEval.VALUE_INVALID; + } + if (Double.isInfinite(d)) { + return (ValueEval) ErrorEval.NUM_ERROR; + } + return new NumberEval(d); + } catch (EvaluationException e) { + return e.getErrorEval(); + } + } + + private double evalArg(Eval arg, int srcRow, short srcCol) throws EvaluationException { + ValueEval ve = singleOperandEvaluate(arg, srcRow, srcCol); + if(ve instanceof ErrorEval) { + throw new EvaluationException((ErrorEval) ve); + } + if (ve instanceof NumericValueEval) { + return ((NumericValueEval) ve).getNumberValue(); + } + throw new EvaluationException(ErrorEval.VALUE_INVALID); + } } diff --git a/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java b/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java index 3dbff81ca0..557060aa50 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java @@ -190,7 +190,7 @@ public class HWPFDocument extends POIDocument } // read in the pictures stream - _pictures = new PicturesTable(_dataStream); + _pictures = new PicturesTable(this, _dataStream); // get the start of text in the main stream int fcMin = _fib.getFcMin(); diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/PicturesTable.java b/src/scratchpad/src/org/apache/poi/hwpf/model/PicturesTable.java index 1ff84996cb..d9598b1061 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/model/PicturesTable.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/PicturesTable.java @@ -19,8 +19,10 @@ package org.apache.poi.hwpf.model; import org.apache.poi.util.LittleEndian; +import org.apache.poi.hwpf.HWPFDocument; import org.apache.poi.hwpf.usermodel.CharacterRun; import org.apache.poi.hwpf.usermodel.Picture; +import org.apache.poi.hwpf.usermodel.Range; import java.util.List; import java.util.ArrayList; @@ -53,6 +55,7 @@ public class PicturesTable static final int BLOCK_TYPE_OFFSET = 0xE; static final int MM_MODE_TYPE_OFFSET = 0x6; + private HWPFDocument _document; private byte[] _dataStream; /** @link dependency @@ -61,10 +64,12 @@ public class PicturesTable /** * + * @param document * @param _dataStream */ - public PicturesTable(byte[] _dataStream) + public PicturesTable(HWPFDocument _document, byte[] _dataStream) { + this._document = _document; this._dataStream = _dataStream; } @@ -119,24 +124,25 @@ public class PicturesTable } /** + * Not all documents have all the images concatenated in the data stream + * although MS claims so. The best approach is to scan all character runs. + * * @return a list of Picture objects found in current document */ public List getAllPictures() { ArrayList pictures = new ArrayList(); - - int pos = 0; - boolean atEnd = false; - - while(pos<_dataStream.length && !atEnd) { - if (isBlockContainsImage(pos)) { - pictures.add(new Picture(pos, _dataStream, false)); - } - - int skipOn = LittleEndian.getInt(_dataStream, pos); - if(skipOn <= 0) { atEnd = true; } - pos += skipOn; - } - + + Range range = _document.getRange(); + for (int i = 0; i < range.numCharacterRuns(); i++) { + CharacterRun run = range.getCharacterRun(i); + String text = run.text(); + int j = text.charAt(0); + Picture picture = extractPicture(run, false); + if (picture != null) { + pictures.add(picture); + } + } + return pictures; } diff --git a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java index d3e9c4c412..66d2a1d270 100755 --- a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java +++ b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java @@ -14,7 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - package org.apache.poi.hssf.record.formula.functions; @@ -41,6 +40,7 @@ public final class AllIndividualFunctionEvaluationTests { result.addTestSuite(TestMid.class); result.addTestSuite(TestMathX.class); result.addTestSuite(TestMatch.class); + result.addTestSuite(TestPmt.class); result.addTestSuite(TestOffset.class); result.addTestSuite(TestRowCol.class); result.addTestSuite(TestSumproduct.class); @@ -50,5 +50,4 @@ public final class AllIndividualFunctionEvaluationTests { result.addTestSuite(TestXYNumericFunction.class); return result; } - } diff --git a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestPmt.java b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestPmt.java new file mode 100644 index 0000000000..935615acae --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestPmt.java @@ -0,0 +1,87 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hssf.record.formula.functions; + +import junit.framework.AssertionFailedError; +import junit.framework.TestCase; + +import org.apache.poi.hssf.record.formula.eval.ErrorEval; +import org.apache.poi.hssf.record.formula.eval.Eval; +import org.apache.poi.hssf.record.formula.eval.NumberEval; +import org.apache.poi.hssf.usermodel.HSSFErrorConstants; + +/** + * + * @author Josh Micich + */ +public final class TestPmt extends TestCase { + + private static void confirm(double expected, NumberEval ne) { + // only asserting accuracy to 4 fractional digits + assertEquals(expected, ne.getNumberValue(), 0.00005); + } + private static Eval invoke(Eval[] args) { + return new Pmt().evaluate(args, -1, (short)-1); + } + /** + * Invocation when not expecting an error result + */ + private static NumberEval invokeNormal(Eval[] args) { + Eval ev = invoke(args); + if(ev instanceof ErrorEval) { + throw new AssertionFailedError("Normal evaluation failed with error code: " + + ev.toString()); + } + return (NumberEval) ev; + } + + private static void confirm(double expected, double rate, double nper, double pv, double fv, boolean isBeginning) { + Eval[] args = { + new NumberEval(rate), + new NumberEval(nper), + new NumberEval(pv), + new NumberEval(fv), + new NumberEval(isBeginning ? 1 : 0), + }; + confirm(expected, invokeNormal(args)); + } + + + public void testBasic() { + confirm(-1037.0321, (0.08/12), 10, 10000, 0, false); + confirm(-1030.1643, (0.08/12), 10, 10000, 0, true); + } + + public void test3args() { + + Eval[] args = { + new NumberEval(0.005), + new NumberEval(24), + new NumberEval(1000), + }; + Eval ev = invoke(args); + if(ev instanceof ErrorEval) { + ErrorEval err = (ErrorEval) ev; + if(err.getErrorCode() == HSSFErrorConstants.ERROR_VALUE) { + throw new AssertionFailedError("Identified bug 44691"); + } + } + + confirm(-44.3206, invokeNormal(args)); + } +} diff --git a/src/scratchpad/testcases/org/apache/poi/hwpf/data/Bug44603.doc b/src/scratchpad/testcases/org/apache/poi/hwpf/data/Bug44603.doc new file mode 100644 index 0000000000000000000000000000000000000000..00312ae1e16b55c025e881d145561f439e058c04 GIT binary patch literal 30208 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 Maw}8tFVzD72hTBQp#T5? literal 0 HcmV?d00001 diff --git a/src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestPictures.java b/src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestPictures.java index 686e558c84..3656b2ff5f 100644 --- a/src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestPictures.java +++ b/src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestPictures.java @@ -17,18 +17,15 @@ package org.apache.poi.hwpf.usermodel; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.FileInputStream; -import java.util.Iterator; import java.util.List; -import org.apache.poi.hwpf.HWPFDocument; -import org.apache.poi.hwpf.model.TextPiece; -import org.apache.poi.hwpf.usermodel.Paragraph; -import org.apache.poi.hwpf.usermodel.Range; -import org.apache.poi.util.LittleEndian; - import junit.framework.TestCase; +import org.apache.poi.hwpf.HWPFDocument; +import org.apache.poi.util.LittleEndian; + /** * Test the picture handling * @@ -118,6 +115,12 @@ public class TestPictures extends TestCase { * emf image, with a crazy offset */ public void testEmfComplexImage() throws Exception { + /* + + Commenting out this test case temporarily. The file emf_2003_image does not contain any + pictures. Instead it has an office drawing object. Need to rewrite this test after + revisiting the implementation of office drawing objects. + HWPFDocument doc = new HWPFDocument(new FileInputStream(dirname + "/emf_2003_image.doc")); List pics = doc.getPicturesTable().getAllPictures(); @@ -137,9 +140,17 @@ public class TestPictures extends TestCase { assertEquals(4, pic.getSize()); assertEquals(0x80000000l, LittleEndian.getUInt(pic.getContent())); assertEquals(0x80000000l, LittleEndian.getUInt(pic.getRawContent())); + */ } - - + + public void testPicturesWithTable() throws Exception { + HWPFDocument doc = new HWPFDocument(new FileInputStream( + new File(dirname, "Bug44603.doc"))); + + List pics = doc.getPicturesTable().getAllPictures(); + assertEquals(pics.size(), 2); + } + private byte[] loadImage(String filename) throws Exception { ByteArrayOutputStream b = new ByteArrayOutputStream(); FileInputStream fis = new FileInputStream(dirname + "/" + filename); diff --git a/src/testcases/org/apache/poi/hssf/data/44693.xls b/src/testcases/org/apache/poi/hssf/data/44693.xls new file mode 100644 index 0000000000000000000000000000000000000000..dd954c63d624ac107452b8ea922240c48263d459 GIT binary patch literal 40448 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 Tp?tXL=+|;q<(Yz?vj+YHqmLN~ literal 0 HcmV?d00001 diff --git a/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java b/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java index b79236ff08..3b98aed0ae 100644 --- a/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java +++ b/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java @@ -293,7 +293,7 @@ public final class TestFormulaParser extends TestCase { assertEquals("FOO", tname.toFormulaString(w)); AbstractFunctionPtg tfunc = (AbstractFunctionPtg) ptg[1]; - assertEquals("externalflag", tfunc.getName()); + assertTrue(tfunc.isExternalFunction()); } public void testEmbeddedSlash() { @@ -888,4 +888,17 @@ public final class TestFormulaParser extends TestCase { assertTrue(e.getMessage().startsWith("Too few arguments suppled to operation token")); } } + public void testFuncPtgSelection() { + Workbook book = Workbook.createWorkbook(); + Ptg[] ptgs; + ptgs = FormulaParser.parse("countif(A1:A2, 1)", book); + assertEquals(3, ptgs.length); + if(FuncVarPtg.class == ptgs[2].getClass()) { + throw new AssertionFailedError("Identified bug 44675"); + } + assertEquals(FuncPtg.class, ptgs[2].getClass()); + ptgs = FormulaParser.parse("sin(1)", book); + assertEquals(2, ptgs.length); + assertEquals(FuncPtg.class, ptgs[1].getClass()); + } } diff --git a/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java b/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java index b1acfeafa1..9da8f45ebc 100755 --- a/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java +++ b/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java @@ -14,7 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - package org.apache.poi.hssf.record; @@ -28,10 +27,10 @@ import junit.framework.TestSuite; * * @author Josh Micich */ -public class AllRecordTests { +public final class AllRecordTests { public static Test suite() { - TestSuite result = new TestSuite("Tests for org.apache.poi.hssf.record"); + TestSuite result = new TestSuite(AllRecordTests.class.getName()); result.addTest(AllFormulaTests.suite()); @@ -56,6 +55,7 @@ public class AllRecordTests { result.addTestSuite(TestEmbeddedObjectRefSubRecord.class); result.addTestSuite(TestEndSubRecord.class); result.addTestSuite(TestEscherAggregate.class); + result.addTestSuite(TestExternalNameRecord.class); result.addTestSuite(TestFontBasisRecord.class); result.addTestSuite(TestFontIndexRecord.class); result.addTestSuite(TestFormulaRecord.class); diff --git a/src/testcases/org/apache/poi/hssf/record/TestExternalNameRecord.java b/src/testcases/org/apache/poi/hssf/record/TestExternalNameRecord.java new file mode 100644 index 0000000000..960627a39b --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/record/TestExternalNameRecord.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; + +import junit.framework.AssertionFailedError; +import junit.framework.TestCase; +/** + * + * @author Josh Micich + */ +public final class TestExternalNameRecord extends TestCase { + + private static final byte[] dataFDS = { + 0, 0, 0, 0, 0, 0, 3, 0, 70, 68, 83, 0, 0, + }; + private static ExternalNameRecord createSimpleENR() { + return new ExternalNameRecord(new TestcaseRecordInputStream((short)0x0023, dataFDS)); + } + public void testBasicDeserializeReserialize() { + + ExternalNameRecord enr = createSimpleENR(); + assertEquals( "FDS", enr.getText()); + + try { + TestcaseRecordInputStream.confirmRecordEncoding(0x0023, dataFDS, enr.serialize()); + } catch (ArrayIndexOutOfBoundsException e) { + if(e.getMessage().equals("15")) { + throw new AssertionFailedError("Identified bug 44695"); + } + } + } + + public void testBasicSize() { + ExternalNameRecord enr = createSimpleENR(); + if(enr.getRecordSize() == 13) { + throw new AssertionFailedError("Identified bug 44695"); + } + assertEquals(17, enr.getRecordSize()); + } +} diff --git a/src/testcases/org/apache/poi/hssf/record/formula/AllFormulaTests.java b/src/testcases/org/apache/poi/hssf/record/formula/AllFormulaTests.java index 92ca4ba044..b50e95fa86 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/AllFormulaTests.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/AllFormulaTests.java @@ -17,6 +17,8 @@ package org.apache.poi.hssf.record.formula; +import org.apache.poi.hssf.record.formula.function.AllFormulaFunctionTests; + import junit.framework.Test; import junit.framework.TestSuite; @@ -28,12 +30,12 @@ import junit.framework.TestSuite; public class AllFormulaTests { public static Test suite() { - TestSuite result = new TestSuite("Tests for org.apache.poi.hssf.record.formula"); + TestSuite result = new TestSuite(AllFormulaTests.class.getName()); result.addTestSuite(TestArea3DPtg.class); result.addTestSuite(TestAreaErrPtg.class); - result.addTestSuite(TestAreaPtg.class); - result.addTestSuite(TestErrPtg.class); - result.addTestSuite(TestExternalFunctionFormulas.class); + result.addTestSuite(TestAreaPtg.class); + result.addTestSuite(TestErrPtg.class); + result.addTestSuite(TestExternalFunctionFormulas.class); result.addTestSuite(TestFuncPtg.class); result.addTestSuite(TestIntersectionPtg.class); result.addTestSuite(TestPercentPtg.class); @@ -42,6 +44,7 @@ public class AllFormulaTests { result.addTestSuite(TestReferencePtg.class); result.addTestSuite(TestSheetNameFormatter.class); result.addTestSuite(TestUnionPtg.class); + result.addTest(AllFormulaFunctionTests.suite()); return result; } } diff --git a/src/testcases/org/apache/poi/hssf/record/formula/function/AllFormulaFunctionTests.java b/src/testcases/org/apache/poi/hssf/record/formula/function/AllFormulaFunctionTests.java new file mode 100644 index 0000000000..8a59095e46 --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/record/formula/function/AllFormulaFunctionTests.java @@ -0,0 +1,37 @@ +/* ==================================================================== + 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.function; + +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * Collects all tests for this package. + * + * @author Josh Micich + */ +public class AllFormulaFunctionTests { + + public static Test suite() { + TestSuite result = new TestSuite(AllFormulaFunctionTests.class.getName()); + result.addTestSuite(TestFunctionMetadataRegistry.class); + result.addTestSuite(TestParseMissingBuiltInFuncs.class); + result.addTestSuite(TestReadMissingBuiltInFuncs.class); + return result; + } +} diff --git a/src/testcases/org/apache/poi/hssf/record/formula/function/ExcelFileFormatDocFunctionExtractor.java b/src/testcases/org/apache/poi/hssf/record/formula/function/ExcelFileFormatDocFunctionExtractor.java new file mode 100644 index 0000000000..c7d74b6f75 --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/record/formula/function/ExcelFileFormatDocFunctionExtractor.java @@ -0,0 +1,503 @@ +/* ==================================================================== + 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.function; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Stack; +import java.util.zip.CRC32; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.InputSource; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.XMLReaderFactory; + +/** + * This class is not used during normal POI run-time but is used at development time to generate + * the file 'functionMetadata.txt'. There are more than 300 built-in functions in Excel and the + * intention of this class is to make it easier to maintain the metadata, by extracting it from + * a reliable source. + * + * @author Josh Micich + */ +public class ExcelFileFormatDocFunctionExtractor { + + private static final String SOURCE_DOC_FILE_NAME = "excelfileformat.odt"; + + private static final class FunctionData { + + private final int _index; + private final boolean _hasFootnote; + private final String _name; + private final int _minParams; + private final int _maxParams; + private final String _returnClass; + private final String _paramClasses; + private final boolean _isVolatile; + + public FunctionData(int funcIx, boolean hasFootnote, String funcName, int minParams, int maxParams, + String returnClass, String paramClasses, boolean isVolatile) { + _index = funcIx; + _hasFootnote = hasFootnote; + _name = funcName; + _minParams = minParams; + _maxParams = maxParams; + _returnClass = returnClass; + _paramClasses = paramClasses; + _isVolatile = isVolatile; + } + public int getIndex() { + return _index; + } + public String getName() { + return _name; + } + public boolean hasFootnote() { + return _hasFootnote; + } + public String formatAsDataLine() { + return _index + "\t" + _name + "\t" + _minParams + "\t" + + _maxParams + "\t" + _returnClass + "\t" + _paramClasses + + "\t" + checkMark(_isVolatile) + "\t" + checkMark(_hasFootnote); + } + private static String checkMark(boolean b) { + return b ? "x" : ""; + } + } + + private static final class FunctionDataCollector { + + + private final Map _allFunctionsByIndex; + private final Map _allFunctionsByName; + private final Set _groupFunctionIndexes; + private final Set _groupFunctionNames; + private final PrintStream _ps; + + public FunctionDataCollector(PrintStream ps) { + _ps = ps; + _allFunctionsByIndex = new HashMap(); + _allFunctionsByName = new HashMap(); + _groupFunctionIndexes = new HashSet(); + _groupFunctionNames = new HashSet(); + } + + public void addFuntion(int funcIx, boolean hasFootnote, String funcName, int minParams, int maxParams, + String returnClass, String paramClasses, String volatileFlagStr) { + boolean isVolatile = volatileFlagStr.length() > 0; + + Integer funcIxKey = new Integer(funcIx); + if(!_groupFunctionIndexes.add(funcIxKey)) { + throw new RuntimeException("Duplicate function index (" + funcIx + ")"); + } + if(!_groupFunctionNames.add(funcName)) { + throw new RuntimeException("Duplicate function name '" + funcName + "'"); + } + + checkRedefinedFunction(hasFootnote, funcName, funcIxKey); + FunctionData fd = new FunctionData(funcIx, hasFootnote, funcName, + minParams, maxParams, returnClass, paramClasses, isVolatile); + + _allFunctionsByIndex.put(funcIxKey, fd); + _allFunctionsByName.put(funcName, fd); + } + + private void checkRedefinedFunction(boolean hasNote, String funcName, Integer funcIxKey) { + FunctionData fdPrev; + fdPrev = (FunctionData) _allFunctionsByIndex.get(funcIxKey); + if(fdPrev != null) { + if(fdPrev.hasFootnote() && hasNote) { + // func def can change if both have a foot-note + _allFunctionsByName.remove(fdPrev.getName()); + } else { + throw new RuntimeException("changing function definition without foot-note"); + } + } + fdPrev = (FunctionData) _allFunctionsByName.get(funcName); + if(fdPrev != null) { + if(fdPrev.hasFootnote() && hasNote) { + // func def can change if both have a foot-note + _allFunctionsByIndex.remove(new Integer(fdPrev.getIndex())); + } else { + throw new RuntimeException("changing function definition without foot-note"); + } + } + } + + public void endTableGroup(String headingText) { + Integer[] keys = new Integer[_groupFunctionIndexes.size()]; + _groupFunctionIndexes.toArray(keys); + _groupFunctionIndexes.clear(); + _groupFunctionNames.clear(); + Arrays.sort(keys); + + _ps.println("# " + headingText); + for (int i = 0; i < keys.length; i++) { + FunctionData fd = (FunctionData) _allFunctionsByIndex.get(keys[i]); + _ps.println(fd.formatAsDataLine()); + } + } + } + + /** + * To avoid drag-in - parse XML using only JDK. + */ + private static class EFFDocHandler implements ContentHandler { + private static final String[] HEADING_PATH_NAMES = { + "office:document-content", "office:body", "office:text", "text:h", + }; + private static final String[] TABLE_BASE_PATH_NAMES = { + "office:document-content", "office:body", "office:text", "table:table", + }; + private static final String[] TABLE_ROW_RELPATH_NAMES = { + "table:table-row", + }; + private static final String[] TABLE_CELL_RELPATH_NAMES = { + "table:table-row", "table:table-cell", "text:p", + }; + private static final String[] NOTE_REF_RELPATH_NAMES = { + "table:table-row", "table:table-cell", "text:p", "text:span", "text:note-ref", + }; + + + private final Stack _elemNameStack; + /** true only when parsing the target tables */ + private boolean _isInsideTable; + + private final List _rowData; + private final StringBuffer _textNodeBuffer; + private final List _rowNoteFlags; + private boolean _cellHasNote; + + private final FunctionDataCollector _fdc; + private String _lastHeadingText; + + public EFFDocHandler(FunctionDataCollector fdc) { + _fdc = fdc; + _elemNameStack = new Stack(); + _isInsideTable = false; + _rowData = new ArrayList(); + _textNodeBuffer = new StringBuffer(); + _rowNoteFlags = new ArrayList(); + } + + private boolean matchesTargetPath() { + return matchesPath(0, TABLE_BASE_PATH_NAMES); + } + private boolean matchesRelPath(String[] pathNames) { + return matchesPath(TABLE_BASE_PATH_NAMES.length, pathNames); + } + private boolean matchesPath(int baseStackIndex, String[] pathNames) { + if(_elemNameStack.size() != baseStackIndex + pathNames.length) { + return false; + } + for (int i = 0; i < pathNames.length; i++) { + if(!_elemNameStack.get(baseStackIndex + i).equals(pathNames[i])) { + return false; + } + } + return true; + } + public void characters(char[] ch, int start, int length) { + // only 2 text nodes where text is collected: + if(matchesRelPath(TABLE_CELL_RELPATH_NAMES) || matchesPath(0, HEADING_PATH_NAMES)) { + _textNodeBuffer.append(ch, start, length); + } + } + + public void endElement(String namespaceURI, String localName, String name) { + String expectedName = (String) _elemNameStack.peek(); + if(expectedName != name) { + throw new RuntimeException("close tag mismatch"); + } + if(matchesPath(0, HEADING_PATH_NAMES)) { + _lastHeadingText = _textNodeBuffer.toString().trim(); + _textNodeBuffer.setLength(0); + } + if(_isInsideTable) { + if(matchesTargetPath()) { + _fdc.endTableGroup(_lastHeadingText); + _isInsideTable = false; + } else if(matchesRelPath(TABLE_ROW_RELPATH_NAMES)) { + String[] cellData = new String[_rowData.size()]; + _rowData.toArray(cellData); + _rowData.clear(); + Boolean[] noteFlags = new Boolean[_rowNoteFlags.size()]; + _rowNoteFlags.toArray(noteFlags); + _rowNoteFlags.clear(); + processTableRow(cellData, noteFlags); + } else if(matchesRelPath(TABLE_CELL_RELPATH_NAMES)) { + _rowData.add(_textNodeBuffer.toString().trim()); + _rowNoteFlags.add(Boolean.valueOf(_cellHasNote)); + _textNodeBuffer.setLength(0); + } + } + _elemNameStack.pop(); + } + + private void processTableRow(String[] cellData, Boolean[] noteFlags) { + // each table row of the document contains data for two functions + if(cellData.length != 15) { + throw new RuntimeException("Bad table row size"); + } + processFunction(cellData, noteFlags, 0); + processFunction(cellData, noteFlags, 8); + } + public void processFunction(String[] cellData, Boolean[] noteFlags, int i) { + String funcIxStr = cellData[i + 0]; + if (funcIxStr.length() < 1) { + // empty (happens on the right hand side when there is an odd number of functions) + return; + } + int funcIx = parseInt(funcIxStr); + + boolean hasFootnote = noteFlags[i + 1].booleanValue(); + String funcName = cellData[i + 1]; + int minParams = parseInt(cellData[i + 2]); + int maxParams = parseInt(cellData[i + 3]); + + String returnClass = cellData[i + 4]; + String paramClasses = cellData[i + 5]; + String volatileFlagStr = cellData[i + 6]; + + _fdc.addFuntion(funcIx, hasFootnote, funcName, minParams, maxParams, returnClass, paramClasses, volatileFlagStr); + } + private static int parseInt(String valStr) { + try { + return Integer.parseInt(valStr); + } catch (NumberFormatException e) { + throw new RuntimeException("Value '" + valStr + "' could not be parsed as an integer"); + } + } + public void startElement(String namespaceURI, String localName, String name, Attributes atts) { + _elemNameStack.add(name); + if(matchesTargetPath()) { + String tableName = atts.getValue("table:name"); + if(tableName.startsWith("tab_fml_func") && !tableName.equals("tab_fml_func0")) { + _isInsideTable = true; + } + return; + } + if(matchesPath(0, HEADING_PATH_NAMES)) { + _textNodeBuffer.setLength(0); + } else if(matchesRelPath(TABLE_ROW_RELPATH_NAMES)) { + _rowData.clear(); + _rowNoteFlags.clear(); + } else if(matchesRelPath(TABLE_CELL_RELPATH_NAMES)) { + _textNodeBuffer.setLength(0); + _cellHasNote = false; + } else if(matchesRelPath(NOTE_REF_RELPATH_NAMES)) { + _cellHasNote = true; + } + } + + public void endDocument() { + // do nothing + } + public void endPrefixMapping(String prefix) { + // do nothing + } + public void ignorableWhitespace(char[] ch, int start, int length) { + // do nothing + } + public void processingInstruction(String target, String data) { + // do nothing + } + public void setDocumentLocator(Locator locator) { + // do nothing + } + public void skippedEntity(String name) { + // do nothing + } + public void startDocument() { + // do nothing + } + public void startPrefixMapping(String prefix, String uri) { + // do nothing + } + } + + private static void extractFunctionData(FunctionDataCollector fdc, InputStream is) { + System.setProperty("org.xml.sax.driver", "org.apache.crimson.parser.XMLReaderImpl"); + + XMLReader xr; + try { + xr = XMLReaderFactory.createXMLReader(); + } catch (SAXException e) { + throw new RuntimeException(e); + } + xr.setContentHandler(new EFFDocHandler(fdc)); + + InputSource inSrc = new InputSource(is); + + try { + xr.parse(inSrc); + is.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (SAXException e) { + throw new RuntimeException(e); + } + } + + private static void processFile(File effDocFile, File outFile) { + OutputStream os; + try { + os = new FileOutputStream(outFile); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + PrintStream ps = new PrintStream(os); + outputLicenseHeader(ps); + Class genClass = ExcelFileFormatDocFunctionExtractor.class; + ps.println("# Created by (" + genClass.getName() + ")"); + // identify the source file + ps.print("# from source file '" + SOURCE_DOC_FILE_NAME + "'"); + ps.println(" (size=" + effDocFile.length() + ", crc=" + getFileCRC(effDocFile) + ")"); + ps.println("#"); + ps.println("#Columns: (index, name, minParams, maxParams, returnClass, paramClasses, isVolatile, hasFootnote )"); + ps.println(""); + try { + ZipFile zf = new ZipFile(effDocFile); + InputStream is = zf.getInputStream(zf.getEntry("content.xml")); + extractFunctionData(new FunctionDataCollector(ps), is); + zf.close(); + } catch (ZipException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + ps.close(); + } + + private static void outputLicenseHeader(PrintStream ps) { + String[] lines= { + "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.", + }; + for (int i = 0; i < lines.length; i++) { + ps.print("# "); + ps.println(lines[i]); + } + ps.println(); + } + + /** + * Helps identify the source file + */ + private static String getFileCRC(File f) { + CRC32 crc = new CRC32(); + byte[]buf = new byte[2048]; + try { + InputStream is = new FileInputStream(f); + while(true) { + int bytesRead = is.read(buf); + if(bytesRead<1) { + break; + } + crc.update(buf, 0, bytesRead); + } + is.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return "0x" + Long.toHexString(crc.getValue()).toUpperCase(); + } + + private static File getSourceFile() { + if (true) { + File dir = new File("c:/josh/ref-docs"); + File effDocFile = new File(dir, SOURCE_DOC_FILE_NAME); + return effDocFile; + } + URL url; + try { + url = new URL("http://sc.openoffice.org/" + SOURCE_DOC_FILE_NAME); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + + File result; + byte[]buf = new byte[2048]; + try { + URLConnection conn = url.openConnection(); + InputStream is = conn.getInputStream(); + System.out.println("downloading " + url.toExternalForm()); + result = File.createTempFile("excelfileformat", "odt"); + OutputStream os = new FileOutputStream(result); + while(true) { + int bytesRead = is.read(buf); + if(bytesRead<1) { + break; + } + os.write(buf, 0, bytesRead); + } + is.close(); + os.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + System.out.println("file downloaded ok"); + return result; + } + + public static void main(String[] args) { + + File effDocFile = getSourceFile(); + if(!effDocFile.exists()) { + throw new RuntimeException("file '" + effDocFile.getAbsolutePath() + "' does not exist"); + } + + File outFile = new File("functionMetadata-asGenerated.txt"); + processFile(effDocFile, outFile); + } + +} diff --git a/src/testcases/org/apache/poi/hssf/record/formula/function/TestFunctionMetadataRegistry.java b/src/testcases/org/apache/poi/hssf/record/formula/function/TestFunctionMetadataRegistry.java new file mode 100644 index 0000000000..edb215ec50 --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/record/formula/function/TestFunctionMetadataRegistry.java @@ -0,0 +1,43 @@ +/* ==================================================================== + 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.function; + +import junit.framework.TestCase; +/** + * + * @author Josh Micich + */ +public final class TestFunctionMetadataRegistry extends TestCase { + + public void testWellKnownFunctions() { + confirmFunction(0, "COUNT"); + confirmFunction(1, "IF"); + + } + + private static void confirmFunction(int index, String funcName) { + FunctionMetadata fm; + fm = FunctionMetadataRegistry.getFunctionByIndex(index); + assertNotNull(fm); + assertEquals(funcName, fm.getName()); + + fm = FunctionMetadataRegistry.getFunctionByName(funcName); + assertNotNull(fm); + assertEquals(index, fm.getIndex()); + } +} diff --git a/src/testcases/org/apache/poi/hssf/record/formula/function/TestParseMissingBuiltInFuncs.java b/src/testcases/org/apache/poi/hssf/record/formula/function/TestParseMissingBuiltInFuncs.java new file mode 100644 index 0000000000..fe1b8fccc8 --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/record/formula/function/TestParseMissingBuiltInFuncs.java @@ -0,0 +1,79 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hssf.record.formula.function; + +import junit.framework.AssertionFailedError; +import junit.framework.TestCase; + +import org.apache.poi.hssf.model.FormulaParser; +import org.apache.poi.hssf.model.Workbook; +import org.apache.poi.hssf.record.formula.AbstractFunctionPtg; +import org.apache.poi.hssf.record.formula.FuncPtg; +import org.apache.poi.hssf.record.formula.FuncVarPtg; +import org.apache.poi.hssf.record.formula.Ptg; +/** + * @author Josh Micich + */ +public final class TestParseMissingBuiltInFuncs extends TestCase { + + private static Ptg[] parse(String formula) { + Workbook book = Workbook.createWorkbook(); + return FormulaParser.parse(formula, book); + } + private static void confirmFunc(String formula, int expPtgArraySize, boolean isVarArgFunc, int funcIx) { + Ptg[] ptgs = parse(formula); + Ptg ptgF = ptgs[ptgs.length-1]; // func is last RPN token in all these formulas + + if(!(ptgF instanceof AbstractFunctionPtg)) { + throw new RuntimeException("function token missing"); + } + AbstractFunctionPtg func = (AbstractFunctionPtg) ptgF; + if(func.getFunctionIndex() == 255) { + throw new AssertionFailedError("Failed to recognise built-in function in formula '" + + formula + "'"); + } + + assertEquals(expPtgArraySize, ptgs.length); + assertEquals(funcIx, func.getFunctionIndex()); + Class expCls = isVarArgFunc ? FuncVarPtg.class : FuncPtg.class; + assertEquals(expCls, ptgF.getClass()); + } + + public void testDatedif() { + int expSize = 4; // NB would be 5 if POI added tAttrVolatile properly + confirmFunc("DATEDIF(NOW(),NOW(),\"d\")", expSize, false, 351); + } + + public void testDdb() { + confirmFunc("DDB(1,1,1,1,1)", 6, true, 144); + } + public void testAtan() { + confirmFunc("ATAN(1)", 2, false, 18); + } + + public void testUsdollar() { + confirmFunc("USDOLLAR(1)", 2, false, 204); + } + + public void testDBCS() { + confirmFunc("DBCS(\"abc\")", 2, false, 215); + } + public void testIsnontext() { + confirmFunc("ISNONTEXT(\"abc\")", 2, false, 190); + } +} diff --git a/src/testcases/org/apache/poi/hssf/record/formula/function/TestReadMissingBuiltInFuncs.java b/src/testcases/org/apache/poi/hssf/record/formula/function/TestReadMissingBuiltInFuncs.java new file mode 100644 index 0000000000..7e1d0317ec --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/record/formula/function/TestReadMissingBuiltInFuncs.java @@ -0,0 +1,142 @@ +/* ==================================================================== + 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.function; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.poi.hssf.usermodel.HSSFSheet; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; + +import junit.framework.AssertionFailedError; +import junit.framework.TestCase; +/** + * Tests reading from a sample spreadsheet some built-in functions that were not properly + * registered in POI as bug #44675 (March 2008). + * + * @author Josh Micich + */ +public final class TestReadMissingBuiltInFuncs extends TestCase { + + private HSSFSheet sht; + + protected void setUp() { + String cwd = System.getProperty("HSSF.testdata.path"); + HSSFWorkbook wb; + try { + InputStream is = new FileInputStream(new File(cwd, "missingFuncs44675.xls")); + wb = new HSSFWorkbook(is); + } catch (IOException e) { + throw new RuntimeException(e); + } + sht = wb.getSheetAt(0); + } + + public void testDatedif() { + + String formula; + try { + formula = getCellFormula(0); + } catch (IllegalStateException e) { + if(e.getMessage().startsWith("Too few arguments")) { + if(e.getMessage().indexOf("AttrPtg") > 0) { + throw afe("tAttrVolatile not supported in FormulaParser.toFormulaString"); + } + throw afe("NOW() registered with 1 arg instead of 0"); + } + if(e.getMessage().startsWith("too much stuff")) { + throw afe("DATEDIF() not registered"); + } + // some other unexpected error + throw e; + } + assertEquals("DATEDIF(NOW(),NOW(),\"d\")", formula); + } + public void testDdb() { + + String formula = getCellFormula(1); + if("externalflag(1,1,1,1,1)".equals(formula)) { + throw afe("DDB() not registered"); + } + assertEquals("DDB(1,1,1,1,1)", formula); + } + public void testAtan() { + + String formula = getCellFormula(2); + if(formula.equals("ARCTAN(1)")) { + throw afe("func ix 18 registered as ARCTAN() instead of ATAN()"); + } + assertEquals("ATAN(1)", formula); + } + + public void testUsdollar() { + + String formula = getCellFormula(3); + if(formula.equals("YEN(1)")) { + throw afe("func ix 204 registered as YEN() instead of USDOLLAR()"); + } + assertEquals("USDOLLAR(1)", formula); + } + + public void testDBCS() { + + String formula; + try { + formula = getCellFormula(4); + } catch (IllegalStateException e) { + if(e.getMessage().startsWith("too much stuff")) { + throw afe("DBCS() not registered"); + } + // some other unexpected error + throw e; + } catch (NegativeArraySizeException e) { + throw afe("found err- DBCS() registered with -1 args"); + } + if(formula.equals("JIS(\"abc\")")) { + throw afe("func ix 215 registered as JIS() instead of DBCS()"); + } + assertEquals("DBCS(\"abc\")", formula); + } + public void testIsnontext() { + + String formula; + try { + formula = getCellFormula(5); + } catch (IllegalStateException e) { + if(e.getMessage().startsWith("too much stuff")) { + throw afe("ISNONTEXT() registered with wrong index"); + } + // some other unexpected error + throw e; + } + assertEquals("ISNONTEXT(\"abc\")", formula); + } + + private String getCellFormula(int rowIx) { + String result = sht.getRow(rowIx).getCell((short)0).getCellFormula(); + if (false) { + System.err.println(result); + } + return result; + } + private static AssertionFailedError afe(String msg) { + return new AssertionFailedError(msg); + } +} diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java index 05ba29d09e..94c19cbc04 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java @@ -1218,6 +1218,30 @@ extends TestCase { assertEquals(1, wb.getNumberOfSheets()); } + + /** + * User reported the wrong number of rows from the + * iterator, but we can't replicate that + */ + public void test44693() throws Exception { + FileInputStream in = new FileInputStream(new File(cwd, "44693.xls")); + + HSSFWorkbook wb = new HSSFWorkbook(in); + HSSFSheet s = wb.getSheetAt(0); + + // Rows are 1 to 713 + assertEquals(0, s.getFirstRowNum()); + assertEquals(712, s.getLastRowNum()); + assertEquals(713, s.getPhysicalNumberOfRows()); + + // Now check the iterator + int rowsSeen = 0; + for(Iterator i = s.rowIterator(); i.hasNext(); ) { + HSSFRow r = (HSSFRow)i.next(); + rowsSeen++; + } + assertEquals(713, rowsSeen); + } }