Merged revisions 638786-638802,638805-638811,638813-638814,638816-639230,639233-639241,639243-639253,639255-639486,639488-639601,639603-639835,639837-639917,639919-640056,640058-640710,640712-641156,641158-641184,641186-641795,641797-641798,641800-641933,641935-641963,641965-641966,641968-641995,641997-642230,642232-642562,642564-642565,642568-642570,642572-642573,642576-642736,642739-642877,642879,642881-642890,642892-642903,642905-642945,642947-643624,643626-643653,643655-643669,643671,643673-643830,643832-643833,643835-644342,644344-644472,644474-644508,644510-645347,645349-645351,645353-645559,645561-645565,645568-645951,645953-646193,646195-646311,646313-646404,646406-646665,646667-646853,646855-646869,646871-647151,647153-647185,647187-647277,647279-647566,647568-647573,647575,647578-647711,647714-647737,647739-647823,647825-648155,648157-648202,648204-648273,648275,648277-648302,648304-648333,648335-648588,648590-648622,648625-648673,648675-649141,649144,649146-649556,649558-649795,649799,649801-649910,649912-649913,649915-650128,650131-650132,650134-650137,650140-650914,650916-651991,651993-652284,652286-652287,652289,652291,652293-652297,652299-652328,652330-652425,652427-652445,652447-652560,652562-652933,652935,652937-652993,652995-653116,653118-653124,653126-653483,653487-653519,653522-653550,653552-653607,653609-653667,653669-653674,653676-653814,653817-653830,653832-653891,653893-653944,653946-654055,654057-654355,654357-654365,654367-654648,654651-655215,655217-655277,655279-655281,655283-655911,655913-656212,656214,656216-656251,656253-656698,656700-656756,656758-656892,656894-657135,657137-657165,657168-657179,657181-657354,657356-657357,657359-657701,657703-657874,657876-658032,658034-658284,658286,658288-658301,658303-658307,658309-658321,658323-658335,658337-658348,658351,658353-658832,658834-658983,658985,658987-659066,659068-659402,659404-659428,659430-659451,659453-659454,659456-659461,659463-659477,659479-659524,659526-659571,659574-660890 via svnmerge from
https://svn.apache.org/repos/asf/poi/trunk ........ r659575 | nick | 2008-05-23 16:55:08 +0100 (Fri, 23 May 2008) | 1 line Help for bug #44840 - Improved handling of HSSFObjectData, especially for entries with data held not in POIFS ........ r660256 | josh | 2008-05-26 19:02:23 +0100 (Mon, 26 May 2008) | 1 line Follow-on fix for bug 42564 (r653668). Array elements are stored internally column by column. ........ r660263 | josh | 2008-05-26 19:25:02 +0100 (Mon, 26 May 2008) | 1 line Small fix for FormulaParser. Need case-insentive match for IF function name ........ r660280 | josh | 2008-05-26 20:36:56 +0100 (Mon, 26 May 2008) | 1 line Added test cases for parsing IF expressions. Segregated IF test cases into a new class ........ r660344 | josh | 2008-05-27 01:57:23 +0100 (Tue, 27 May 2008) | 1 line Changed class hierarchy of Ptg to improve 'operand class' transformation. ........ r660474 | nick | 2008-05-27 12:44:49 +0100 (Tue, 27 May 2008) | 1 line X, Y, Width and Height getters/setters on HSSFChart ........ r660828 | josh | 2008-05-28 07:19:31 +0100 (Wed, 28 May 2008) | 1 line Fix for 45060 (and 45041) - Improved token class transformation during formula parsing ........ r660834 | yegor | 2008-05-28 07:50:35 +0100 (Wed, 28 May 2008) | 1 line bump 3.1-beta2 announcement ........ r660889 | nick | 2008-05-28 11:03:00 +0100 (Wed, 28 May 2008) | 1 line Fix bug #45087 - Correctly detect date formats like [Black]YYYY as being date based ........ git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@660905 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
fb7f5240fe
commit
804cc620e7
@ -46,6 +46,9 @@
|
||||
<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
|
||||
</release>
|
||||
<release version="3.1-final" date="2008-06-??">
|
||||
<action dev="POI-DEVELOPERS" type="fix">45087 - Correctly detect date formats like [Black]YYYY as being date based</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">45060 - Improved token class transformation during formula parsing</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">44840 - Improved handling of HSSFObjectData, especially for entries with data held not in POIFS</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">45043 - Support for getting excel cell comments when extracting text</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">Extend the support for specifying a policy to HSSF on missing / blank cells when fetching, to be able to specify the policy at the HSSFWorkbook level</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">45025 - improved FormulaParser parse error messages</action>
|
||||
|
@ -40,9 +40,9 @@
|
||||
People interested should follow the
|
||||
<link href="mailinglists.html">dev list</link> to track progress.</p>
|
||||
</section>
|
||||
<section><title>POI 3.1-BETA1 Released (2008-04028)</title>
|
||||
<section><title>POI 3.1-BETA2 Released (2008-05-28)</title>
|
||||
<p>
|
||||
The POI team is pleased to announce the release of 3.1 BETA1 which is one of the final steps before 3.1 FINAL.
|
||||
The POI team is pleased to announce the release of 3.1 BETA2 which is one of the final steps before 3.1 FINAL.
|
||||
The status of this release is a beta, meaning that we encourage users to try it out.
|
||||
If you find any bugs, please report them to the POI <link href="https://issues.apache.org/bugzilla/buglist.cgi?product=POI">bug database</link> or to
|
||||
the <link href="./mailinglists.html">POI Developer List</link>.
|
||||
@ -54,7 +54,7 @@
|
||||
</p>
|
||||
<p>
|
||||
The release is also available from the central Maven repository
|
||||
under Group ID "org.apache.poi" and Version "3.1-beta1".
|
||||
under Group ID "org.apache.poi" and Version "3.1-beta2".
|
||||
</p>
|
||||
</section>
|
||||
<section><title>POI 3.0.2 Released</title>
|
||||
|
@ -43,6 +43,9 @@
|
||||
<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
|
||||
</release>
|
||||
<release version="3.1-final" date="2008-06-??">
|
||||
<action dev="POI-DEVELOPERS" type="fix">45087 - Correctly detect date formats like [Black]YYYY as being date based</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">45060 - Improved token class transformation during formula parsing</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">44840 - Improved handling of HSSFObjectData, especially for entries with data held not in POIFS</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">45043 - Support for getting excel cell comments when extracting text</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">Extend the support for specifying a policy to HSSF on missing / blank cells when fetching, to be able to specify the policy at the HSSFWorkbook level</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">45025 - improved FormulaParser parse error messages</action>
|
||||
|
@ -18,7 +18,6 @@
|
||||
package org.apache.poi.hssf.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
import java.util.regex.Pattern;
|
||||
@ -61,17 +60,17 @@ public final class FormulaParser {
|
||||
}
|
||||
}
|
||||
|
||||
public static int FORMULA_TYPE_CELL = 0;
|
||||
public static int FORMULA_TYPE_SHARED = 1;
|
||||
public static int FORMULA_TYPE_ARRAY =2;
|
||||
public static int FORMULA_TYPE_CONDFOMRAT = 3;
|
||||
public static int FORMULA_TYPE_NAMEDRANGE = 4;
|
||||
public static final int FORMULA_TYPE_CELL = 0;
|
||||
public static final int FORMULA_TYPE_SHARED = 1;
|
||||
public static final int FORMULA_TYPE_ARRAY =2;
|
||||
public static final int FORMULA_TYPE_CONDFOMRAT = 3;
|
||||
public static final int FORMULA_TYPE_NAMEDRANGE = 4;
|
||||
|
||||
private final String formulaString;
|
||||
private final int formulaLength;
|
||||
private int pointer;
|
||||
|
||||
private final List tokens = new Stack();
|
||||
private ParseNode _rootNode;
|
||||
|
||||
/**
|
||||
* Used for spotting if we have a cell reference,
|
||||
@ -221,14 +220,15 @@ public final class FormulaParser {
|
||||
return value.length() == 0 ? null : value.toString();
|
||||
}
|
||||
|
||||
/** Parse and Translate a String Identifier */
|
||||
private Ptg parseIdent() {
|
||||
String name;
|
||||
name = GetName();
|
||||
private ParseNode parseFunctionOrIdentifier() {
|
||||
String name = GetName();
|
||||
if (look == '('){
|
||||
//This is a function
|
||||
return function(name);
|
||||
}
|
||||
return new ParseNode(parseIdentifier(name));
|
||||
}
|
||||
private Ptg parseIdentifier(String name) {
|
||||
|
||||
if (look == ':' || look == '.') { // this is a AreaReference
|
||||
GetChar();
|
||||
@ -287,14 +287,6 @@ public final class FormulaParser {
|
||||
+ name + "\", but that named range wasn't defined!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a pointer to the last token to the latest function argument list.
|
||||
* @param obj
|
||||
*/
|
||||
private void addArgumentPointer(List argumentPointers) {
|
||||
argumentPointers.add(tokens.get(tokens.size()-1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Note - Excel function names are 'case aware but not case sensitive'. This method may end
|
||||
* up creating a defined name record in the workbook if the specified name is not an internal
|
||||
@ -302,58 +294,23 @@ public final class FormulaParser {
|
||||
*
|
||||
* @param name case preserved function name (as it was entered/appeared in the formula).
|
||||
*/
|
||||
private Ptg function(String name) {
|
||||
int numArgs =0 ;
|
||||
private ParseNode function(String name) {
|
||||
NamePtg nameToken = null;
|
||||
// Note regarding parameter -
|
||||
if(!AbstractFunctionPtg.isInternalFunctionName(name)) {
|
||||
// external functions get a Name token which points to a defined name record
|
||||
NamePtg nameToken = new NamePtg(name, this.book);
|
||||
nameToken = new NamePtg(name, this.book);
|
||||
|
||||
// in the token tree, the name is more or less the first argument
|
||||
numArgs++;
|
||||
tokens.add(nameToken);
|
||||
}
|
||||
//average 2 args per function
|
||||
List argumentPointers = new ArrayList(2);
|
||||
|
||||
Match('(');
|
||||
numArgs += Arguments(argumentPointers);
|
||||
ParseNode[] args = Arguments();
|
||||
Match(')');
|
||||
|
||||
return getFunction(name, numArgs, argumentPointers);
|
||||
return getFunction(name, nameToken, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the size of all the ptgs after the provided index (inclusive).
|
||||
* <p>
|
||||
* Initially used to count a goto
|
||||
* @param index
|
||||
* @return int
|
||||
*/
|
||||
private int getPtgSize(int index) {
|
||||
int count = 0;
|
||||
|
||||
Iterator ptgIterator = tokens.listIterator(index);
|
||||
while (ptgIterator.hasNext()) {
|
||||
Ptg ptg = (Ptg)ptgIterator.next();
|
||||
count+=ptg.getSize();
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
private int getPtgSize(int start, int end) {
|
||||
int count = 0;
|
||||
int index = start;
|
||||
Iterator ptgIterator = tokens.listIterator(index);
|
||||
while (ptgIterator.hasNext() && index <= end) {
|
||||
Ptg ptg = (Ptg)ptgIterator.next();
|
||||
count+=ptg.getSize();
|
||||
index++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
/**
|
||||
* Generates the variable function ptg for the formula.
|
||||
* <p>
|
||||
@ -362,84 +319,35 @@ public final class FormulaParser {
|
||||
* @param numArgs
|
||||
* @return Ptg a null is returned if we're in an IF formula, it needs extreme manipulation and is handled in this function
|
||||
*/
|
||||
private AbstractFunctionPtg getFunction(String name, int numArgs, List argumentPointers) {
|
||||
private ParseNode getFunction(String name, NamePtg namePtg, ParseNode[] args) {
|
||||
|
||||
boolean isVarArgs;
|
||||
int funcIx;
|
||||
FunctionMetadata fm = FunctionMetadataRegistry.getFunctionByName(name.toUpperCase());
|
||||
int numArgs = args.length;
|
||||
if(fm == null) {
|
||||
if (namePtg == null) {
|
||||
throw new IllegalStateException("NamePtg must be supplied for external functions");
|
||||
}
|
||||
// must be external function
|
||||
isVarArgs = true;
|
||||
funcIx = FunctionMetadataRegistry.FUNCTION_INDEX_EXTERNAL;
|
||||
} else {
|
||||
isVarArgs = !fm.hasFixedArgsLength();
|
||||
funcIx = fm.getIndex();
|
||||
validateNumArgs(numArgs, fm);
|
||||
ParseNode[] allArgs = new ParseNode[numArgs+1];
|
||||
allArgs[0] = new ParseNode(namePtg);
|
||||
System.arraycopy(args, 0, allArgs, 1, numArgs);
|
||||
return new ParseNode(new FuncVarPtg(name, (byte)(numArgs+1)), allArgs);
|
||||
}
|
||||
|
||||
if (namePtg != null) {
|
||||
throw new IllegalStateException("NamePtg no applicable to internal functions");
|
||||
}
|
||||
boolean isVarArgs = !fm.hasFixedArgsLength();
|
||||
int funcIx = fm.getIndex();
|
||||
validateNumArgs(args.length, fm);
|
||||
|
||||
AbstractFunctionPtg retval;
|
||||
if(isVarArgs) {
|
||||
retval = new FuncVarPtg(name, (byte)numArgs);
|
||||
} else {
|
||||
retval = new FuncPtg(funcIx);
|
||||
}
|
||||
if (!name.equals(AbstractFunctionPtg.FUNCTION_NAME_IF)) {
|
||||
// early return for everything else besides IF()
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
AttrPtg ifPtg = new AttrPtg();
|
||||
ifPtg.setData((short)7); //mirroring excel output
|
||||
ifPtg.setOptimizedIf(true);
|
||||
|
||||
if (argumentPointers.size() != 2 && argumentPointers.size() != 3) {
|
||||
throw new IllegalArgumentException("["+argumentPointers.size()+"] Arguments Found - An IF formula requires 2 or 3 arguments. IF(CONDITION, TRUE_VALUE, FALSE_VALUE [OPTIONAL]");
|
||||
}
|
||||
|
||||
//Biffview of an IF formula record indicates the attr ptg goes after the condition ptgs and are
|
||||
//tracked in the argument pointers
|
||||
//The beginning first argument pointer is the last ptg of the condition
|
||||
int ifIndex = tokens.indexOf(argumentPointers.get(0))+1;
|
||||
tokens.add(ifIndex, ifPtg);
|
||||
|
||||
//we now need a goto ptgAttr to skip to the end of the formula after a true condition
|
||||
//the true condition is should be inserted after the last ptg in the first argument
|
||||
|
||||
int gotoIndex = tokens.indexOf(argumentPointers.get(1))+1;
|
||||
|
||||
AttrPtg goto1Ptg = new AttrPtg();
|
||||
goto1Ptg.setGoto(true);
|
||||
|
||||
|
||||
tokens.add(gotoIndex, goto1Ptg);
|
||||
|
||||
|
||||
if (numArgs > 2) { //only add false jump if there is a false condition
|
||||
|
||||
//second goto to skip past the function ptg
|
||||
AttrPtg goto2Ptg = new AttrPtg();
|
||||
goto2Ptg.setGoto(true);
|
||||
goto2Ptg.setData((short)(retval.getSize()-1));
|
||||
//Page 472 of the Microsoft Excel Developer's kit states that:
|
||||
//The b(or w) field specifies the number byes (or words to skip, minus 1
|
||||
|
||||
tokens.add(goto2Ptg); //this goes after all the arguments are defined
|
||||
}
|
||||
|
||||
//data portion of the if ptg points to the false subexpression (Page 472 of MS Excel Developer's kit)
|
||||
//count the number of bytes after the ifPtg to the False Subexpression
|
||||
//doesn't specify -1 in the documentation
|
||||
ifPtg.setData((short)(getPtgSize(ifIndex+1, gotoIndex)));
|
||||
|
||||
//count all the additional (goto) ptgs but dont count itself
|
||||
int ptgCount = this.getPtgSize(gotoIndex)-goto1Ptg.getSize()+retval.getSize();
|
||||
if (ptgCount > Short.MAX_VALUE) {
|
||||
throw new RuntimeException("Ptg Size exceeds short when being specified for a goto ptg in an if");
|
||||
}
|
||||
|
||||
goto1Ptg.setData((short)(ptgCount-1));
|
||||
|
||||
return retval;
|
||||
return new ParseNode(retval, args);
|
||||
}
|
||||
|
||||
private void validateNumArgs(int numArgs, FunctionMetadata fm) {
|
||||
@ -470,10 +378,12 @@ public final class FormulaParser {
|
||||
}
|
||||
|
||||
/** get arguments to a function */
|
||||
private int Arguments(List argumentPointers) {
|
||||
private ParseNode[] Arguments() {
|
||||
//average 2 args per function
|
||||
List temp = new ArrayList(2);
|
||||
SkipWhite();
|
||||
if(look == ')') {
|
||||
return 0;
|
||||
return ParseNode.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
boolean missedPrevArg = true;
|
||||
@ -482,8 +392,7 @@ public final class FormulaParser {
|
||||
SkipWhite();
|
||||
if (isArgumentDelimiter(look)) {
|
||||
if (missedPrevArg) {
|
||||
tokens.add(new MissingArgPtg());
|
||||
addArgumentPointer(argumentPointers);
|
||||
temp.add(new ParseNode(new MissingArgPtg()));
|
||||
numArgs++;
|
||||
}
|
||||
if (look == ')') {
|
||||
@ -493,8 +402,7 @@ public final class FormulaParser {
|
||||
missedPrevArg = true;
|
||||
continue;
|
||||
}
|
||||
comparisonExpression();
|
||||
addArgumentPointer(argumentPointers);
|
||||
temp.add(comparisonExpression());
|
||||
numArgs++;
|
||||
missedPrevArg = false;
|
||||
SkipWhite();
|
||||
@ -502,32 +410,34 @@ public final class FormulaParser {
|
||||
throw expected("',' or ')'");
|
||||
}
|
||||
}
|
||||
return numArgs;
|
||||
ParseNode[] result = new ParseNode[temp.size()];
|
||||
temp.toArray(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Parse and Translate a Math Factor */
|
||||
private void powerFactor() {
|
||||
percentFactor();
|
||||
private ParseNode powerFactor() {
|
||||
ParseNode result = percentFactor();
|
||||
while(true) {
|
||||
SkipWhite();
|
||||
if(look != '^') {
|
||||
return;
|
||||
return result;
|
||||
}
|
||||
Match('^');
|
||||
percentFactor();
|
||||
tokens.add(new PowerPtg());
|
||||
ParseNode other = percentFactor();
|
||||
result = new ParseNode(new PowerPtg(), result, other);
|
||||
}
|
||||
}
|
||||
|
||||
private void percentFactor() {
|
||||
tokens.add(parseSimpleFactor());
|
||||
private ParseNode percentFactor() {
|
||||
ParseNode result = parseSimpleFactor();
|
||||
while(true) {
|
||||
SkipWhite();
|
||||
if(look != '%') {
|
||||
return;
|
||||
return result;
|
||||
}
|
||||
Match('%');
|
||||
tokens.add(new PercentPtg());
|
||||
result = new ParseNode(new PercentPtg(), result);
|
||||
}
|
||||
}
|
||||
|
||||
@ -535,32 +445,30 @@ public final class FormulaParser {
|
||||
/**
|
||||
* factors (without ^ or % )
|
||||
*/
|
||||
private Ptg parseSimpleFactor() {
|
||||
private ParseNode parseSimpleFactor() {
|
||||
SkipWhite();
|
||||
switch(look) {
|
||||
case '#':
|
||||
return parseErrorLiteral();
|
||||
return new ParseNode(parseErrorLiteral());
|
||||
case '-':
|
||||
Match('-');
|
||||
powerFactor();
|
||||
return new UnaryMinusPtg();
|
||||
return new ParseNode(new UnaryMinusPtg(), powerFactor());
|
||||
case '+':
|
||||
Match('+');
|
||||
powerFactor();
|
||||
return new UnaryPlusPtg();
|
||||
return new ParseNode(new UnaryPlusPtg(), powerFactor());
|
||||
case '(':
|
||||
Match('(');
|
||||
comparisonExpression();
|
||||
ParseNode inside = comparisonExpression();
|
||||
Match(')');
|
||||
return new ParenthesisPtg();
|
||||
return new ParseNode(new ParenthesisPtg(), inside);
|
||||
case '"':
|
||||
return parseStringLiteral();
|
||||
return new ParseNode(parseStringLiteral());
|
||||
}
|
||||
if (IsAlpha(look) || look == '\''){
|
||||
return parseIdent();
|
||||
return parseFunctionOrIdentifier();
|
||||
}
|
||||
// else - assume number
|
||||
return parseNumber();
|
||||
return new ParseNode(parseNumber());
|
||||
}
|
||||
|
||||
|
||||
@ -716,28 +624,30 @@ public final class FormulaParser {
|
||||
}
|
||||
|
||||
/** Parse and Translate a Math Term */
|
||||
private void Term() {
|
||||
powerFactor();
|
||||
private ParseNode Term() {
|
||||
ParseNode result = powerFactor();
|
||||
while(true) {
|
||||
SkipWhite();
|
||||
Ptg operator;
|
||||
switch(look) {
|
||||
case '*':
|
||||
Match('*');
|
||||
powerFactor();
|
||||
tokens.add(new MultiplyPtg());
|
||||
continue;
|
||||
operator = new MultiplyPtg();
|
||||
break;
|
||||
case '/':
|
||||
Match('/');
|
||||
powerFactor();
|
||||
tokens.add(new DividePtg());
|
||||
continue;
|
||||
operator = new DividePtg();
|
||||
break;
|
||||
default:
|
||||
return result; // finished with Term
|
||||
}
|
||||
return; // finished with Term
|
||||
ParseNode other = powerFactor();
|
||||
result = new ParseNode(operator, result, other);
|
||||
}
|
||||
}
|
||||
|
||||
private void comparisonExpression() {
|
||||
concatExpression();
|
||||
private ParseNode comparisonExpression() {
|
||||
ParseNode result = concatExpression();
|
||||
while (true) {
|
||||
SkipWhite();
|
||||
switch(look) {
|
||||
@ -745,11 +655,11 @@ public final class FormulaParser {
|
||||
case '>':
|
||||
case '<':
|
||||
Ptg comparisonToken = getComparisonToken();
|
||||
concatExpression();
|
||||
tokens.add(comparisonToken);
|
||||
ParseNode other = concatExpression();
|
||||
result = new ParseNode(comparisonToken, result, other);
|
||||
continue;
|
||||
}
|
||||
return; // finished with predicate expression
|
||||
return result; // finished with predicate expression
|
||||
}
|
||||
}
|
||||
|
||||
@ -779,38 +689,41 @@ public final class FormulaParser {
|
||||
}
|
||||
|
||||
|
||||
private void concatExpression() {
|
||||
additiveExpression();
|
||||
private ParseNode concatExpression() {
|
||||
ParseNode result = additiveExpression();
|
||||
while (true) {
|
||||
SkipWhite();
|
||||
if(look != '&') {
|
||||
break; // finished with concat expression
|
||||
}
|
||||
Match('&');
|
||||
additiveExpression();
|
||||
tokens.add(new ConcatPtg());
|
||||
ParseNode other = additiveExpression();
|
||||
result = new ParseNode(new ConcatPtg(), result, other);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/** Parse and Translate an Expression */
|
||||
private void additiveExpression() {
|
||||
Term();
|
||||
private ParseNode additiveExpression() {
|
||||
ParseNode result = Term();
|
||||
while (true) {
|
||||
SkipWhite();
|
||||
Ptg operator;
|
||||
switch(look) {
|
||||
case '+':
|
||||
Match('+');
|
||||
Term();
|
||||
tokens.add(new AddPtg());
|
||||
continue;
|
||||
operator = new AddPtg();
|
||||
break;
|
||||
case '-':
|
||||
Match('-');
|
||||
Term();
|
||||
tokens.add(new SubtractPtg());
|
||||
continue;
|
||||
operator = new SubtractPtg();
|
||||
break;
|
||||
default:
|
||||
return result; // finished with additive expression
|
||||
}
|
||||
return; // finished with additive expression
|
||||
ParseNode other = Term();
|
||||
result = new ParseNode(operator, result, other);
|
||||
}
|
||||
}
|
||||
|
||||
@ -835,7 +748,7 @@ end;
|
||||
public void parse() {
|
||||
pointer=0;
|
||||
GetChar();
|
||||
comparisonExpression();
|
||||
_rootNode = comparisonExpression();
|
||||
|
||||
if(pointer <= formulaLength) {
|
||||
String msg = "Unused input [" + formulaString.substring(pointer-1)
|
||||
@ -858,87 +771,12 @@ end;
|
||||
}
|
||||
|
||||
public Ptg[] getRPNPtg(int formulaType) {
|
||||
Node node = createTree();
|
||||
OperandClassTransformer oct = new OperandClassTransformer(formulaType);
|
||||
// RVA is for 'operand class': 'reference', 'value', 'array'
|
||||
setRootLevelRVA(node, formulaType);
|
||||
setParameterRVA(node,formulaType);
|
||||
return (Ptg[]) tokens.toArray(new Ptg[0]);
|
||||
oct.transformFormula(_rootNode);
|
||||
return ParseNode.toTokenArray(_rootNode);
|
||||
}
|
||||
|
||||
private void setRootLevelRVA(Node n, int formulaType) {
|
||||
//Pg 16, excelfileformat.pdf @ openoffice.org
|
||||
Ptg p = n.getValue();
|
||||
if (formulaType == FormulaParser.FORMULA_TYPE_NAMEDRANGE) {
|
||||
if (p.getDefaultOperandClass() == Ptg.CLASS_REF) {
|
||||
setClass(n,Ptg.CLASS_REF);
|
||||
} else {
|
||||
setClass(n,Ptg.CLASS_ARRAY);
|
||||
}
|
||||
} else {
|
||||
setClass(n,Ptg.CLASS_VALUE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void setParameterRVA(Node n, int formulaType) {
|
||||
Ptg p = n.getValue();
|
||||
int numOperands = n.getNumChildren();
|
||||
if (p instanceof AbstractFunctionPtg) {
|
||||
for (int i =0;i<numOperands;i++) {
|
||||
setParameterRVA(n.getChild(i),((AbstractFunctionPtg)p).getParameterClass(i),formulaType);
|
||||
// if (n.getChild(i).getValue() instanceof AbstractFunctionPtg) {
|
||||
// setParameterRVA(n.getChild(i),formulaType);
|
||||
// }
|
||||
setParameterRVA(n.getChild(i),formulaType);
|
||||
}
|
||||
} else {
|
||||
for (int i =0;i<numOperands;i++) {
|
||||
setParameterRVA(n.getChild(i),formulaType);
|
||||
}
|
||||
}
|
||||
}
|
||||
private void setParameterRVA(Node n, int expectedClass,int formulaType) {
|
||||
Ptg p = n.getValue();
|
||||
if (expectedClass == Ptg.CLASS_REF) { //pg 15, table 1
|
||||
if (p.getDefaultOperandClass() == Ptg.CLASS_REF ) {
|
||||
setClass(n, Ptg.CLASS_REF);
|
||||
}
|
||||
if (p.getDefaultOperandClass() == Ptg.CLASS_VALUE) {
|
||||
if (formulaType==FORMULA_TYPE_CELL || formulaType == FORMULA_TYPE_SHARED) {
|
||||
setClass(n,Ptg.CLASS_VALUE);
|
||||
} else {
|
||||
setClass(n,Ptg.CLASS_ARRAY);
|
||||
}
|
||||
}
|
||||
if (p.getDefaultOperandClass() == Ptg.CLASS_ARRAY ) {
|
||||
setClass(n, Ptg.CLASS_ARRAY);
|
||||
}
|
||||
} else if (expectedClass == Ptg.CLASS_VALUE) { //pg 15, table 2
|
||||
if (formulaType == FORMULA_TYPE_NAMEDRANGE) {
|
||||
setClass(n,Ptg.CLASS_ARRAY) ;
|
||||
} else {
|
||||
setClass(n,Ptg.CLASS_VALUE);
|
||||
}
|
||||
} else { //Array class, pg 16.
|
||||
if (p.getDefaultOperandClass() == Ptg.CLASS_VALUE &&
|
||||
(formulaType==FORMULA_TYPE_CELL || formulaType == FORMULA_TYPE_SHARED)) {
|
||||
setClass(n,Ptg.CLASS_VALUE);
|
||||
} else {
|
||||
setClass(n,Ptg.CLASS_ARRAY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setClass(Node n, byte theClass) {
|
||||
Ptg p = n.getValue();
|
||||
if (p instanceof AbstractFunctionPtg || !(p instanceof OperationPtg)) {
|
||||
p.setClass(theClass);
|
||||
} else {
|
||||
for (int i =0;i<n.getNumChildren();i++) {
|
||||
setClass(n.getChild(i),theClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Convenience method which takes in a list then passes it to the
|
||||
* other toFormulaString signature.
|
||||
@ -988,11 +826,11 @@ end;
|
||||
// TODO - put comment and throw exception in toFormulaString() of these classes
|
||||
continue;
|
||||
}
|
||||
if (! (ptg instanceof OperationPtg)) {
|
||||
stack.push(ptg.toFormulaString(book));
|
||||
if (ptg instanceof ParenthesisPtg) {
|
||||
String contents = (String)stack.pop();
|
||||
stack.push ("(" + contents + ")");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ptg instanceof AttrPtg) {
|
||||
AttrPtg attrPtg = ((AttrPtg) ptg);
|
||||
if (attrPtg.isOptimizedIf() || attrPtg.isOptimizedChoose() || attrPtg.isGoto()) {
|
||||
@ -1009,24 +847,21 @@ end;
|
||||
// similar to tAttrSpace - RPN is violated
|
||||
continue;
|
||||
}
|
||||
if (!attrPtg.isSum()) {
|
||||
throw new RuntimeException("Unexpected tAttr: " + attrPtg.toString());
|
||||
if (attrPtg.isSum()) {
|
||||
String[] operands = getOperands(stack, attrPtg.getNumberOfOperands());
|
||||
stack.push(attrPtg.toFormulaString(operands));
|
||||
continue;
|
||||
}
|
||||
throw new RuntimeException("Unexpected tAttr: " + attrPtg.toString());
|
||||
}
|
||||
|
||||
final OperationPtg o = (OperationPtg) ptg;
|
||||
int nOperands = o.getNumberOfOperands();
|
||||
final String[] operands = new String[nOperands];
|
||||
|
||||
for (int j = nOperands-1; j >= 0; j--) { // reverse iteration because args were pushed in-order
|
||||
if(stack.isEmpty()) {
|
||||
String msg = "Too few arguments suppled to operation token ("
|
||||
+ o.getClass().getName() + "). Expected (" + nOperands
|
||||
+ ") operands but got (" + (nOperands - j - 1) + ")";
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
operands[j] = (String) stack.pop();
|
||||
if (! (ptg instanceof OperationPtg)) {
|
||||
stack.push(ptg.toFormulaString(book));
|
||||
continue;
|
||||
}
|
||||
|
||||
OperationPtg o = (OperationPtg) ptg;
|
||||
String[] operands = getOperands(stack, o.getNumberOfOperands());
|
||||
stack.push(o.toFormulaString(operands));
|
||||
}
|
||||
if(stack.isEmpty()) {
|
||||
@ -1042,6 +877,20 @@ end;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String[] getOperands(Stack stack, int nOperands) {
|
||||
String[] operands = new String[nOperands];
|
||||
|
||||
for (int j = nOperands-1; j >= 0; j--) { // reverse iteration because args were pushed in-order
|
||||
if(stack.isEmpty()) {
|
||||
String msg = "Too few arguments supplied to operation. Expected (" + nOperands
|
||||
+ ") operands but got (" + (nOperands - j - 1) + ")";
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
operands[j] = (String) stack.pop();
|
||||
}
|
||||
return operands;
|
||||
}
|
||||
/**
|
||||
* Static method to convert an array of Ptgs in RPN order
|
||||
* to a human readable string format in infix mode. Works
|
||||
@ -1052,59 +901,4 @@ end;
|
||||
public String toFormulaString(Ptg[] ptgs) {
|
||||
return toFormulaString(book, ptgs);
|
||||
}
|
||||
|
||||
|
||||
/** Create a tree representation of the RPN token array
|
||||
*used to run the class(RVA) change algo
|
||||
*/
|
||||
private Node createTree() {
|
||||
Stack stack = new Stack();
|
||||
int numPtgs = tokens.size();
|
||||
OperationPtg o;
|
||||
int numOperands;
|
||||
Node[] operands;
|
||||
for (int i=0;i<numPtgs;i++) {
|
||||
if (tokens.get(i) instanceof OperationPtg) {
|
||||
|
||||
o = (OperationPtg) tokens.get(i);
|
||||
numOperands = o.getNumberOfOperands();
|
||||
operands = new Node[numOperands];
|
||||
for (int j=0;j<numOperands;j++) {
|
||||
operands[numOperands-j-1] = (Node) stack.pop();
|
||||
}
|
||||
Node result = new Node(o);
|
||||
result.setChildren(operands);
|
||||
stack.push(result);
|
||||
} else {
|
||||
stack.push(new Node((Ptg)tokens.get(i)));
|
||||
}
|
||||
}
|
||||
return (Node) stack.pop();
|
||||
}
|
||||
|
||||
/** toString on the parser instance returns the RPN ordered list of tokens
|
||||
* Useful for testing
|
||||
*/
|
||||
public String toString() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
for (int i=0;i<tokens.size();i++) {
|
||||
buf.append( ( (Ptg)tokens.get(i)).toFormulaString(book));
|
||||
buf.append(' ');
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/** Private helper class, used to create a tree representation of the formula*/
|
||||
private static final class Node {
|
||||
private Ptg value=null;
|
||||
private Node[] children=new Node[0];
|
||||
private int numChild=0;
|
||||
public Node(Ptg val) {
|
||||
value = val;
|
||||
}
|
||||
public void setChildren(Node[] child) {children = child;numChild=child.length;}
|
||||
public int getNumChildren() {return numChild;}
|
||||
public Node getChild(int number) {return children[number];}
|
||||
public Ptg getValue() {return value;}
|
||||
}
|
||||
}
|
||||
|
206
src/java/org/apache/poi/hssf/model/OperandClassTransformer.java
Normal file
206
src/java/org/apache/poi/hssf/model/OperandClassTransformer.java
Normal file
@ -0,0 +1,206 @@
|
||||
/* ====================================================================
|
||||
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.model;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.AbstractFunctionPtg;
|
||||
import org.apache.poi.hssf.record.formula.ControlPtg;
|
||||
import org.apache.poi.hssf.record.formula.ValueOperatorPtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
|
||||
/**
|
||||
* This class performs 'operand class' transformation. Non-base tokens are classified into three
|
||||
* operand classes:
|
||||
* <ul>
|
||||
* <li>reference</li>
|
||||
* <li>value</li>
|
||||
* <li>array</li>
|
||||
* </ul>
|
||||
* <p/>
|
||||
*
|
||||
* The final operand class chosen for each token depends on the formula type and the token's place
|
||||
* in the formula. If POI gets the operand class wrong, Excel <em>may</em> interpret the formula
|
||||
* incorrectly. This condition is typically manifested as a formula cell that displays as '#VALUE!',
|
||||
* but resolves correctly when the user presses F2, enter.<p/>
|
||||
*
|
||||
* The logic implemented here was partially inspired by the description in
|
||||
* "OpenOffice.org's Documentation of the Microsoft Excel File Format". The model presented there
|
||||
* seems to be inconsistent with observed Excel behaviour (These differences have not been fully
|
||||
* investigated). The implementation in this class has been heavily modified in order to satisfy
|
||||
* concrete examples of how Excel performs the same logic (see TestRVA).<p/>
|
||||
*
|
||||
* Hopefully, as additional important test cases are identified and added to the test suite,
|
||||
* patterns might become more obvious in this code and allow for simplification.
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
final class OperandClassTransformer {
|
||||
|
||||
private final int _formulaType;
|
||||
|
||||
public OperandClassTransformer(int formulaType) {
|
||||
_formulaType = formulaType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverses the supplied formula parse tree, calling <tt>Ptg.setClass()</tt> for each non-base
|
||||
* token to set its operand class.
|
||||
*/
|
||||
public void transformFormula(ParseNode rootNode) {
|
||||
byte rootNodeOperandClass;
|
||||
switch (_formulaType) {
|
||||
case FormulaParser.FORMULA_TYPE_CELL:
|
||||
rootNodeOperandClass = Ptg.CLASS_VALUE;
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Incomplete code - formula type ("
|
||||
+ _formulaType + ") not supported yet");
|
||||
|
||||
}
|
||||
transformNode(rootNode, rootNodeOperandClass, false);
|
||||
}
|
||||
|
||||
private void transformNode(ParseNode node, byte desiredOperandClass,
|
||||
boolean callerForceArrayFlag) {
|
||||
Ptg token = node.getToken();
|
||||
ParseNode[] children = node.getChildren();
|
||||
if (token instanceof ValueOperatorPtg || token instanceof ControlPtg) {
|
||||
// Value Operator Ptgs and Control are base tokens, so token will be unchanged
|
||||
|
||||
// but any child nodes are processed according to desiredOperandClass and callerForceArrayFlag
|
||||
for (int i = 0; i < children.length; i++) {
|
||||
ParseNode child = children[i];
|
||||
transformNode(child, desiredOperandClass, callerForceArrayFlag);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (token instanceof AbstractFunctionPtg) {
|
||||
transformFunctionNode((AbstractFunctionPtg) token, children, desiredOperandClass,
|
||||
callerForceArrayFlag);
|
||||
return;
|
||||
}
|
||||
if (children.length > 0) {
|
||||
throw new IllegalStateException("Node should not have any children");
|
||||
}
|
||||
|
||||
if (token.isBaseToken()) {
|
||||
// nothing to do
|
||||
return;
|
||||
}
|
||||
if (callerForceArrayFlag) {
|
||||
switch (desiredOperandClass) {
|
||||
case Ptg.CLASS_VALUE:
|
||||
case Ptg.CLASS_ARRAY:
|
||||
token.setClass(Ptg.CLASS_ARRAY);
|
||||
break;
|
||||
case Ptg.CLASS_REF:
|
||||
token.setClass(Ptg.CLASS_REF);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unexpected operand class ("
|
||||
+ desiredOperandClass + ")");
|
||||
}
|
||||
} else {
|
||||
token.setClass(desiredOperandClass);
|
||||
}
|
||||
}
|
||||
|
||||
private void transformFunctionNode(AbstractFunctionPtg afp, ParseNode[] children,
|
||||
byte desiredOperandClass, boolean callerForceArrayFlag) {
|
||||
|
||||
boolean localForceArrayFlag;
|
||||
byte defaultReturnOperandClass = afp.getDefaultOperandClass();
|
||||
|
||||
if (callerForceArrayFlag) {
|
||||
switch (defaultReturnOperandClass) {
|
||||
case Ptg.CLASS_REF:
|
||||
if (desiredOperandClass == Ptg.CLASS_REF) {
|
||||
afp.setClass(Ptg.CLASS_REF);
|
||||
} else {
|
||||
afp.setClass(Ptg.CLASS_ARRAY);
|
||||
}
|
||||
localForceArrayFlag = false;
|
||||
break;
|
||||
case Ptg.CLASS_ARRAY:
|
||||
afp.setClass(Ptg.CLASS_ARRAY);
|
||||
localForceArrayFlag = false;
|
||||
break;
|
||||
case Ptg.CLASS_VALUE:
|
||||
afp.setClass(Ptg.CLASS_ARRAY);
|
||||
localForceArrayFlag = true;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unexpected operand class ("
|
||||
+ defaultReturnOperandClass + ")");
|
||||
}
|
||||
} else {
|
||||
if (defaultReturnOperandClass == desiredOperandClass) {
|
||||
localForceArrayFlag = false;
|
||||
// an alternative would have been to for non-base Ptgs to set their operand class
|
||||
// from their default, but this would require the call in many subclasses because
|
||||
// the default OC is not known until the end of the constructor
|
||||
afp.setClass(defaultReturnOperandClass);
|
||||
} else {
|
||||
switch (desiredOperandClass) {
|
||||
case Ptg.CLASS_VALUE:
|
||||
// always OK to set functions to return 'value'
|
||||
afp.setClass(Ptg.CLASS_VALUE);
|
||||
localForceArrayFlag = false;
|
||||
break;
|
||||
case Ptg.CLASS_ARRAY:
|
||||
switch (defaultReturnOperandClass) {
|
||||
case Ptg.CLASS_REF:
|
||||
afp.setClass(Ptg.CLASS_REF);
|
||||
break;
|
||||
case Ptg.CLASS_VALUE:
|
||||
afp.setClass(Ptg.CLASS_ARRAY);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unexpected operand class ("
|
||||
+ defaultReturnOperandClass + ")");
|
||||
}
|
||||
localForceArrayFlag = (defaultReturnOperandClass == Ptg.CLASS_VALUE);
|
||||
break;
|
||||
case Ptg.CLASS_REF:
|
||||
switch (defaultReturnOperandClass) {
|
||||
case Ptg.CLASS_ARRAY:
|
||||
afp.setClass(Ptg.CLASS_ARRAY);
|
||||
break;
|
||||
case Ptg.CLASS_VALUE:
|
||||
afp.setClass(Ptg.CLASS_VALUE);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unexpected operand class ("
|
||||
+ defaultReturnOperandClass + ")");
|
||||
}
|
||||
localForceArrayFlag = false;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unexpected operand class ("
|
||||
+ desiredOperandClass + ")");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < children.length; i++) {
|
||||
ParseNode child = children[i];
|
||||
byte paramOperandClass = afp.getParameterClass(i);
|
||||
transformNode(child, paramOperandClass, localForceArrayFlag);
|
||||
}
|
||||
}
|
||||
}
|
201
src/java/org/apache/poi/hssf/model/ParseNode.java
Normal file
201
src/java/org/apache/poi/hssf/model/ParseNode.java
Normal file
@ -0,0 +1,201 @@
|
||||
/* ====================================================================
|
||||
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.model;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.AttrPtg;
|
||||
import org.apache.poi.hssf.record.formula.FuncVarPtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
|
||||
/**
|
||||
* Represents a syntactic element from a formula by encapsulating the corresponding <tt>Ptg</tt>
|
||||
* token. Each <tt>ParseNode</tt> may have child <tt>ParseNode</tt>s in the case when the wrapped
|
||||
* <tt>Ptg</tt> is non-atomic.
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
final class ParseNode {
|
||||
|
||||
public static final ParseNode[] EMPTY_ARRAY = { };
|
||||
private final Ptg _token;
|
||||
private final ParseNode[] _children;
|
||||
private boolean _isIf;
|
||||
private final int _tokenCount;
|
||||
|
||||
public ParseNode(Ptg token, ParseNode[] children) {
|
||||
_token = token;
|
||||
_children = children;
|
||||
_isIf = isIf(token);
|
||||
int tokenCount = 1;
|
||||
for (int i = 0; i < children.length; i++) {
|
||||
tokenCount += children[i].getTokenCount();
|
||||
}
|
||||
if (_isIf) {
|
||||
// there will be 2 or 3 extra tAttr tokens according to whether the false param is present
|
||||
tokenCount += children.length;
|
||||
}
|
||||
_tokenCount = tokenCount;
|
||||
}
|
||||
public ParseNode(Ptg token) {
|
||||
this(token, EMPTY_ARRAY);
|
||||
}
|
||||
public ParseNode(Ptg token, ParseNode child0) {
|
||||
this(token, new ParseNode[] { child0, });
|
||||
}
|
||||
public ParseNode(Ptg token, ParseNode child0, ParseNode child1) {
|
||||
this(token, new ParseNode[] { child0, child1, });
|
||||
}
|
||||
private int getTokenCount() {
|
||||
return _tokenCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the array of <tt>Ptg</tt> tokens for the specified tree.
|
||||
*/
|
||||
public static Ptg[] toTokenArray(ParseNode rootNode) {
|
||||
TokenCollector temp = new TokenCollector(rootNode.getTokenCount());
|
||||
rootNode.collectPtgs(temp);
|
||||
return temp.getResult();
|
||||
}
|
||||
private void collectPtgs(TokenCollector temp) {
|
||||
if (isIf(getToken())) {
|
||||
collectIfPtgs(temp);
|
||||
return;
|
||||
}
|
||||
for (int i=0; i< getChildren().length; i++) {
|
||||
getChildren()[i].collectPtgs(temp);
|
||||
}
|
||||
temp.add(getToken());
|
||||
}
|
||||
/**
|
||||
* The IF() function gets marked up with two or three tAttr tokens.
|
||||
* Similar logic will be required for CHOOSE() when it is supported
|
||||
*
|
||||
* See excelfileformat.pdf sec 3.10.5 "tAttr (19H)
|
||||
*/
|
||||
private void collectIfPtgs(TokenCollector temp) {
|
||||
|
||||
// condition goes first
|
||||
getChildren()[0].collectPtgs(temp);
|
||||
|
||||
// placeholder for tAttrIf
|
||||
int ifAttrIndex = temp.createPlaceholder();
|
||||
|
||||
// true parameter
|
||||
getChildren()[1].collectPtgs(temp);
|
||||
|
||||
// placeholder for first skip attr
|
||||
int skipAfterTrueParamIndex = temp.createPlaceholder();
|
||||
int trueParamSize = temp.sumTokenSizes(ifAttrIndex+1, skipAfterTrueParamIndex);
|
||||
|
||||
AttrPtg attrIf = new AttrPtg();
|
||||
attrIf.setOptimizedIf(true);
|
||||
AttrPtg attrSkipAfterTrue = new AttrPtg();
|
||||
attrSkipAfterTrue.setGoto(true);
|
||||
|
||||
if (getChildren().length > 2) {
|
||||
// false param present
|
||||
|
||||
// false parameter
|
||||
getChildren()[2].collectPtgs(temp);
|
||||
|
||||
int skipAfterFalseParamIndex = temp.createPlaceholder();
|
||||
|
||||
AttrPtg attrSkipAfterFalse = new AttrPtg();
|
||||
attrSkipAfterFalse.setGoto(true);
|
||||
|
||||
int falseParamSize = temp.sumTokenSizes(skipAfterTrueParamIndex+1, skipAfterFalseParamIndex);
|
||||
|
||||
attrIf.setData((short)(trueParamSize + 4)); // distance to start of false parameter. +4 for skip after true
|
||||
attrSkipAfterTrue.setData((short)(falseParamSize + 4 + 4 - 1)); // 1 less than distance to end of if FuncVar(size=4). +4 for attr skip before
|
||||
attrSkipAfterFalse.setData((short)(4 - 1)); // 1 less than distance to end of if FuncVar(size=4).
|
||||
|
||||
temp.setPlaceholder(ifAttrIndex, attrIf);
|
||||
temp.setPlaceholder(skipAfterTrueParamIndex, attrSkipAfterTrue);
|
||||
temp.setPlaceholder(skipAfterFalseParamIndex, attrSkipAfterFalse);
|
||||
} else {
|
||||
// false parameter not present
|
||||
attrIf.setData((short)(trueParamSize + 4)); // distance to start of FuncVar. +4 for skip after true
|
||||
attrSkipAfterTrue.setData((short)(4 - 1)); // 1 less than distance to end of if FuncVar(size=4).
|
||||
|
||||
temp.setPlaceholder(ifAttrIndex, attrIf);
|
||||
temp.setPlaceholder(skipAfterTrueParamIndex, attrSkipAfterTrue);
|
||||
}
|
||||
|
||||
temp.add(getToken());
|
||||
}
|
||||
|
||||
private static boolean isIf(Ptg token) {
|
||||
if (token instanceof FuncVarPtg) {
|
||||
FuncVarPtg func = (FuncVarPtg) token;
|
||||
if (FunctionMetadataRegistry.FUNCTION_NAME_IF.equals(func.getName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Ptg getToken() {
|
||||
return _token;
|
||||
}
|
||||
|
||||
public ParseNode[] getChildren() {
|
||||
return _children;
|
||||
}
|
||||
|
||||
private static final class TokenCollector {
|
||||
|
||||
private final Ptg[] _ptgs;
|
||||
private int _offset;
|
||||
|
||||
public TokenCollector(int tokenCount) {
|
||||
_ptgs = new Ptg[tokenCount];
|
||||
_offset = 0;
|
||||
}
|
||||
|
||||
public int sumTokenSizes(int fromIx, int toIx) {
|
||||
int result = 0;
|
||||
for (int i=fromIx; i<toIx; i++) {
|
||||
result += _ptgs[i].getSize();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public int createPlaceholder() {
|
||||
return _offset++;
|
||||
}
|
||||
|
||||
public void add(Ptg token) {
|
||||
if (token == null) {
|
||||
throw new IllegalArgumentException("token must not be null");
|
||||
}
|
||||
_ptgs[_offset] = token;
|
||||
_offset++;
|
||||
}
|
||||
|
||||
public void setPlaceholder(int index, Ptg token) {
|
||||
if (_ptgs[index] != null) {
|
||||
throw new IllegalStateException("Invalid placeholder index (" + index + ")");
|
||||
}
|
||||
_ptgs[index] = token;
|
||||
}
|
||||
|
||||
public Ptg[] getResult() {
|
||||
return _ptgs;
|
||||
}
|
||||
}
|
||||
}
|
@ -108,7 +108,10 @@ public class EmbeddedObjectRefSubRecord
|
||||
in.readByte(); // discard
|
||||
}
|
||||
|
||||
field_6_stream_id = in.readInt();
|
||||
// Fetch the stream ID
|
||||
field_6_stream_id = in.readInt();
|
||||
|
||||
// Store what's left
|
||||
remainingBytes = in.readRemainder();
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,10 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
|
||||
protected byte field_1_num_args;
|
||||
protected short field_2_fnc_index;
|
||||
|
||||
public final boolean isBaseToken() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer(64);
|
||||
sb.append(getClass().getName()).append(" [");
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
@ -16,11 +15,6 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
/*
|
||||
* AddPtg.java
|
||||
*
|
||||
* Created on October 29, 2001, 7:48 PM
|
||||
*/
|
||||
package org.apache.poi.hssf.record.formula;
|
||||
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
@ -32,10 +26,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
* @author Andrew C. Oliver (acoliver@apache.org)
|
||||
* @author Jason Height (jheight at chariot dot net dot au)
|
||||
*/
|
||||
|
||||
public class AddPtg
|
||||
extends OperationPtg
|
||||
{
|
||||
public final class AddPtg extends ValueOperatorPtg {
|
||||
public final static int SIZE = 1;
|
||||
public final static byte sid = 0x03;
|
||||
|
||||
@ -89,8 +80,6 @@ public class AddPtg
|
||||
buffer.append(operands[ 1 ]);
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
public byte getDefaultOperandClass() {return Ptg.CLASS_VALUE;}
|
||||
|
||||
public Object clone() {
|
||||
return new AddPtg();
|
||||
|
@ -35,8 +35,7 @@ import org.apache.poi.util.LittleEndian;
|
||||
* @author Jason Height (jheight at chariot dot net dot au)
|
||||
* @version 1.0-pre
|
||||
*/
|
||||
public class Area3DPtg extends Ptg implements AreaI
|
||||
{
|
||||
public class Area3DPtg extends OperandPtg implements AreaI {
|
||||
public final static byte sid = 0x3b;
|
||||
private final static int SIZE = 11; // 10 + 1 for Ptg
|
||||
private short field_1_index_extern_sheet;
|
||||
|
@ -31,7 +31,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
* @author andy
|
||||
* @author Jason Height (jheight at chariot dot net dot au)
|
||||
*/
|
||||
public class AreaPtg extends Ptg implements AreaI {
|
||||
public class AreaPtg extends OperandPtg implements AreaI {
|
||||
/**
|
||||
* TODO - (May-2008) fix subclasses of AreaPtg 'AreaN~' which are used in shared formulas.
|
||||
* see similar comment in ReferencePtg
|
||||
|
@ -59,6 +59,10 @@ public class ArrayPtg extends Ptg {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isBaseToken() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read in the actual token (array) values. This occurs
|
||||
* AFTER the last Ptg in the expression.
|
||||
@ -95,6 +99,10 @@ public class ArrayPtg extends Ptg {
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Note - (2D) array elements are stored column by column
|
||||
* @return the index into the internal 1D array for the specified column and row
|
||||
*/
|
||||
/* package */ int getValueIndex(int colIx, int rowIx) {
|
||||
if(colIx < 0 || colIx >= token_1_columns) {
|
||||
throw new IllegalArgumentException("Specified colIx (" + colIx
|
||||
@ -104,7 +112,7 @@ public class ArrayPtg extends Ptg {
|
||||
throw new IllegalArgumentException("Specified rowIx (" + rowIx
|
||||
+ ") is outside the allowed range (0.." + (token_2_rows-1) + ")");
|
||||
}
|
||||
return rowIx * token_1_columns + colIx;
|
||||
return rowIx + token_2_rows * colIx;
|
||||
}
|
||||
|
||||
public void writeBytes(byte[] data, int offset) {
|
||||
|
@ -15,7 +15,6 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
|
||||
package org.apache.poi.hssf.record.formula;
|
||||
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
@ -32,8 +31,7 @@ import org.apache.poi.util.BitFieldFactory;
|
||||
* @author andy
|
||||
* @author Jason Height (jheight at chariot dot net dot au)
|
||||
*/
|
||||
|
||||
public final class AttrPtg extends OperationPtg {
|
||||
public final class AttrPtg extends ControlPtg {
|
||||
public final static byte sid = 0x19;
|
||||
private final static int SIZE = 4;
|
||||
private byte field_1_options;
|
||||
@ -289,12 +287,6 @@ public final class AttrPtg extends OperationPtg {
|
||||
}
|
||||
return "UNKNOWN ATTRIBUTE";
|
||||
}
|
||||
|
||||
|
||||
|
||||
public byte getDefaultOperandClass() {
|
||||
return Ptg.CLASS_VALUE;
|
||||
}
|
||||
|
||||
public Object clone() {
|
||||
int[] jt;
|
||||
|
@ -27,10 +27,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
* @author Andrew C. Oliver (acoliver at apache dot org)
|
||||
* @author Jason Height (jheight at chariot dot net dot au)
|
||||
*/
|
||||
|
||||
public class BoolPtg
|
||||
extends Ptg
|
||||
{
|
||||
public final class BoolPtg extends ScalarConstantPtg {
|
||||
public final static int SIZE = 2;
|
||||
public final static byte sid = 0x1d;
|
||||
private boolean field_1_value;
|
||||
@ -75,8 +72,6 @@ public class BoolPtg
|
||||
return field_1_value ? "TRUE" : "FALSE";
|
||||
}
|
||||
|
||||
public byte getDefaultOperandClass() {return Ptg.CLASS_VALUE;}
|
||||
|
||||
public Object clone() {
|
||||
BoolPtg ptg = new BoolPtg();
|
||||
ptg.field_1_value = field_1_value;
|
||||
|
@ -15,7 +15,6 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
|
||||
package org.apache.poi.hssf.record.formula;
|
||||
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
@ -26,10 +25,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
* @author andy
|
||||
* @author Jason Height (jheight at chariot dot net dot au)
|
||||
*/
|
||||
|
||||
public class ConcatPtg
|
||||
extends OperationPtg
|
||||
{
|
||||
public final class ConcatPtg extends ValueOperatorPtg {
|
||||
public final static int SIZE = 1;
|
||||
public final static byte sid = 0x08;
|
||||
|
||||
|
@ -15,11 +15,24 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
|
||||
package org.apache.poi.hssf.record.formula;
|
||||
|
||||
public abstract class ControlPtg
|
||||
extends Ptg
|
||||
{
|
||||
/**
|
||||
* Common superclass for
|
||||
* tExp
|
||||
* tTbl
|
||||
* tParen
|
||||
* tNlr
|
||||
* tAttr
|
||||
* tSheet
|
||||
* tEndSheet
|
||||
*/
|
||||
public abstract class ControlPtg extends Ptg {
|
||||
|
||||
public boolean isBaseToken() {
|
||||
return true;
|
||||
}
|
||||
public final byte getDefaultOperandClass() {
|
||||
throw new IllegalStateException("Control tokens are not classified");
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,6 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
|
||||
package org.apache.poi.hssf.record.formula;
|
||||
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
@ -26,10 +25,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
* @author Andrew C. Oliver acoliver at apache dot org
|
||||
* @author Jason Height (jheight at chariot dot net dot au)
|
||||
*/
|
||||
|
||||
public class DividePtg
|
||||
extends OperationPtg
|
||||
{
|
||||
public final class DividePtg extends ValueOperatorPtg {
|
||||
public final static int SIZE = 1;
|
||||
public final static byte sid = 0x06;
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
@ -25,10 +24,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
*
|
||||
* @author andy
|
||||
*/
|
||||
|
||||
public class EqualPtg
|
||||
extends OperationPtg
|
||||
{
|
||||
public final class EqualPtg extends ValueOperatorPtg {
|
||||
public final static int SIZE = 1;
|
||||
public final static byte sid = 0x0b;
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
@ -16,7 +15,6 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
|
||||
package org.apache.poi.hssf.record.formula;
|
||||
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
@ -26,7 +24,7 @@ import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
|
||||
/**
|
||||
* @author Daniel Noll (daniel at nuix dot com dot au)
|
||||
*/
|
||||
public final class ErrPtg extends Ptg {
|
||||
public final class ErrPtg extends ScalarConstantPtg {
|
||||
|
||||
// convenient access to namespace
|
||||
private static final HSSFErrorConstants EC = null;
|
||||
@ -78,10 +76,6 @@ public final class ErrPtg extends Ptg {
|
||||
return SIZE;
|
||||
}
|
||||
|
||||
public byte getDefaultOperandClass() {
|
||||
return Ptg.CLASS_VALUE;
|
||||
}
|
||||
|
||||
public Object clone() {
|
||||
return new ErrPtg(field_1_error_code);
|
||||
}
|
||||
|
@ -31,9 +31,7 @@ import org.apache.poi.util.LittleEndian;
|
||||
* @author dmui (save existing implementation)
|
||||
*/
|
||||
|
||||
public class ExpPtg
|
||||
extends Ptg
|
||||
{
|
||||
public final class ExpPtg extends ControlPtg {
|
||||
private final static int SIZE = 5;
|
||||
public final static short sid = 0x1;
|
||||
private short field_1_first_row;
|
||||
@ -52,7 +50,7 @@ public class ExpPtg
|
||||
field_1_first_row = in.readShort();
|
||||
field_2_first_col = in.readShort();
|
||||
}
|
||||
|
||||
|
||||
public void writeBytes(byte [] array, int offset)
|
||||
{
|
||||
array[offset+0]= (byte) (sid);
|
||||
@ -86,8 +84,6 @@ public class ExpPtg
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
public byte getDefaultOperandClass() {return Ptg.CLASS_VALUE;}
|
||||
|
||||
public Object clone() {
|
||||
ExpPtg result = new ExpPtg();
|
||||
result.field_1_first_row = field_1_first_row;
|
||||
|
@ -44,6 +44,8 @@ public final class FuncPtg extends AbstractFunctionPtg {
|
||||
throw new RuntimeException("Invalid built-in function index (" + field_2_fnc_index + ")");
|
||||
}
|
||||
numParams = fm.getMinParams();
|
||||
returnClass = fm.getReturnClassCode();
|
||||
paramClass = fm.getParameterClassCodes();
|
||||
}
|
||||
public FuncPtg(int functionIndex) {
|
||||
field_2_fnc_index = (short) functionIndex;
|
||||
|
@ -40,6 +40,15 @@ public final class FuncVarPtg extends AbstractFunctionPtg{
|
||||
public FuncVarPtg(RecordInputStream in) {
|
||||
field_1_num_args = in.readByte();
|
||||
field_2_fnc_index = in.readShort();
|
||||
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 = fm.getReturnClassCode();
|
||||
paramClass = fm.getParameterClassCodes();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
@ -16,22 +15,17 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
|
||||
package org.apache.poi.hssf.record.formula;
|
||||
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.hssf.record.RecordInputStream;
|
||||
|
||||
|
||||
/**
|
||||
* PTG class to implement greater or equal to
|
||||
*
|
||||
* @author fred at stsci dot edu
|
||||
*/
|
||||
|
||||
public class GreaterEqualPtg
|
||||
extends OperationPtg
|
||||
{
|
||||
public final class GreaterEqualPtg extends ValueOperatorPtg {
|
||||
public final static int SIZE = 1;
|
||||
public final static byte sid = 0x0c;
|
||||
|
||||
|
@ -15,16 +15,8 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
* GreaterThanPtg.java
|
||||
*
|
||||
* Created on January 23, 2003, 9:47 AM
|
||||
*/
|
||||
package org.apache.poi.hssf.record.formula;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.hssf.record.RecordInputStream;
|
||||
|
||||
@ -32,9 +24,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
* Greater than operator PTG ">"
|
||||
* @author Cameron Riley (criley at ekmail.com)
|
||||
*/
|
||||
public class GreaterThanPtg
|
||||
extends OperationPtg
|
||||
{
|
||||
public final class GreaterThanPtg extends ValueOperatorPtg {
|
||||
public final static int SIZE = 1;
|
||||
public final static byte sid = 0x0D;
|
||||
private final static String GREATERTHAN = ">";
|
||||
@ -117,15 +107,6 @@ public class GreaterThanPtg
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default operands class value
|
||||
* @return byte the Ptg Class Value as a byte from the Ptg Parent object
|
||||
*/
|
||||
public byte getDefaultOperandClass()
|
||||
{
|
||||
return Ptg.CLASS_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of clone method from Object
|
||||
* @return Object a clone of this class as an Object
|
||||
|
@ -27,7 +27,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
* @author Andrew C. Oliver (acoliver at apache dot org)
|
||||
* @author Jason Height (jheight at chariot dot net dot au)
|
||||
*/
|
||||
public final class IntPtg extends Ptg {
|
||||
public final class IntPtg extends ScalarConstantPtg {
|
||||
// 16 bit unsigned integer
|
||||
private static final int MIN_VALUE = 0x0000;
|
||||
private static final int MAX_VALUE = 0xFFFF;
|
||||
@ -75,9 +75,6 @@ public final class IntPtg extends Ptg {
|
||||
public String toFormulaString(Workbook book) {
|
||||
return String.valueOf(getValue());
|
||||
}
|
||||
public byte getDefaultOperandClass() {
|
||||
return Ptg.CLASS_VALUE;
|
||||
}
|
||||
|
||||
public Object clone() {
|
||||
return new IntPtg(field_1_value);
|
||||
|
@ -23,8 +23,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
/**
|
||||
* @author Daniel Noll (daniel at nuix dot com dot au)
|
||||
*/
|
||||
public class IntersectionPtg extends OperationPtg
|
||||
{
|
||||
public final class IntersectionPtg extends OperationPtg {
|
||||
public final static byte sid = 0x0f;
|
||||
|
||||
|
||||
@ -37,6 +36,9 @@ public class IntersectionPtg extends OperationPtg
|
||||
// doesn't need anything
|
||||
}
|
||||
|
||||
public final boolean isBaseToken() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getSize()
|
||||
{
|
||||
|
@ -16,7 +16,6 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
|
||||
package org.apache.poi.hssf.record.formula;
|
||||
|
||||
|
||||
@ -29,9 +28,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
*
|
||||
* @author fred at stsci dot edu
|
||||
*/
|
||||
public class LessEqualPtg
|
||||
extends OperationPtg
|
||||
{
|
||||
public final class LessEqualPtg extends ValueOperatorPtg {
|
||||
public final static int SIZE = 1;
|
||||
public final static byte sid = 0x0a;
|
||||
|
||||
|
@ -15,18 +15,8 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
* LessThanPtg.java
|
||||
*
|
||||
* Created on January 23, 2003, 9:47 AM
|
||||
*/
|
||||
package org.apache.poi.hssf.record.formula;
|
||||
|
||||
//JDK
|
||||
import java.util.List;
|
||||
|
||||
//POI
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.hssf.record.RecordInputStream;
|
||||
|
||||
@ -36,9 +26,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
* Table 3.5.7
|
||||
* @author Cameron Riley (criley at ekmail.com)
|
||||
*/
|
||||
public class LessThanPtg
|
||||
extends OperationPtg
|
||||
{
|
||||
public final class LessThanPtg extends ValueOperatorPtg {
|
||||
/** the size of the Ptg */
|
||||
public final static int SIZE = 1;
|
||||
|
||||
@ -125,15 +113,6 @@ public class LessThanPtg
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default operands class value
|
||||
* @return byte the Ptg Class Value as a byte from the Ptg Parent object
|
||||
*/
|
||||
public byte getDefaultOperandClass()
|
||||
{
|
||||
return Ptg.CLASS_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of clone method from Object
|
||||
* @return Object a clone of this class as an Object
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
@ -16,12 +15,6 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
* MemAreaPtg.java
|
||||
*
|
||||
* Created on November 21, 2001, 8:46 AM
|
||||
*/
|
||||
package org.apache.poi.hssf.record.formula;
|
||||
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
@ -31,9 +24,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
/**
|
||||
* @author Daniel Noll (daniel at nuix dot com dot au)
|
||||
*/
|
||||
public class MemAreaPtg
|
||||
extends Ptg
|
||||
{
|
||||
public class MemAreaPtg extends OperandPtg {
|
||||
public final static short sid = 0x26;
|
||||
private final static int SIZE = 7;
|
||||
private int field_1_reserved;
|
||||
|
@ -30,8 +30,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
/**
|
||||
* @author Glen Stampoultzis (glens at apache.org)
|
||||
*/
|
||||
public class MemFuncPtg extends ControlPtg
|
||||
{
|
||||
public class MemFuncPtg extends OperandPtg {
|
||||
|
||||
public final static byte sid = 0x29;
|
||||
private short field_1_len_ref_subexpression = 0;
|
||||
|
@ -26,9 +26,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
* Avik Sengupta <avik at apache.org>
|
||||
* @author Jason Height (jheight at chariot dot net dot au)
|
||||
*/
|
||||
public class MissingArgPtg
|
||||
extends Ptg
|
||||
{
|
||||
public final class MissingArgPtg extends ScalarConstantPtg {
|
||||
|
||||
private final static int SIZE = 1;
|
||||
public final static byte sid = 0x16;
|
||||
@ -59,8 +57,6 @@ public class MissingArgPtg
|
||||
{
|
||||
return " ";
|
||||
}
|
||||
|
||||
public byte getDefaultOperandClass() {return Ptg.CLASS_VALUE;}
|
||||
|
||||
public Object clone() {
|
||||
return new MissingArgPtg();
|
||||
|
@ -25,9 +25,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
* @author Jason Height (jheight at chariot dot net dot au)
|
||||
*/
|
||||
|
||||
public class MultiplyPtg
|
||||
extends OperationPtg
|
||||
{
|
||||
public final class MultiplyPtg extends ValueOperatorPtg {
|
||||
public final static int SIZE = 1;
|
||||
public final static byte sid = 0x05;
|
||||
|
||||
|
@ -27,10 +27,7 @@ import org.apache.poi.util.LittleEndian;
|
||||
* @author andy
|
||||
* @author Jason Height (jheight at chariot dot net dot au)
|
||||
*/
|
||||
|
||||
public class NamePtg
|
||||
extends Ptg
|
||||
{
|
||||
public final class NamePtg extends OperandPtg {
|
||||
public final static short sid = 0x23;
|
||||
private final static int SIZE = 5;
|
||||
/** one-based index to defined name record */
|
||||
|
@ -25,7 +25,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
*
|
||||
* @author aviks
|
||||
*/
|
||||
public final class NameXPtg extends Ptg {
|
||||
public final class NameXPtg extends OperandPtg {
|
||||
public final static short sid = 0x39;
|
||||
private final static int SIZE = 7;
|
||||
private short field_1_ixals; // index to REF entry in externsheet record
|
||||
|
@ -26,9 +26,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
*
|
||||
* @author fred at stsci dot edu
|
||||
*/
|
||||
public class NotEqualPtg
|
||||
extends OperationPtg
|
||||
{
|
||||
public final class NotEqualPtg extends ValueOperatorPtg {
|
||||
public final static int SIZE = 1;
|
||||
public final static byte sid = 0x0e;
|
||||
|
||||
|
@ -28,10 +28,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
* @author Avik Sengupta
|
||||
* @author Jason Height (jheight at chariot dot net dot au)
|
||||
*/
|
||||
|
||||
public class NumberPtg
|
||||
extends Ptg
|
||||
{
|
||||
public final class NumberPtg extends ScalarConstantPtg {
|
||||
public final static int SIZE = 9;
|
||||
public final static byte sid = 0x1f;
|
||||
private double field_1_value;
|
||||
@ -82,7 +79,6 @@ public class NumberPtg
|
||||
{
|
||||
return "" + getValue();
|
||||
}
|
||||
public byte getDefaultOperandClass() {return Ptg.CLASS_VALUE;}
|
||||
|
||||
public Object clone() {
|
||||
NumberPtg ptg = new NumberPtg();
|
||||
|
31
src/java/org/apache/poi/hssf/record/formula/OperandPtg.java
Normal file
31
src/java/org/apache/poi/hssf/record/formula/OperandPtg.java
Normal file
@ -0,0 +1,31 @@
|
||||
/* ====================================================================
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public abstract class OperandPtg extends Ptg {
|
||||
|
||||
/**
|
||||
* All Operand <tt>Ptg</tt>s are classifed ('relative', 'value', 'array')
|
||||
*/
|
||||
public final boolean isBaseToken() {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -32,9 +32,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
* Andrew C. Oliver (acoliver at apache dot org)
|
||||
* @author Jason Height (jheight at chariot dot net dot au)
|
||||
*/
|
||||
public class ParenthesisPtg
|
||||
extends OperationPtg
|
||||
{
|
||||
public final class ParenthesisPtg extends ControlPtg {
|
||||
|
||||
private final static int SIZE = 1;
|
||||
public final static byte sid = 0x15;
|
||||
@ -61,16 +59,6 @@ public class ParenthesisPtg
|
||||
return SIZE;
|
||||
}
|
||||
|
||||
public int getType()
|
||||
{
|
||||
return TYPE_BINARY;
|
||||
}
|
||||
|
||||
public int getNumberOfOperands()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
public String toFormulaString(Workbook book)
|
||||
{
|
||||
return "()";
|
||||
@ -80,8 +68,6 @@ public class ParenthesisPtg
|
||||
public String toFormulaString(String[] operands) {
|
||||
return "("+operands[0]+")";
|
||||
}
|
||||
|
||||
public byte getDefaultOperandClass() {return Ptg.CLASS_VALUE;}
|
||||
|
||||
public Object clone() {
|
||||
return new ParenthesisPtg();
|
||||
|
@ -32,9 +32,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
* @author Daniel Noll (daniel at nuix.com.au)
|
||||
*/
|
||||
|
||||
public class PercentPtg
|
||||
extends OperationPtg
|
||||
{
|
||||
public final class PercentPtg extends ValueOperatorPtg {
|
||||
public final static int SIZE = 1;
|
||||
public final static byte sid = 0x14;
|
||||
|
||||
@ -88,8 +86,6 @@ public class PercentPtg
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
public byte getDefaultOperandClass() {return Ptg.CLASS_VALUE;}
|
||||
|
||||
public Object clone() {
|
||||
return new PercentPtg();
|
||||
}
|
||||
|
@ -17,8 +17,6 @@
|
||||
|
||||
package org.apache.poi.hssf.record.formula;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.hssf.record.RecordInputStream;
|
||||
|
||||
@ -27,10 +25,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
* @author andy
|
||||
* @author Jason Height (jheight at chariot dot net dot au)
|
||||
*/
|
||||
|
||||
public class PowerPtg
|
||||
extends OperationPtg
|
||||
{
|
||||
public final class PowerPtg extends ValueOperatorPtg {
|
||||
public final static int SIZE = 1;
|
||||
public final static byte sid = 0x07;
|
||||
|
||||
|
@ -119,254 +119,14 @@ public abstract class Ptg
|
||||
return stack;
|
||||
}
|
||||
|
||||
public static Ptg createPtg(RecordInputStream in)
|
||||
{
|
||||
byte id = in.readByte();
|
||||
Ptg retval = null;
|
||||
|
||||
switch (id)
|
||||
{
|
||||
case ExpPtg.sid : // 0x01
|
||||
retval = new ExpPtg(in);
|
||||
break;
|
||||
|
||||
case AddPtg.sid : // 0x03
|
||||
retval = new AddPtg(in);
|
||||
break;
|
||||
|
||||
case SubtractPtg.sid : // 0x04
|
||||
retval = new SubtractPtg(in);
|
||||
break;
|
||||
|
||||
case MultiplyPtg.sid : // 0x05
|
||||
retval = new MultiplyPtg(in);
|
||||
break;
|
||||
|
||||
case DividePtg.sid : // 0x06
|
||||
retval = new DividePtg(in);
|
||||
break;
|
||||
|
||||
case PowerPtg.sid : // 0x07
|
||||
retval = new PowerPtg(in);
|
||||
break;
|
||||
|
||||
case ConcatPtg.sid : // 0x08
|
||||
retval = new ConcatPtg(in);
|
||||
break;
|
||||
|
||||
case LessThanPtg.sid: // 0x09
|
||||
retval = new LessThanPtg(in);
|
||||
break;
|
||||
|
||||
case LessEqualPtg.sid : // 0x0a
|
||||
retval = new LessEqualPtg(in);
|
||||
break;
|
||||
|
||||
case EqualPtg.sid : // 0x0b
|
||||
retval = new EqualPtg(in);
|
||||
break;
|
||||
|
||||
case GreaterEqualPtg.sid : // 0x0c
|
||||
retval = new GreaterEqualPtg(in);
|
||||
break;
|
||||
|
||||
case GreaterThanPtg.sid : // 0x0d
|
||||
retval = new GreaterThanPtg(in);
|
||||
break;
|
||||
|
||||
case NotEqualPtg.sid : // 0x0e
|
||||
retval = new NotEqualPtg(in);
|
||||
break;
|
||||
|
||||
case IntersectionPtg.sid : // 0x0f
|
||||
retval = new IntersectionPtg(in);
|
||||
break;
|
||||
case UnionPtg.sid : // 0x10
|
||||
retval = new UnionPtg(in);
|
||||
break;
|
||||
|
||||
case RangePtg.sid : // 0x11
|
||||
retval = new RangePtg(in);
|
||||
break;
|
||||
|
||||
case UnaryPlusPtg.sid : // 0x12
|
||||
retval = new UnaryPlusPtg(in);
|
||||
break;
|
||||
|
||||
case UnaryMinusPtg.sid : // 0x13
|
||||
retval = new UnaryMinusPtg(in);
|
||||
break;
|
||||
|
||||
case PercentPtg.sid : // 0x14
|
||||
retval = new PercentPtg(in);
|
||||
break;
|
||||
|
||||
case ParenthesisPtg.sid : // 0x15
|
||||
retval = new ParenthesisPtg(in);
|
||||
break;
|
||||
|
||||
case MissingArgPtg.sid : // 0x16
|
||||
retval = new MissingArgPtg(in);
|
||||
break;
|
||||
|
||||
case StringPtg.sid : // 0x17
|
||||
retval = new StringPtg(in);
|
||||
break;
|
||||
|
||||
case AttrPtg.sid : // 0x19
|
||||
case 0x1a :
|
||||
retval = new AttrPtg(in);
|
||||
break;
|
||||
|
||||
case ErrPtg.sid : // 0x1c
|
||||
retval = new ErrPtg(in);
|
||||
break;
|
||||
|
||||
case BoolPtg.sid : // 0x1d
|
||||
retval = new BoolPtg(in);
|
||||
break;
|
||||
|
||||
case IntPtg.sid : // 0x1e
|
||||
retval = new IntPtg(in);
|
||||
break;
|
||||
|
||||
case NumberPtg.sid : // 0x1f
|
||||
retval = new NumberPtg(in);
|
||||
break;
|
||||
|
||||
case ArrayPtg.sid : // 0x20
|
||||
retval = new ArrayPtg(in);
|
||||
break;
|
||||
case ArrayPtgV.sid : // 0x40
|
||||
retval = new ArrayPtgV(in);
|
||||
break;
|
||||
case ArrayPtgA.sid : // 0x60
|
||||
retval = new ArrayPtgA(in);
|
||||
break;
|
||||
|
||||
case FuncPtg.sid : // 0x21
|
||||
case FuncPtg.sid + 0x20 : // 0x41
|
||||
case FuncPtg.sid + 0x40 : // 0x61
|
||||
retval = new FuncPtg(in);
|
||||
break;
|
||||
|
||||
case FuncVarPtg.sid : // 0x22
|
||||
case FuncVarPtg.sid + 0x20 : // 0x42
|
||||
case FuncVarPtg.sid + 0x40 : // 0x62
|
||||
retval = new FuncVarPtg(in);
|
||||
break;
|
||||
|
||||
case ReferencePtg.sid : // 0x24
|
||||
retval = new ReferencePtg(in);
|
||||
break;
|
||||
case RefAPtg.sid : // 0x64
|
||||
retval = new RefAPtg(in);
|
||||
break;
|
||||
case RefVPtg.sid : // 0x44
|
||||
retval = new RefVPtg(in);
|
||||
break;
|
||||
case RefNAPtg.sid : // 0x6C
|
||||
retval = new RefNAPtg(in);
|
||||
break;
|
||||
case RefNPtg.sid : // 0x2C
|
||||
retval = new RefNPtg(in);
|
||||
break;
|
||||
case RefNVPtg.sid : // 0x4C
|
||||
retval = new RefNVPtg(in);
|
||||
break;
|
||||
|
||||
case AreaPtg.sid : // 0x25
|
||||
retval = new AreaPtg(in);
|
||||
break;
|
||||
case AreaVPtg.sid: // 0x45
|
||||
retval = new AreaVPtg(in);
|
||||
break;
|
||||
case AreaAPtg.sid: // 0x65
|
||||
retval = new AreaAPtg(in);
|
||||
break;
|
||||
case AreaNAPtg.sid : // 0x6D
|
||||
retval = new AreaNAPtg(in);
|
||||
break;
|
||||
case AreaNPtg.sid : // 0x2D
|
||||
retval = new AreaNPtg(in);
|
||||
break;
|
||||
case AreaNVPtg.sid : // 0x4D
|
||||
retval = new AreaNVPtg(in);
|
||||
break;
|
||||
|
||||
case MemAreaPtg.sid : // 0x26
|
||||
case MemAreaPtg.sid + 0x40 : // 0x46
|
||||
case MemAreaPtg.sid + 0x20 : // 0x66
|
||||
retval = new MemAreaPtg(in);
|
||||
break;
|
||||
|
||||
case MemErrPtg.sid : // 0x27
|
||||
case MemErrPtg.sid + 0x20 : // 0x47
|
||||
case MemErrPtg.sid + 0x40 : // 0x67
|
||||
retval = new MemErrPtg(in);
|
||||
break;
|
||||
|
||||
case MemFuncPtg.sid : // 0x29
|
||||
retval = new MemFuncPtg(in);
|
||||
break;
|
||||
|
||||
case RefErrorPtg.sid : // 0x2a
|
||||
case RefErrorPtg.sid + 0x20 : // 0x4a
|
||||
case RefErrorPtg.sid + 0x40 : // 0x6a
|
||||
retval = new RefErrorPtg(in);
|
||||
break;
|
||||
|
||||
case AreaErrPtg.sid : // 0x2b
|
||||
case AreaErrPtg.sid + 0x20 : // 0x4b
|
||||
case AreaErrPtg.sid + 0x40 : // 0x6b
|
||||
retval = new AreaErrPtg(in);
|
||||
break;
|
||||
|
||||
case NamePtg.sid : // 0x23
|
||||
case NamePtg.sid + 0x20 : // 0x43
|
||||
case NamePtg.sid + 0x40 : // 0x63
|
||||
retval = new NamePtg(in);
|
||||
break;
|
||||
|
||||
case NameXPtg.sid : // 0x39
|
||||
case NameXPtg.sid + 0x20 : // 0x45
|
||||
case NameXPtg.sid + 0x40 : // 0x79
|
||||
retval = new NameXPtg(in);
|
||||
break;
|
||||
|
||||
case Area3DPtg.sid : // 0x3b
|
||||
case Area3DPtg.sid + 0x20 : // 0x5b
|
||||
case Area3DPtg.sid + 0x40 : // 0x7b
|
||||
retval = new Area3DPtg(in);
|
||||
break;
|
||||
|
||||
case Ref3DPtg.sid : // 0x3a
|
||||
case Ref3DPtg.sid + 0x20: // 0x5a
|
||||
case Ref3DPtg.sid + 0x40: // 0x7a
|
||||
retval = new Ref3DPtg(in);
|
||||
break;
|
||||
|
||||
case DeletedRef3DPtg.sid: // 0x3c
|
||||
case DeletedRef3DPtg.sid + 0x20: // 0x5c
|
||||
case DeletedRef3DPtg.sid + 0x40: // 0x7c
|
||||
retval = new DeletedRef3DPtg(in);
|
||||
break;
|
||||
|
||||
case DeletedArea3DPtg.sid : // 0x3d
|
||||
case DeletedArea3DPtg.sid + 0x20 : // 0x5d
|
||||
case DeletedArea3DPtg.sid + 0x40 : // 0x7d
|
||||
retval = new DeletedArea3DPtg(in);
|
||||
break;
|
||||
|
||||
case 0x00:
|
||||
retval = new UnknownPtg();
|
||||
break;
|
||||
|
||||
default :
|
||||
//retval = new UnknownPtg();
|
||||
throw new java.lang.UnsupportedOperationException(" Unknown Ptg in Formula: 0x"+
|
||||
Integer.toHexString(( int ) id) + " (" + ( int ) id + ")");
|
||||
public static Ptg createPtg(RecordInputStream in) {
|
||||
byte id = in.readByte();
|
||||
|
||||
if (id < 0x20) {
|
||||
return createBasePtg(id, in);
|
||||
}
|
||||
|
||||
Ptg retval = createClassifiedPtg(id, in);
|
||||
|
||||
if (id > 0x60) {
|
||||
retval.setClass(CLASS_ARRAY);
|
||||
@ -380,6 +140,118 @@ public abstract class Ptg
|
||||
|
||||
}
|
||||
|
||||
private static Ptg createClassifiedPtg(byte id, RecordInputStream in) {
|
||||
|
||||
int baseId = id & 0x1F | 0x20;
|
||||
|
||||
switch (baseId) {
|
||||
case FuncPtg.sid: return new FuncPtg(in); // 0x21, 0x41, 0x61
|
||||
case FuncVarPtg.sid: return new FuncVarPtg(in); // 0x22, 0x42, 0x62
|
||||
case NamePtg.sid: return new NamePtg(in); // 0x23, 0x43, 0x63
|
||||
|
||||
case MemAreaPtg.sid: return new MemAreaPtg(in); // 0x26, 0x46, 0x66
|
||||
case MemErrPtg.sid: return new MemErrPtg(in); // 0x27, 0x47, 0x67
|
||||
case MemFuncPtg.sid: return new MemFuncPtg(in); // 0x29, 0x49, 0x69
|
||||
case RefErrorPtg.sid: return new RefErrorPtg(in);// 0x2a, 0x4a, 0x6a
|
||||
case AreaErrPtg.sid: return new AreaErrPtg(in); // 0x2b, 0x4b, 0x6b
|
||||
|
||||
case NameXPtg.sid: return new NameXPtg(in); // 0x39, 0x49, 0x79
|
||||
case Ref3DPtg.sid: return new Ref3DPtg(in); // 0x3a, 0x5a, 0x7a
|
||||
case Area3DPtg.sid: return new Area3DPtg(in); // 0x3b, 0x5b, 0x7b
|
||||
case DeletedRef3DPtg.sid: return new DeletedRef3DPtg(in); // 0x3c, 0x5c, 0x7c
|
||||
case DeletedArea3DPtg.sid: return new DeletedArea3DPtg(in); // 0x3d, 0x5d, 0x7d
|
||||
}
|
||||
|
||||
|
||||
switch (id) {
|
||||
// TODO - why are specific subclasses needed for these Ptgs?
|
||||
case ArrayPtg.sid: return new ArrayPtg(in); // 0x20
|
||||
case ArrayPtgV.sid: return new ArrayPtgV(in); // 0x40
|
||||
case ArrayPtgA.sid: return new ArrayPtgA(in); // 0x60
|
||||
|
||||
case ReferencePtg.sid: return new ReferencePtg(in);// 0x24
|
||||
case RefAPtg.sid: return new RefAPtg(in); // 0x64
|
||||
case RefVPtg.sid: return new RefVPtg(in); // 0x44
|
||||
|
||||
case RefNAPtg.sid: return new RefNAPtg(in); // 0x6C
|
||||
case RefNPtg.sid: return new RefNPtg(in); // 0x2C
|
||||
case RefNVPtg.sid: return new RefNVPtg(in); // 0x4C
|
||||
|
||||
case AreaPtg.sid: return new AreaPtg(in); // 0x25
|
||||
case AreaVPtg.sid: return new AreaVPtg(in); // 0x45
|
||||
case AreaAPtg.sid: return new AreaAPtg(in); // 0x65
|
||||
|
||||
case AreaNAPtg.sid: return new AreaNAPtg(in); // 0x6D
|
||||
case AreaNPtg.sid: return new AreaNPtg(in); // 0x2D
|
||||
case AreaNVPtg.sid: return new AreaNVPtg(in); // 0x4D
|
||||
|
||||
}
|
||||
throw new UnsupportedOperationException(" Unknown Ptg in Formula: 0x"+
|
||||
Integer.toHexString(id) + " (" + ( int ) id + ")");
|
||||
}
|
||||
|
||||
private static Ptg createBasePtg(byte id, RecordInputStream in) {
|
||||
switch(id) {
|
||||
case 0x00: return new UnknownPtg(); // TODO - not a real Ptg
|
||||
case ExpPtg.sid: return new ExpPtg(in); // 0x01
|
||||
case AddPtg.sid: return new AddPtg(in); // 0x03
|
||||
case SubtractPtg.sid: return new SubtractPtg(in); // 0x04
|
||||
case MultiplyPtg.sid: return new MultiplyPtg(in); // 0x05
|
||||
case DividePtg.sid: return new DividePtg(in); // 0x06
|
||||
case PowerPtg.sid: return new PowerPtg(in); // 0x07
|
||||
case ConcatPtg.sid: return new ConcatPtg(in); // 0x08
|
||||
case LessThanPtg.sid: return new LessThanPtg(in); // 0x09
|
||||
case LessEqualPtg.sid: return new LessEqualPtg(in); // 0x0a
|
||||
case EqualPtg.sid: return new EqualPtg(in); // 0x0b
|
||||
case GreaterEqualPtg.sid: return new GreaterEqualPtg(in);// 0x0c
|
||||
case GreaterThanPtg.sid: return new GreaterThanPtg(in); // 0x0d
|
||||
case NotEqualPtg.sid: return new NotEqualPtg(in); // 0x0e
|
||||
case IntersectionPtg.sid: return new IntersectionPtg(in);// 0x0f
|
||||
case UnionPtg.sid: return new UnionPtg(in); // 0x10
|
||||
case RangePtg.sid: return new RangePtg(in); // 0x11
|
||||
case UnaryPlusPtg.sid: return new UnaryPlusPtg(in); // 0x12
|
||||
case UnaryMinusPtg.sid: return new UnaryMinusPtg(in); // 0x13
|
||||
case PercentPtg.sid: return new PercentPtg(in); // 0x14
|
||||
case ParenthesisPtg.sid: return new ParenthesisPtg(in); // 0x15
|
||||
case MissingArgPtg.sid: return new MissingArgPtg(in); // 0x16
|
||||
case StringPtg.sid: return new StringPtg(in); // 0x17
|
||||
case AttrPtg.sid:
|
||||
case 0x1a: return new AttrPtg(in); // 0x19
|
||||
case ErrPtg.sid: return new ErrPtg(in); // 0x1c
|
||||
case BoolPtg.sid: return new BoolPtg(in); // 0x1d
|
||||
case IntPtg.sid: return new IntPtg(in); // 0x1e
|
||||
case NumberPtg.sid: return new NumberPtg(in); // 0x1f
|
||||
}
|
||||
throw new RuntimeException("Unexpected base token id (" + id + ")");
|
||||
}
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
public static int getEncodedSize(Stack ptgs) {
|
||||
return getEncodedSize(toPtgArray(ptgs));
|
||||
}
|
||||
private static Ptg[] toPtgArray(List l) {
|
||||
Ptg[] result = new Ptg[l.size()];
|
||||
l.toArray(result);
|
||||
return result;
|
||||
}
|
||||
private static Stack createStack(Ptg[] formulaTokens) {
|
||||
Stack result = new Stack();
|
||||
for (int i = 0; i < formulaTokens.length; i++) {
|
||||
result.add(formulaTokens[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// TODO - several duplicates of this code should be refactored here
|
||||
public static int getEncodedSize(Ptg[] ptgs) {
|
||||
int result = 0;
|
||||
for (int i = 0; i < ptgs.length; i++) {
|
||||
result += ptgs[i].getSize();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int serializePtgStack(Stack expression, byte[] array, int offset) {
|
||||
int pos = 0;
|
||||
int size = 0;
|
||||
@ -408,7 +280,15 @@ public abstract class Ptg
|
||||
return pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the encoded length of this Ptg, including the initial Ptg type identifier byte.
|
||||
*/
|
||||
public abstract int getSize();
|
||||
|
||||
/**
|
||||
* @return the encoded length of this Ptg, not including the initial Ptg type identifier byte.
|
||||
*/
|
||||
// public abstract int getDataSize();
|
||||
|
||||
public final byte [] getBytes()
|
||||
{
|
||||
@ -455,10 +335,15 @@ public abstract class Ptg
|
||||
protected byte ptgClass = CLASS_REF; //base ptg
|
||||
|
||||
public void setClass(byte thePtgClass) {
|
||||
if (isBaseToken()) {
|
||||
throw new RuntimeException("setClass should not be called on a base token");
|
||||
}
|
||||
ptgClass = thePtgClass;
|
||||
}
|
||||
|
||||
/** returns the class (REF/VALUE/ARRAY) for this Ptg */
|
||||
/**
|
||||
* @return the 'operand class' (REF/VALUE/ARRAY) for this Ptg
|
||||
*/
|
||||
public byte getPtgClass() {
|
||||
return ptgClass;
|
||||
}
|
||||
@ -468,5 +353,8 @@ public abstract class Ptg
|
||||
public abstract Object clone();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return <code>false</code> if this token is classified as 'reference', 'value', or 'array'
|
||||
*/
|
||||
public abstract boolean isBaseToken();
|
||||
}
|
||||
|
@ -23,8 +23,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
/**
|
||||
* @author Daniel Noll (daniel at nuix dot com dot au)
|
||||
*/
|
||||
public class RangePtg extends OperationPtg
|
||||
{
|
||||
public final class RangePtg extends OperationPtg {
|
||||
public final static int SIZE = 1;
|
||||
public final static byte sid = 0x11;
|
||||
|
||||
@ -37,6 +36,10 @@ public class RangePtg extends OperationPtg
|
||||
// No contents
|
||||
}
|
||||
|
||||
public final boolean isBaseToken() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public int getSize()
|
||||
{
|
||||
|
@ -35,8 +35,7 @@ import org.apache.poi.util.LittleEndian;
|
||||
* @author Jason Height (jheight at chariot dot net dot au)
|
||||
* @version 1.0-pre
|
||||
*/
|
||||
|
||||
public class Ref3DPtg extends Ptg {
|
||||
public class Ref3DPtg extends OperandPtg {
|
||||
public final static byte sid = 0x3a;
|
||||
private final static int SIZE = 7; // 6 + 1 for Ptg
|
||||
private short field_1_index_extern_sheet;
|
||||
|
@ -28,9 +28,8 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
* RefError - handles deleted cell reference
|
||||
* @author Jason Height (jheight at chariot dot net dot au)
|
||||
*/
|
||||
public final class RefErrorPtg extends OperandPtg {
|
||||
|
||||
public class RefErrorPtg extends Ptg
|
||||
{
|
||||
private final static int SIZE = 5;
|
||||
public final static byte sid = 0x2a;
|
||||
private int field_1_reserved;
|
||||
|
@ -30,14 +30,14 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
* @author Andrew C. Oliver (acoliver@apache.org)
|
||||
* @author Jason Height (jheight at chariot dot net dot au)
|
||||
*/
|
||||
public class ReferencePtg extends Ptg {
|
||||
public class ReferencePtg extends OperandPtg {
|
||||
/**
|
||||
* TODO - (May-2008) fix subclasses of ReferencePtg 'RefN~' which are used in shared formulas.
|
||||
* (See bugzilla 44921)
|
||||
* The 'RefN~' instances do not work properly, and are expected to be converted by
|
||||
* SharedFormulaRecord.convertSharedFormulas().
|
||||
* This conversion currently does not take place for formulas of named ranges, conditional
|
||||
* format rules and data validation rules.
|
||||
* The 'RefN~' instances do not work properly, and are expected to be converted by
|
||||
* SharedFormulaRecord.convertSharedFormulas().
|
||||
* This conversion currently does not take place for formulas of named ranges, conditional
|
||||
* format rules and data validation rules.
|
||||
* Furthermore, conversion is probably not appropriate in those instances.
|
||||
*/
|
||||
protected final RuntimeException notImplemented() {
|
||||
@ -46,14 +46,14 @@ public class ReferencePtg extends Ptg {
|
||||
|
||||
private final static int SIZE = 5;
|
||||
public final static byte sid = 0x24;
|
||||
private final static int MAX_ROW_NUMBER = 65536;
|
||||
private final static int MAX_ROW_NUMBER = 65536;
|
||||
|
||||
/** The row index - zero based unsigned 16 bit value */
|
||||
private int field_1_row;
|
||||
/** Field 2
|
||||
* - lower 8 bits is the zero based unsigned byte column index
|
||||
/** Field 2
|
||||
* - lower 8 bits is the zero based unsigned byte column index
|
||||
* - bit 16 - isRowRelative
|
||||
* - bit 15 - isColumnRelative
|
||||
* - bit 15 - isColumnRelative
|
||||
*/
|
||||
private int field_2_col;
|
||||
private static final BitField rowRelative = BitFieldFactory.getInstance(0x8000);
|
||||
@ -63,9 +63,9 @@ public class ReferencePtg extends Ptg {
|
||||
protected ReferencePtg() {
|
||||
//Required for clone methods
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Takes in a String represnetation of a cell reference and fills out the
|
||||
* Takes in a String represnetation of a cell reference and fills out the
|
||||
* numeric fields.
|
||||
*/
|
||||
public ReferencePtg(String cellref) {
|
||||
@ -75,13 +75,13 @@ public class ReferencePtg extends Ptg {
|
||||
setColRelative(!c.isColAbsolute());
|
||||
setRowRelative(!c.isRowAbsolute());
|
||||
}
|
||||
|
||||
|
||||
public ReferencePtg(int row, int column, boolean isRowRelative, boolean isColumnRelative) {
|
||||
setRow(row);
|
||||
setColumn(column);
|
||||
setRowRelative(isRowRelative);
|
||||
setColRelative(isColumnRelative);
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates new ValueReferencePtg */
|
||||
|
||||
@ -90,22 +90,19 @@ public class ReferencePtg extends Ptg {
|
||||
field_1_row = in.readUShort();
|
||||
field_2_col = in.readUShort();
|
||||
}
|
||||
|
||||
|
||||
public String getRefPtgName() {
|
||||
return "ReferencePtg";
|
||||
}
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
StringBuffer buffer = new StringBuffer("[");
|
||||
buffer.append(getRefPtgName());
|
||||
buffer.append("]\n");
|
||||
|
||||
buffer.append("row = ").append(getRow()).append("\n");
|
||||
buffer.append("col = ").append(getColumn()).append("\n");
|
||||
buffer.append("rowrelative = ").append(isRowRelative()).append("\n");
|
||||
buffer.append("colrelative = ").append(isColRelative()).append("\n");
|
||||
return buffer.toString();
|
||||
public String toString() {
|
||||
CellReference cr = new CellReference(getRow(), getColumn(), !isRowRelative(),!isColRelative());
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append(getClass().getName());
|
||||
sb.append(" [");
|
||||
sb.append(cr.formatAsString());
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public void writeBytes(byte [] array, int offset)
|
||||
@ -147,16 +144,16 @@ public class ReferencePtg extends Ptg {
|
||||
{
|
||||
return rowRelative.isSet(field_2_col);
|
||||
}
|
||||
|
||||
|
||||
public void setRowRelative(boolean rel) {
|
||||
field_2_col=rowRelative.setBoolean(field_2_col,rel);
|
||||
}
|
||||
|
||||
|
||||
public boolean isColRelative()
|
||||
{
|
||||
return colRelative.isSet(field_2_col);
|
||||
}
|
||||
|
||||
|
||||
public void setColRelative(boolean rel) {
|
||||
field_2_col=colRelative.setBoolean(field_2_col,rel);
|
||||
}
|
||||
@ -193,11 +190,11 @@ public class ReferencePtg extends Ptg {
|
||||
//TODO -- should we store a cellreference instance in this ptg?? but .. memory is an issue, i believe!
|
||||
return (new CellReference(getRowAsInt(),getColumn(),!isRowRelative(),!isColRelative())).formatAsString();
|
||||
}
|
||||
|
||||
|
||||
public byte getDefaultOperandClass() {
|
||||
return Ptg.CLASS_REF;
|
||||
}
|
||||
|
||||
|
||||
public Object clone() {
|
||||
ReferencePtg ptg = new ReferencePtg();
|
||||
ptg.field_1_row = field_1_row;
|
||||
|
@ -0,0 +1,31 @@
|
||||
/* ====================================================================
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author Josh Micich
|
||||
*/
|
||||
abstract class ScalarConstantPtg extends Ptg {
|
||||
public boolean isBaseToken() {
|
||||
return true;
|
||||
}
|
||||
public final byte getDefaultOperandClass() {
|
||||
return Ptg.CLASS_VALUE;
|
||||
}
|
||||
|
||||
}
|
@ -31,7 +31,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
* @author Jason Height (jheight at chariot dot net dot au)
|
||||
* @author Bernard Chesnoy
|
||||
*/
|
||||
public final class StringPtg extends Ptg {
|
||||
public final class StringPtg extends ScalarConstantPtg {
|
||||
public final static int SIZE = 9;
|
||||
public final static byte sid = 0x17;
|
||||
private static final BitField fHighByte = BitFieldFactory.getInstance(0x01);
|
||||
@ -124,10 +124,6 @@ public final class StringPtg extends Ptg {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public byte getDefaultOperandClass() {
|
||||
return Ptg.CLASS_VALUE;
|
||||
}
|
||||
|
||||
public Object clone() {
|
||||
StringPtg ptg = new StringPtg();
|
||||
ptg.field_1_length = field_1_length;
|
||||
|
@ -26,10 +26,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
* @author andy
|
||||
* @author Jason Height (jheight at chariot dot net dot au)
|
||||
*/
|
||||
|
||||
public class SubtractPtg
|
||||
extends OperationPtg
|
||||
{
|
||||
public final class SubtractPtg extends ValueOperatorPtg {
|
||||
public final static int SIZE = 1;
|
||||
public final static byte sid = 0x04;
|
||||
|
||||
|
@ -28,8 +28,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
* @author Avik Sengupta
|
||||
*/
|
||||
|
||||
public class UnaryMinusPtg extends OperationPtg
|
||||
{
|
||||
public final class UnaryMinusPtg extends ValueOperatorPtg {
|
||||
public final static int SIZE = 1;
|
||||
public final static byte sid = 0x13;
|
||||
|
||||
@ -82,8 +81,6 @@ public class UnaryMinusPtg extends OperationPtg
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
public byte getDefaultOperandClass() {return Ptg.CLASS_VALUE;}
|
||||
|
||||
public Object clone() {
|
||||
return new UnaryPlusPtg();
|
||||
}
|
||||
|
@ -28,8 +28,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
* @author Avik Sengupta
|
||||
*/
|
||||
|
||||
public class UnaryPlusPtg extends OperationPtg
|
||||
{
|
||||
public final class UnaryPlusPtg extends ValueOperatorPtg {
|
||||
public final static int SIZE = 1;
|
||||
public final static byte sid = 0x12;
|
||||
|
||||
@ -82,8 +81,6 @@ public class UnaryPlusPtg extends OperationPtg
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
public byte getDefaultOperandClass() {return Ptg.CLASS_VALUE;}
|
||||
|
||||
public Object clone() {
|
||||
return new UnaryPlusPtg();
|
||||
}
|
||||
|
@ -23,8 +23,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
/**
|
||||
* @author Glen Stampoultzis (glens at apache.org)
|
||||
*/
|
||||
public class UnionPtg extends OperationPtg
|
||||
{
|
||||
public final class UnionPtg extends OperationPtg {
|
||||
public final static byte sid = 0x10;
|
||||
|
||||
|
||||
@ -37,6 +36,9 @@ public class UnionPtg extends OperationPtg
|
||||
// doesn't need anything
|
||||
}
|
||||
|
||||
public final boolean isBaseToken() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getSize()
|
||||
{
|
||||
|
@ -24,10 +24,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
* @author andy
|
||||
* @author Jason Height (jheight at chariot dot net dot au)
|
||||
*/
|
||||
|
||||
public class UnknownPtg
|
||||
extends Ptg
|
||||
{
|
||||
public class UnknownPtg extends Ptg {
|
||||
private short size = 1;
|
||||
|
||||
/** Creates new UnknownPtg */
|
||||
@ -36,12 +33,13 @@ public class UnknownPtg
|
||||
{
|
||||
}
|
||||
|
||||
public UnknownPtg(RecordInputStream in)
|
||||
{
|
||||
|
||||
public UnknownPtg(RecordInputStream in) {
|
||||
// doesn't need anything
|
||||
}
|
||||
|
||||
public boolean isBaseToken() {
|
||||
return true;
|
||||
}
|
||||
public void writeBytes(byte [] array, int offset)
|
||||
{
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
* Common superclass of all value operators.
|
||||
* Subclasses include all unary and binary operators except for the reference operators (IntersectionPtg, RangePtg, UnionPtg)
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public abstract class ValueOperatorPtg extends OperationPtg {
|
||||
|
||||
/**
|
||||
* All Operator <tt>Ptg</tt>s are base tokens (i.e. are not RVA classifed)
|
||||
*/
|
||||
public final boolean isBaseToken() {
|
||||
return true;
|
||||
}
|
||||
public final byte getDefaultOperandClass() {
|
||||
return Ptg.CLASS_VALUE;
|
||||
}
|
||||
}
|
@ -55,36 +55,72 @@ public class HSSFObjectData
|
||||
this.record = record;
|
||||
this.poifs = poifs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the OLE2 Class Name of the object
|
||||
*/
|
||||
public String getOLE2ClassName() {
|
||||
EmbeddedObjectRefSubRecord subRecord = findObjectRecord();
|
||||
return subRecord.field_5_ole_classname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the object data.
|
||||
* Gets the object data. Only call for ones that have
|
||||
* data though. See {@link #hasDirectoryEntry()}
|
||||
*
|
||||
* @return the object data as an OLE2 directory.
|
||||
* @throws IOException if there was an error reading the data.
|
||||
*/
|
||||
public DirectoryEntry getDirectory() throws IOException
|
||||
{
|
||||
Iterator subRecordIter = record.getSubRecords().iterator();
|
||||
while (subRecordIter.hasNext())
|
||||
{
|
||||
Object subRecord = subRecordIter.next();
|
||||
if (subRecord instanceof EmbeddedObjectRefSubRecord)
|
||||
{
|
||||
int streamId = ((EmbeddedObjectRefSubRecord) subRecord).getStreamId();
|
||||
String streamName = "MBD" + HexDump.toHex(streamId);
|
||||
public DirectoryEntry getDirectory() throws IOException {
|
||||
EmbeddedObjectRefSubRecord subRecord = findObjectRecord();
|
||||
|
||||
Entry entry = poifs.getRoot().getEntry(streamName);
|
||||
if (entry instanceof DirectoryEntry)
|
||||
{
|
||||
return (DirectoryEntry) entry;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IOException("Stream " + streamName + " was not an OLE2 directory");
|
||||
}
|
||||
int streamId = ((EmbeddedObjectRefSubRecord) subRecord).getStreamId();
|
||||
String streamName = "MBD" + HexDump.toHex(streamId);
|
||||
|
||||
Entry entry = poifs.getRoot().getEntry(streamName);
|
||||
if (entry instanceof DirectoryEntry) {
|
||||
return (DirectoryEntry) entry;
|
||||
} else {
|
||||
throw new IOException("Stream " + streamName + " was not an OLE2 directory");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data portion, for an ObjectData
|
||||
* that doesn't have an associated POIFS Directory
|
||||
* Entry
|
||||
*/
|
||||
public byte[] getObjectData() {
|
||||
EmbeddedObjectRefSubRecord subRecord = findObjectRecord();
|
||||
return subRecord.remainingBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this ObjectData have an associated POIFS
|
||||
* Directory Entry?
|
||||
* (Not all do, those that don't have a data portion)
|
||||
*/
|
||||
public boolean hasDirectoryEntry() {
|
||||
EmbeddedObjectRefSubRecord subRecord = findObjectRecord();
|
||||
|
||||
// Field 6 tells you
|
||||
return (subRecord.field_6_stream_id != 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the EmbeddedObjectRefSubRecord, or throws an
|
||||
* Exception if there wasn't one
|
||||
*/
|
||||
protected EmbeddedObjectRefSubRecord findObjectRecord() {
|
||||
Iterator subRecordIter = record.getSubRecords().iterator();
|
||||
|
||||
while (subRecordIter.hasNext()) {
|
||||
Object subRecord = subRecordIter.next();
|
||||
if (subRecord instanceof EmbeddedObjectRefSubRecord) {
|
||||
return (EmbeddedObjectRefSubRecord)subRecord;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
throw new IllegalStateException("Object data does not contain a reference to an embedded object OLE2 directory");
|
||||
}
|
||||
}
|
||||
|
@ -220,9 +220,13 @@ public class DateUtil
|
||||
// switching stuff, which we can ignore
|
||||
fs = fs.replaceAll(";@", "");
|
||||
|
||||
// If it starts with [$-...], then it is a date, but
|
||||
// If it starts with [$-...], then could be a date, but
|
||||
// who knows what that starting bit is all about
|
||||
fs = fs.replaceAll("\\[\\$\\-.*?\\]", "");
|
||||
fs = fs.replaceAll("^\\[\\$\\-.*?\\]", "");
|
||||
|
||||
// If it starts with something like [Black] or [Yellow],
|
||||
// then it could be a date
|
||||
fs = fs.replaceAll("^\\[[a-zA-Z]+\\]", "");
|
||||
|
||||
// Otherwise, check it's only made up, in any case, of:
|
||||
// y m d h s - / , . :
|
||||
|
@ -330,31 +330,27 @@ public class FormulaEvaluator {
|
||||
}
|
||||
private static ValueEval evaluateCell(Workbook workbook, Sheet sheet,
|
||||
int srcRowNum, short srcColNum, String cellFormulaText) {
|
||||
|
||||
FormulaParser parser =
|
||||
new FormulaParser(cellFormulaText, workbook);
|
||||
|
||||
parser.parse();
|
||||
Ptg[] ptgs = parser.getRPNPtg();
|
||||
// -- parsing over --
|
||||
|
||||
Ptg[] ptgs = FormulaParser.parse(cellFormulaText, workbook);
|
||||
|
||||
Stack stack = new Stack();
|
||||
for (int i = 0, iSize = ptgs.length; i < iSize; i++) {
|
||||
|
||||
// since we don't know how to handle these yet :(
|
||||
Ptg ptg = ptgs[i];
|
||||
if (ptg instanceof ControlPtg) { continue; }
|
||||
if (ptg instanceof ControlPtg) {
|
||||
// skip Parentheses, Attr, etc
|
||||
continue;
|
||||
}
|
||||
if (ptg instanceof MemErrPtg) { continue; }
|
||||
if (ptg instanceof MissingArgPtg) { continue; }
|
||||
if (ptg instanceof NamePtg) {
|
||||
// named ranges, macro functions
|
||||
// named ranges, macro functions
|
||||
NamePtg namePtg = (NamePtg) ptg;
|
||||
stack.push(new NameEval(namePtg.getIndex()));
|
||||
continue;
|
||||
}
|
||||
if (ptg instanceof NameXPtg) {
|
||||
// TODO - external functions
|
||||
// TODO - external functions
|
||||
continue;
|
||||
}
|
||||
if (ptg instanceof UnknownPtg) { continue; }
|
||||
@ -362,9 +358,6 @@ public class FormulaEvaluator {
|
||||
if (ptg instanceof OperationPtg) {
|
||||
OperationPtg optg = (OperationPtg) ptg;
|
||||
|
||||
// parens can be ignored since we have RPN tokens
|
||||
if (optg instanceof ParenthesisPtg) { continue; }
|
||||
if (optg instanceof AttrPtg) { continue; }
|
||||
if (optg instanceof UnionPtg) { continue; }
|
||||
|
||||
OperationEval operation = OperationEvaluatorFactory.create(optg);
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
# Created by (org.apache.poi.hssf.record.formula.function.ExcelFileFormatDocFunctionExtractor)
|
||||
# from source file 'excelfileformat.odt' (size=356107, md5=0x8f789cb6e75594caf068f8e193004ef4)
|
||||
# ! + some manual edits !
|
||||
#
|
||||
#Columns: (index, name, minParams, maxParams, returnClass, paramClasses, isVolatile, hasFootnote )
|
||||
|
||||
@ -78,8 +79,8 @@
|
||||
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
|
||||
61 MIRR 3 3 V A V V
|
||||
62 IRR 1 2 V A V
|
||||
63 RAND 0 0 V - x
|
||||
64 MATCH 2 3 V V R R
|
||||
65 DATE 3 3 V V V V
|
||||
@ -93,8 +94,8 @@
|
||||
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
|
||||
76 ROWS 1 1 V A
|
||||
77 COLUMNS 1 1 V A
|
||||
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
|
||||
|
@ -213,6 +213,23 @@ public class HSSFChart
|
||||
charts.toArray( new HSSFChart[charts.size()] );
|
||||
}
|
||||
|
||||
/** Get the X offset of the chart */
|
||||
public int getChartX() { return chartRecord.getX(); }
|
||||
/** Get the Y offset of the chart */
|
||||
public int getChartY() { return chartRecord.getY(); }
|
||||
/** Get the width of the chart. {@link ChartRecord} */
|
||||
public int getChartWidth() { return chartRecord.getWidth(); }
|
||||
/** Get the height of the chart. {@link ChartRecord} */
|
||||
public int getChartHeight() { return chartRecord.getHeight(); }
|
||||
|
||||
/** Sets the X offset of the chart */
|
||||
public void setChartX(int x) { chartRecord.setX(x); }
|
||||
/** Sets the Y offset of the chart */
|
||||
public void setChartY(int y) { chartRecord.setY(y); }
|
||||
/** Sets the width of the chart. {@link ChartRecord} */
|
||||
public void setChartWidth(int width) { chartRecord.setWidth(width); }
|
||||
/** Sets the height of the chart. {@link ChartRecord} */
|
||||
public void setChartHeight(int height) { chartRecord.setHeight(height); }
|
||||
|
||||
/**
|
||||
* Returns the series of the chart
|
||||
|
@ -53,6 +53,12 @@ public class TestHSSFChart extends TestCase {
|
||||
assertEquals("1st Column", charts[0].getSeries()[0].getSeriesTitle());
|
||||
assertEquals("2nd Column", charts[0].getSeries()[1].getSeriesTitle());
|
||||
assertEquals(null, charts[0].getChartTitle());
|
||||
|
||||
// Check x, y, width, height
|
||||
assertEquals(0, charts[0].getChartX());
|
||||
assertEquals(0, charts[0].getChartY());
|
||||
assertEquals(26492928, charts[0].getChartWidth());
|
||||
assertEquals(15040512, charts[0].getChartHeight());
|
||||
}
|
||||
|
||||
public void testTwoCharts() throws Exception {
|
||||
|
BIN
src/testcases/org/apache/poi/hssf/data/ex42564-elementOrder.xls
Normal file
BIN
src/testcases/org/apache/poi/hssf/data/ex42564-elementOrder.xls
Normal file
Binary file not shown.
BIN
src/testcases/org/apache/poi/hssf/data/testRVA.xls
Normal file
BIN
src/testcases/org/apache/poi/hssf/data/testRVA.xls
Normal file
Binary file not shown.
@ -33,6 +33,9 @@ public final class AllModelTests {
|
||||
result.addTestSuite(TestDrawingManager2.class);
|
||||
result.addTestSuite(TestFormulaParser.class);
|
||||
result.addTestSuite(TestFormulaParserEval.class);
|
||||
result.addTestSuite(TestFormulaParserIf.class);
|
||||
result.addTestSuite(TestOperandClassTransformer.class);
|
||||
result.addTestSuite(TestRVA.class);
|
||||
result.addTestSuite(TestSheet.class);
|
||||
result.addTestSuite(TestSheetAdditional.class);
|
||||
return result;
|
||||
|
@ -33,12 +33,9 @@ import org.apache.poi.hssf.record.formula.ErrPtg;
|
||||
import org.apache.poi.hssf.record.formula.FuncPtg;
|
||||
import org.apache.poi.hssf.record.formula.FuncVarPtg;
|
||||
import org.apache.poi.hssf.record.formula.IntPtg;
|
||||
import org.apache.poi.hssf.record.formula.LessEqualPtg;
|
||||
import org.apache.poi.hssf.record.formula.LessThanPtg;
|
||||
import org.apache.poi.hssf.record.formula.MissingArgPtg;
|
||||
import org.apache.poi.hssf.record.formula.MultiplyPtg;
|
||||
import org.apache.poi.hssf.record.formula.NamePtg;
|
||||
import org.apache.poi.hssf.record.formula.NotEqualPtg;
|
||||
import org.apache.poi.hssf.record.formula.NumberPtg;
|
||||
import org.apache.poi.hssf.record.formula.PercentPtg;
|
||||
import org.apache.poi.hssf.record.formula.PowerPtg;
|
||||
@ -62,10 +59,8 @@ public final class TestFormulaParser extends TestCase {
|
||||
/**
|
||||
* @return parsed token array already confirmed not <code>null</code>
|
||||
*/
|
||||
private static Ptg[] parseFormula(String s) {
|
||||
FormulaParser fp = new FormulaParser(s, null);
|
||||
fp.parse();
|
||||
Ptg[] result = fp.getRPNPtg();
|
||||
/* package */ static Ptg[] parseFormula(String formula) {
|
||||
Ptg[] result = FormulaParser.parse(formula, null);
|
||||
assertNotNull("Ptg array should not be null", result);
|
||||
return result;
|
||||
}
|
||||
@ -105,83 +100,6 @@ public final class TestFormulaParser extends TestCase {
|
||||
assertEquals(true, flag.getValue());
|
||||
}
|
||||
|
||||
public void testYN() {
|
||||
Ptg[] ptgs = parseFormula("IF(TRUE,\"Y\",\"N\")");
|
||||
assertEquals(7, ptgs.length);
|
||||
|
||||
BoolPtg flag = (BoolPtg) ptgs[0];
|
||||
AttrPtg funif = (AttrPtg) ptgs[1];
|
||||
StringPtg y = (StringPtg) ptgs[2];
|
||||
AttrPtg goto1 = (AttrPtg) ptgs[3];
|
||||
StringPtg n = (StringPtg) ptgs[4];
|
||||
|
||||
|
||||
assertEquals(true, flag.getValue());
|
||||
assertEquals("Y", y.getValue());
|
||||
assertEquals("N", n.getValue());
|
||||
assertEquals("IF", funif.toFormulaString((HSSFWorkbook) null));
|
||||
assertTrue("Goto ptg exists", goto1.isGoto());
|
||||
}
|
||||
|
||||
public void testSimpleIf() {
|
||||
String formula = "IF(1=1,0,1)";
|
||||
|
||||
Class[] expectedClasses = {
|
||||
IntPtg.class,
|
||||
IntPtg.class,
|
||||
EqualPtg.class,
|
||||
AttrPtg.class,
|
||||
IntPtg.class,
|
||||
AttrPtg.class,
|
||||
IntPtg.class,
|
||||
AttrPtg.class,
|
||||
FuncVarPtg.class,
|
||||
};
|
||||
confirmTokenClasses(formula, expectedClasses);
|
||||
|
||||
Ptg[] ptgs = parseFormula(formula);
|
||||
|
||||
AttrPtg ifPtg = (AttrPtg) ptgs[3];
|
||||
AttrPtg ptgGoto= (AttrPtg) ptgs[5];
|
||||
assertEquals("Goto 1 Length", 10, ptgGoto.getData());
|
||||
|
||||
AttrPtg ptgGoto2 = (AttrPtg) ptgs[7];
|
||||
assertEquals("Goto 2 Length", 3, ptgGoto2.getData());
|
||||
assertEquals("If FALSE offset", 7, ifPtg.getData());
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the ptgs are generated properly with two functions embedded
|
||||
*
|
||||
*/
|
||||
public void testNestedFunctionIf() {
|
||||
Ptg[] ptgs = parseFormula("IF(A1=B1,AVERAGE(A1:B1),AVERAGE(A2:B2))");
|
||||
assertEquals(11, ptgs.length);
|
||||
|
||||
assertTrue("IF Attr set correctly", (ptgs[3] instanceof AttrPtg));
|
||||
AttrPtg ifFunc = (AttrPtg)ptgs[3];
|
||||
assertTrue("It is not an if", ifFunc.isOptimizedIf());
|
||||
|
||||
assertTrue("Average Function set correctly", (ptgs[5] instanceof FuncVarPtg));
|
||||
}
|
||||
|
||||
public void testIfSingleCondition(){
|
||||
Ptg[] ptgs = parseFormula("IF(1=1,10)");
|
||||
assertEquals(7, ptgs.length);
|
||||
|
||||
assertTrue("IF Attr set correctly", (ptgs[3] instanceof AttrPtg));
|
||||
AttrPtg ifFunc = (AttrPtg)ptgs[3];
|
||||
assertTrue("It is not an if", ifFunc.isOptimizedIf());
|
||||
|
||||
assertTrue("Single Value is not an IntPtg", (ptgs[4] instanceof IntPtg));
|
||||
IntPtg intPtg = (IntPtg)ptgs[4];
|
||||
assertEquals("Result", (short)10, intPtg.getValue());
|
||||
|
||||
assertTrue("Ptg is not a Variable Function", (ptgs[6] instanceof FuncVarPtg));
|
||||
FuncVarPtg funcPtg = (FuncVarPtg)ptgs[6];
|
||||
assertEquals("Arguments", 2, funcPtg.getNumberOfOperands());
|
||||
}
|
||||
|
||||
public void testSumIf() {
|
||||
Ptg[] ptgs = parseFormula("SUMIF(A1:A5,\">4000\",B1:B5)");
|
||||
assertEquals(4, ptgs.length);
|
||||
@ -203,33 +121,9 @@ public final class TestFormulaParser extends TestCase {
|
||||
//the PTG order isn't 100% correct but it still works - dmui
|
||||
}
|
||||
|
||||
public void testSimpleLogical() {
|
||||
Ptg[] ptgs = parseFormula("IF(A1<A2,B1,B2)");
|
||||
assertEquals(9, ptgs.length);
|
||||
assertEquals("3rd Ptg is less than", LessThanPtg.class, ptgs[2].getClass());
|
||||
}
|
||||
|
||||
public void testParenIf() {
|
||||
Ptg[] ptgs = parseFormula("IF((A1+A2)<=3,\"yes\",\"no\")");
|
||||
assertEquals(12, ptgs.length);
|
||||
assertEquals("6th Ptg is less than equal",LessEqualPtg.class,ptgs[5].getClass());
|
||||
assertEquals("11th Ptg is not a goto (Attr) ptg",AttrPtg.class,ptgs[10].getClass());
|
||||
}
|
||||
|
||||
public void testEmbeddedIf() {
|
||||
Ptg[] ptgs = parseFormula("IF(3>=1,\"*\",IF(4<>1,\"first\",\"second\"))");
|
||||
assertEquals(17, ptgs.length);
|
||||
|
||||
assertEquals("6th Ptg is not a goto (Attr) ptg",AttrPtg.class,ptgs[5].getClass());
|
||||
assertEquals("9th Ptg is not a not equal ptg",NotEqualPtg.class,ptgs[8].getClass());
|
||||
assertEquals("15th Ptg is not the inner IF variable function ptg",FuncVarPtg.class,ptgs[14].getClass());
|
||||
}
|
||||
|
||||
public void testMacroFunction() {
|
||||
HSSFWorkbook w = new HSSFWorkbook();
|
||||
FormulaParser fp = new FormulaParser("FOO()", w);
|
||||
fp.parse();
|
||||
Ptg[] ptg = fp.getRPNPtg();
|
||||
Ptg[] ptg = FormulaParser.parse("FOO()", w);
|
||||
|
||||
// the name gets encoded as the first arg
|
||||
NamePtg tname = (NamePtg) ptg[0];
|
||||
@ -597,7 +491,7 @@ public final class TestFormulaParser extends TestCase {
|
||||
confirmTokenClasses("2^200%", expClss);
|
||||
}
|
||||
|
||||
private static void confirmTokenClasses(String formula, Class[] expectedClasses) {
|
||||
/* package */ static Ptg[] confirmTokenClasses(String formula, Class[] expectedClasses) {
|
||||
Ptg[] ptgs = parseFormula(formula);
|
||||
assertEquals(expectedClasses.length, ptgs.length);
|
||||
for (int i = 0; i < expectedClasses.length; i++) {
|
||||
@ -607,6 +501,7 @@ public final class TestFormulaParser extends TestCase {
|
||||
+ ptgs[i].getClass().getName() + ")");
|
||||
}
|
||||
}
|
||||
return ptgs;
|
||||
}
|
||||
|
||||
public void testPower() {
|
||||
@ -644,8 +539,16 @@ public final class TestFormulaParser extends TestCase {
|
||||
|
||||
Class[] expClss;
|
||||
|
||||
expClss = new Class[] { ReferencePtg.class, MissingArgPtg.class, ReferencePtg.class,
|
||||
FuncVarPtg.class, };
|
||||
expClss = new Class[] {
|
||||
ReferencePtg.class,
|
||||
AttrPtg.class, // tAttrIf
|
||||
MissingArgPtg.class,
|
||||
AttrPtg.class, // tAttrSkip
|
||||
ReferencePtg.class,
|
||||
AttrPtg.class, // tAttrSkip
|
||||
FuncVarPtg.class,
|
||||
};
|
||||
|
||||
confirmTokenClasses("if(A1, ,C1)", expClss);
|
||||
|
||||
expClss = new Class[] { MissingArgPtg.class, AreaPtg.class, MissingArgPtg.class,
|
||||
@ -814,7 +717,7 @@ public final class TestFormulaParser extends TestCase {
|
||||
fail("Expected exception was not thrown");
|
||||
} catch (IllegalStateException e) {
|
||||
// expected during successful test
|
||||
assertTrue(e.getMessage().startsWith("Too few arguments suppled to operation token"));
|
||||
assertTrue(e.getMessage().startsWith("Too few arguments supplied to operation"));
|
||||
}
|
||||
}
|
||||
/**
|
||||
|
239
src/testcases/org/apache/poi/hssf/model/TestFormulaParserIf.java
Normal file
239
src/testcases/org/apache/poi/hssf/model/TestFormulaParserIf.java
Normal file
@ -0,0 +1,239 @@
|
||||
/* ====================================================================
|
||||
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.model;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.AddPtg;
|
||||
import org.apache.poi.hssf.record.formula.AttrPtg;
|
||||
import org.apache.poi.hssf.record.formula.BoolPtg;
|
||||
import org.apache.poi.hssf.record.formula.FuncPtg;
|
||||
import org.apache.poi.hssf.record.formula.FuncVarPtg;
|
||||
import org.apache.poi.hssf.record.formula.IntPtg;
|
||||
import org.apache.poi.hssf.record.formula.LessEqualPtg;
|
||||
import org.apache.poi.hssf.record.formula.LessThanPtg;
|
||||
import org.apache.poi.hssf.record.formula.MultiplyPtg;
|
||||
import org.apache.poi.hssf.record.formula.NotEqualPtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.hssf.record.formula.ReferencePtg;
|
||||
import org.apache.poi.hssf.record.formula.StringPtg;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
|
||||
/**
|
||||
* Tests <tt>FormulaParser</tt> specifically with respect to IF() functions
|
||||
*/
|
||||
public final class TestFormulaParserIf extends TestCase {
|
||||
private static Ptg[] parseFormula(String formula) {
|
||||
return TestFormulaParser.parseFormula(formula);
|
||||
}
|
||||
|
||||
private static Ptg[] confirmTokenClasses(String formula, Class[] expectedClasses) {
|
||||
return TestFormulaParser.confirmTokenClasses(formula, expectedClasses);
|
||||
}
|
||||
|
||||
private static void confirmAttrData(Ptg[] ptgs, int i, int expectedData) {
|
||||
Ptg ptg = ptgs[i];
|
||||
if (!(ptg instanceof AttrPtg)) {
|
||||
throw new AssertionFailedError("Token[" + i + "] was not AttrPtg as expected");
|
||||
}
|
||||
AttrPtg attrPtg = (AttrPtg) ptg;
|
||||
assertEquals(expectedData, attrPtg.getData());
|
||||
}
|
||||
|
||||
public void testSimpleIf() {
|
||||
|
||||
Class[] expClss;
|
||||
|
||||
expClss = new Class[] {
|
||||
ReferencePtg.class,
|
||||
AttrPtg.class, // tAttrIf
|
||||
IntPtg.class,
|
||||
AttrPtg.class, // tAttrSkip
|
||||
IntPtg.class,
|
||||
AttrPtg.class, // tAttrSkip
|
||||
FuncVarPtg.class,
|
||||
};
|
||||
|
||||
Ptg[] ptgs = confirmTokenClasses("if(A1,1,2)", expClss);
|
||||
|
||||
confirmAttrData(ptgs, 1, 7);
|
||||
confirmAttrData(ptgs, 3, 10);
|
||||
confirmAttrData(ptgs, 5, 3);
|
||||
}
|
||||
|
||||
public void testSimpleIfNoFalseParam() {
|
||||
|
||||
Class[] expClss;
|
||||
|
||||
expClss = new Class[] {
|
||||
ReferencePtg.class,
|
||||
AttrPtg.class, // tAttrIf
|
||||
ReferencePtg.class,
|
||||
AttrPtg.class, // tAttrSkip
|
||||
FuncVarPtg.class,
|
||||
};
|
||||
|
||||
Ptg[] ptgs = confirmTokenClasses("if(A1,B1)", expClss);
|
||||
|
||||
confirmAttrData(ptgs, 1, 9);
|
||||
confirmAttrData(ptgs, 3, 3);
|
||||
}
|
||||
|
||||
public void testIfWithLargeParams() {
|
||||
|
||||
Class[] expClss;
|
||||
|
||||
expClss = new Class[] {
|
||||
ReferencePtg.class,
|
||||
AttrPtg.class, // tAttrIf
|
||||
|
||||
ReferencePtg.class,
|
||||
IntPtg.class,
|
||||
MultiplyPtg.class,
|
||||
ReferencePtg.class,
|
||||
IntPtg.class,
|
||||
AddPtg.class,
|
||||
FuncPtg.class,
|
||||
AttrPtg.class, // tAttrSkip
|
||||
|
||||
ReferencePtg.class,
|
||||
ReferencePtg.class,
|
||||
FuncPtg.class,
|
||||
|
||||
AttrPtg.class, // tAttrSkip
|
||||
FuncVarPtg.class,
|
||||
};
|
||||
|
||||
Ptg[] ptgs = confirmTokenClasses("if(A1,round(B1*100,C1+2),round(B1,C1))", expClss);
|
||||
|
||||
confirmAttrData(ptgs, 1, 25);
|
||||
confirmAttrData(ptgs, 9, 20);
|
||||
confirmAttrData(ptgs, 13, 3);
|
||||
}
|
||||
|
||||
public void testNestedIf() {
|
||||
|
||||
Class[] expClss;
|
||||
|
||||
expClss = new Class[] {
|
||||
|
||||
ReferencePtg.class,
|
||||
AttrPtg.class, // A tAttrIf
|
||||
ReferencePtg.class,
|
||||
AttrPtg.class, // B tAttrIf
|
||||
IntPtg.class,
|
||||
AttrPtg.class, // B tAttrSkip
|
||||
IntPtg.class,
|
||||
AttrPtg.class, // B tAttrSkip
|
||||
FuncVarPtg.class,
|
||||
AttrPtg.class, // A tAttrSkip
|
||||
ReferencePtg.class,
|
||||
AttrPtg.class, // C tAttrIf
|
||||
IntPtg.class,
|
||||
AttrPtg.class, // C tAttrSkip
|
||||
IntPtg.class,
|
||||
AttrPtg.class, // C tAttrSkip
|
||||
FuncVarPtg.class,
|
||||
AttrPtg.class, // A tAttrSkip
|
||||
FuncVarPtg.class,
|
||||
};
|
||||
|
||||
Ptg[] ptgs = confirmTokenClasses("if(A1,if(B1,1,2),if(C1,3,4))", expClss);
|
||||
confirmAttrData(ptgs, 1, 31);
|
||||
confirmAttrData(ptgs, 3, 7);
|
||||
confirmAttrData(ptgs, 5, 10);
|
||||
confirmAttrData(ptgs, 7, 3);
|
||||
confirmAttrData(ptgs, 9, 34);
|
||||
confirmAttrData(ptgs, 11, 7);
|
||||
confirmAttrData(ptgs, 13, 10);
|
||||
confirmAttrData(ptgs, 15, 3);
|
||||
confirmAttrData(ptgs, 17, 3);
|
||||
}
|
||||
|
||||
public void testEmbeddedIf() {
|
||||
Ptg[] ptgs = parseFormula("IF(3>=1,\"*\",IF(4<>1,\"first\",\"second\"))");
|
||||
assertEquals(17, ptgs.length);
|
||||
|
||||
assertEquals("6th Ptg is not a goto (Attr) ptg",AttrPtg.class,ptgs[5].getClass());
|
||||
assertEquals("9th Ptg is not a not equal ptg",NotEqualPtg.class,ptgs[8].getClass());
|
||||
assertEquals("15th Ptg is not the inner IF variable function ptg",FuncVarPtg.class,ptgs[14].getClass());
|
||||
}
|
||||
|
||||
|
||||
public void testSimpleLogical() {
|
||||
Ptg[] ptgs = parseFormula("IF(A1<A2,B1,B2)");
|
||||
assertEquals(9, ptgs.length);
|
||||
assertEquals("3rd Ptg is less than", LessThanPtg.class, ptgs[2].getClass());
|
||||
}
|
||||
|
||||
public void testParenIf() {
|
||||
Ptg[] ptgs = parseFormula("IF((A1+A2)<=3,\"yes\",\"no\")");
|
||||
assertEquals(12, ptgs.length);
|
||||
assertEquals("6th Ptg is less than equal",LessEqualPtg.class,ptgs[5].getClass());
|
||||
assertEquals("11th Ptg is not a goto (Attr) ptg",AttrPtg.class,ptgs[10].getClass());
|
||||
}
|
||||
public void testYN() {
|
||||
Ptg[] ptgs = parseFormula("IF(TRUE,\"Y\",\"N\")");
|
||||
assertEquals(7, ptgs.length);
|
||||
|
||||
BoolPtg flag = (BoolPtg) ptgs[0];
|
||||
AttrPtg funif = (AttrPtg) ptgs[1];
|
||||
StringPtg y = (StringPtg) ptgs[2];
|
||||
AttrPtg goto1 = (AttrPtg) ptgs[3];
|
||||
StringPtg n = (StringPtg) ptgs[4];
|
||||
|
||||
|
||||
assertEquals(true, flag.getValue());
|
||||
assertEquals("Y", y.getValue());
|
||||
assertEquals("N", n.getValue());
|
||||
assertEquals("IF", funif.toFormulaString((HSSFWorkbook) null));
|
||||
assertTrue("Goto ptg exists", goto1.isGoto());
|
||||
}
|
||||
/**
|
||||
* Make sure the ptgs are generated properly with two functions embedded
|
||||
*
|
||||
*/
|
||||
public void testNestedFunctionIf() {
|
||||
Ptg[] ptgs = parseFormula("IF(A1=B1,AVERAGE(A1:B1),AVERAGE(A2:B2))");
|
||||
assertEquals(11, ptgs.length);
|
||||
|
||||
assertTrue("IF Attr set correctly", (ptgs[3] instanceof AttrPtg));
|
||||
AttrPtg ifFunc = (AttrPtg)ptgs[3];
|
||||
assertTrue("It is not an if", ifFunc.isOptimizedIf());
|
||||
|
||||
assertTrue("Average Function set correctly", (ptgs[5] instanceof FuncVarPtg));
|
||||
}
|
||||
|
||||
public void testIfSingleCondition(){
|
||||
Ptg[] ptgs = parseFormula("IF(1=1,10)");
|
||||
assertEquals(7, ptgs.length);
|
||||
|
||||
assertTrue("IF Attr set correctly", (ptgs[3] instanceof AttrPtg));
|
||||
AttrPtg ifFunc = (AttrPtg)ptgs[3];
|
||||
assertTrue("It is not an if", ifFunc.isOptimizedIf());
|
||||
|
||||
assertTrue("Single Value is not an IntPtg", (ptgs[4] instanceof IntPtg));
|
||||
IntPtg intPtg = (IntPtg)ptgs[4];
|
||||
assertEquals("Result", (short)10, intPtg.getValue());
|
||||
|
||||
assertTrue("Ptg is not a Variable Function", (ptgs[6] instanceof FuncVarPtg));
|
||||
FuncVarPtg funcPtg = (FuncVarPtg)ptgs[6];
|
||||
assertEquals("Arguments", 2, funcPtg.getNumberOfOperands());
|
||||
}
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
/* ====================================================================
|
||||
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.model;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.AbstractFunctionPtg;
|
||||
import org.apache.poi.hssf.record.formula.FuncVarPtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
|
||||
/**
|
||||
* Tests specific formula examples in <tt>OperandClassTransformer</tt>.
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class TestOperandClassTransformer extends TestCase {
|
||||
|
||||
public void testMdeterm() {
|
||||
String formula = "MDETERM(ABS(A1))";
|
||||
Ptg[] ptgs = FormulaParser.parse(formula, null);
|
||||
|
||||
confirmTokenClass(ptgs, 0, Ptg.CLASS_ARRAY);
|
||||
confirmFuncClass(ptgs, 1, "ABS", Ptg.CLASS_ARRAY);
|
||||
confirmFuncClass(ptgs, 2, "MDETERM", Ptg.CLASS_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* In the example: <code>INDEX(PI(),1)</code>, Excel encodes PI() as 'array'. It is not clear
|
||||
* what rule justifies this. POI currently encodes it as 'value' which Excel(2007) seems to
|
||||
* tolerate. Changing the metadata for INDEX to have first parameter as 'array' class breaks
|
||||
* other formulas involving INDEX. It seems like a special case needs to be made. Perhaps an
|
||||
* important observation is that INDEX is one of very few functions that returns 'reference' type.
|
||||
*
|
||||
* This test has been added but disabled in order to document this issue.
|
||||
*/
|
||||
public void DISABLED_testIndexPi1() {
|
||||
String formula = "INDEX(PI(),1)";
|
||||
Ptg[] ptgs = FormulaParser.parse(formula, null);
|
||||
|
||||
confirmFuncClass(ptgs, 1, "PI", Ptg.CLASS_ARRAY); // fails as of POI 3.1
|
||||
confirmFuncClass(ptgs, 2, "INDEX", Ptg.CLASS_VALUE);
|
||||
}
|
||||
|
||||
public void testComplexIRR_bug45041() {
|
||||
String formula = "(1+IRR(SUMIF(A:A,ROW(INDIRECT(MIN(A:A)&\":\"&MAX(A:A))),B:B),0))^365-1";
|
||||
Ptg[] ptgs = FormulaParser.parse(formula, null);
|
||||
|
||||
FuncVarPtg rowFunc = (FuncVarPtg) ptgs[10];
|
||||
FuncVarPtg sumifFunc = (FuncVarPtg) ptgs[12];
|
||||
assertEquals("ROW", rowFunc.getName());
|
||||
assertEquals("SUMIF", sumifFunc.getName());
|
||||
|
||||
if (rowFunc.getPtgClass() == Ptg.CLASS_VALUE || sumifFunc.getPtgClass() == Ptg.CLASS_VALUE) {
|
||||
throw new AssertionFailedError("Identified bug 45041");
|
||||
}
|
||||
confirmTokenClass(ptgs, 1, Ptg.CLASS_REF);
|
||||
confirmTokenClass(ptgs, 2, Ptg.CLASS_REF);
|
||||
confirmFuncClass(ptgs, 3, "MIN", Ptg.CLASS_VALUE);
|
||||
confirmTokenClass(ptgs, 6, Ptg.CLASS_REF);
|
||||
confirmFuncClass(ptgs, 7, "MAX", Ptg.CLASS_VALUE);
|
||||
confirmFuncClass(ptgs, 9, "INDIRECT", Ptg.CLASS_REF);
|
||||
confirmFuncClass(ptgs, 10, "ROW", Ptg.CLASS_ARRAY);
|
||||
confirmTokenClass(ptgs, 11, Ptg.CLASS_REF);
|
||||
confirmFuncClass(ptgs, 12, "SUMIF", Ptg.CLASS_ARRAY);
|
||||
confirmFuncClass(ptgs, 14, "IRR", Ptg.CLASS_VALUE);
|
||||
}
|
||||
|
||||
private void confirmFuncClass(Ptg[] ptgs, int i, String expectedFunctionName, byte operandClass) {
|
||||
confirmTokenClass(ptgs, i, operandClass);
|
||||
AbstractFunctionPtg afp = (AbstractFunctionPtg) ptgs[i];
|
||||
assertEquals(expectedFunctionName, afp.getName());
|
||||
}
|
||||
|
||||
private void confirmTokenClass(Ptg[] ptgs, int i, byte operandClass) {
|
||||
Ptg ptg = ptgs[i];
|
||||
if (operandClass != ptg.getPtgClass()) {
|
||||
throw new AssertionFailedError("Wrong operand class for function ptg ("
|
||||
+ ptg.toString() + "). Expected " + getOperandClassName(operandClass)
|
||||
+ " but got " + getOperandClassName(ptg.getPtgClass()));
|
||||
}
|
||||
}
|
||||
|
||||
private static String getOperandClassName(byte ptgClass) {
|
||||
switch (ptgClass) {
|
||||
case Ptg.CLASS_REF:
|
||||
return "R";
|
||||
case Ptg.CLASS_VALUE:
|
||||
return "V";
|
||||
case Ptg.CLASS_ARRAY:
|
||||
return "A";
|
||||
}
|
||||
throw new RuntimeException("Unknown operand class (" + ptgClass + ")");
|
||||
}
|
||||
}
|
156
src/testcases/org/apache/poi/hssf/model/TestRVA.java
Normal file
156
src/testcases/org/apache/poi/hssf/model/TestRVA.java
Normal file
@ -0,0 +1,156 @@
|
||||
/* ====================================================================
|
||||
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.model;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hssf.HSSFTestDataSamples;
|
||||
import org.apache.poi.hssf.record.formula.AttrPtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.hssf.record.formula.ReferencePtg;
|
||||
import org.apache.poi.hssf.usermodel.FormulaExtractor;
|
||||
import org.apache.poi.hssf.usermodel.HSSFCell;
|
||||
import org.apache.poi.hssf.usermodel.HSSFRow;
|
||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
|
||||
/**
|
||||
* Tests 'operand class' transformation performed by
|
||||
* <tt>OperandClassTransformer</tt> by comparing its results with those
|
||||
* directly produced by Excel (in a sample spreadsheet).
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class TestRVA extends TestCase {
|
||||
|
||||
private static final String NEW_LINE = System.getProperty("line.separator");
|
||||
|
||||
public void testFormulas() {
|
||||
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("testRVA.xls");
|
||||
HSSFSheet sheet = wb.getSheetAt(0);
|
||||
|
||||
int countFailures = 0;
|
||||
int countErrors = 0;
|
||||
|
||||
int rowIx = 0;
|
||||
while (rowIx < 65535) {
|
||||
HSSFRow row = sheet.getRow(rowIx);
|
||||
if (row == null) {
|
||||
break;
|
||||
}
|
||||
HSSFCell cell = row.getCell(0);
|
||||
if (cell == null || cell.getCellType() == HSSFCell.CELL_TYPE_BLANK) {
|
||||
break;
|
||||
}
|
||||
String formula = cell.getCellFormula();
|
||||
try {
|
||||
confirmCell(cell, formula);
|
||||
} catch (AssertionFailedError e) {
|
||||
System.err.println("Problem with row[" + rowIx + "] formula '" + formula + "'");
|
||||
System.err.println(e.getMessage());
|
||||
countFailures++;
|
||||
} catch (RuntimeException e) {
|
||||
System.err.println("Problem with row[" + rowIx + "] formula '" + formula + "'");
|
||||
countErrors++;
|
||||
e.printStackTrace();
|
||||
}
|
||||
rowIx++;
|
||||
}
|
||||
if (countErrors + countFailures > 0) {
|
||||
String msg = "One or more RVA tests failed: countFailures=" + countFailures
|
||||
+ " countFailures=" + countErrors + ". See stderr for details.";
|
||||
throw new AssertionFailedError(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private void confirmCell(HSSFCell formulaCell, String formula) {
|
||||
Ptg[] excelPtgs = FormulaExtractor.getPtgs(formulaCell);
|
||||
Ptg[] poiPtgs = FormulaParser.parse(formula, null);
|
||||
int nExcelTokens = excelPtgs.length;
|
||||
int nPoiTokens = poiPtgs.length;
|
||||
if (nExcelTokens != nPoiTokens) {
|
||||
if (nExcelTokens == nPoiTokens + 1 && excelPtgs[0].getClass() == AttrPtg.class) {
|
||||
// compensate for missing tAttrVolatile, which belongs in any formula
|
||||
// involving OFFSET() et al. POI currently does not insert where required
|
||||
Ptg[] temp = new Ptg[nExcelTokens];
|
||||
temp[0] = excelPtgs[0];
|
||||
System.arraycopy(poiPtgs, 0, temp, 1, nPoiTokens);
|
||||
poiPtgs = temp;
|
||||
} else {
|
||||
throw new RuntimeException("Expected " + nExcelTokens + " tokens but got "
|
||||
+ nPoiTokens);
|
||||
}
|
||||
}
|
||||
boolean hasMismatch = false;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (int i = 0; i < nExcelTokens; i++) {
|
||||
Ptg poiPtg = poiPtgs[i];
|
||||
Ptg excelPtg = excelPtgs[i];
|
||||
if (!areTokenClassesSame(poiPtg, excelPtg)) {
|
||||
hasMismatch = true;
|
||||
sb.append(" mismatch token type[" + i + "] " + getShortClassName(excelPtg) + " "
|
||||
+ getOperandClassName(excelPtg) + " - " + getShortClassName(poiPtg) + " "
|
||||
+ getOperandClassName(poiPtg));
|
||||
sb.append(NEW_LINE);
|
||||
continue;
|
||||
}
|
||||
if (poiPtg.isBaseToken()) {
|
||||
continue;
|
||||
}
|
||||
sb.append(" token[" + i + "] " + excelPtg.toString() + " "
|
||||
+ getOperandClassName(excelPtg));
|
||||
|
||||
if (excelPtg.getPtgClass() != poiPtg.getPtgClass()) {
|
||||
hasMismatch = true;
|
||||
sb.append(" - was " + getOperandClassName(poiPtg));
|
||||
}
|
||||
sb.append(NEW_LINE);
|
||||
}
|
||||
if (hasMismatch) {
|
||||
throw new AssertionFailedError(sb.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean areTokenClassesSame(Ptg poiPtg, Ptg excelPtg) {
|
||||
if (excelPtg.getClass() == poiPtg.getClass()) {
|
||||
return true;
|
||||
}
|
||||
if (poiPtg.getClass() == ReferencePtg.class) {
|
||||
// TODO - remove funny subclasses of ReferencePtg
|
||||
return excelPtg instanceof ReferencePtg;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private String getShortClassName(Object o) {
|
||||
String cn = o.getClass().getName();
|
||||
int pos = cn.lastIndexOf('.');
|
||||
return cn.substring(pos + 1);
|
||||
}
|
||||
|
||||
private static String getOperandClassName(Ptg ptg) {
|
||||
byte ptgClass = ptg.getPtgClass();
|
||||
switch (ptgClass) {
|
||||
case Ptg.CLASS_REF: return "R";
|
||||
case Ptg.CLASS_VALUE: return "V";
|
||||
case Ptg.CLASS_ARRAY: return "A";
|
||||
}
|
||||
throw new RuntimeException("Unknown operand class (" + ptgClass + ")");
|
||||
}
|
||||
}
|
@ -19,8 +19,10 @@ package org.apache.poi.hssf.record.formula;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.poi.hssf.HSSFTestDataSamples;
|
||||
import org.apache.poi.hssf.record.TestcaseRecordInputStream;
|
||||
import org.apache.poi.hssf.record.UnicodeString;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
import junit.framework.TestCase;
|
||||
@ -77,7 +79,7 @@ public final class TestArrayPtg extends TestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* make sure constant elements are stored row by row
|
||||
* Excel stores array elements column by column. This test makes sure POI does the same.
|
||||
*/
|
||||
public void testElementOrdering() {
|
||||
ArrayPtg ptg = new ArrayPtgV(new TestcaseRecordInputStream(ArrayPtgV.sid, ENCODED_PTG_DATA));
|
||||
@ -86,10 +88,27 @@ public final class TestArrayPtg extends TestCase {
|
||||
assertEquals(2, ptg.getRowCount());
|
||||
|
||||
assertEquals(0, ptg.getValueIndex(0, 0));
|
||||
assertEquals(1, ptg.getValueIndex(1, 0));
|
||||
assertEquals(2, ptg.getValueIndex(2, 0));
|
||||
assertEquals(3, ptg.getValueIndex(0, 1));
|
||||
assertEquals(4, ptg.getValueIndex(1, 1));
|
||||
assertEquals(2, ptg.getValueIndex(1, 0));
|
||||
assertEquals(4, ptg.getValueIndex(2, 0));
|
||||
assertEquals(1, ptg.getValueIndex(0, 1));
|
||||
assertEquals(3, ptg.getValueIndex(1, 1));
|
||||
assertEquals(5, ptg.getValueIndex(2, 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for a bug which was temporarily introduced by the fix for bug 42564.
|
||||
* A spreadsheet was added to make the ordering clearer.
|
||||
*/
|
||||
public void testElementOrderingInSpreadsheet() {
|
||||
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("ex42564-elementOrder.xls");
|
||||
|
||||
// The formula has an array with 3 rows and 5 column
|
||||
String formula = wb.getSheetAt(0).getRow(0).getCell((short)0).getCellFormula();
|
||||
// TODO - These number literals should not have '.0'. Excel has different number rendering rules
|
||||
|
||||
if (formula.equals("SUM({1.0,6.0,11.0;2.0,7.0,12.0;3.0,8.0,13.0;4.0,9.0,14.0;5.0,10.0,15.0})")) {
|
||||
throw new AssertionFailedError("Identified bug 42564 b");
|
||||
}
|
||||
assertEquals("SUM({1.0,2.0,3.0;4.0,5.0,6.0;7.0,8.0,9.0;10.0,11.0,12.0;13.0,14.0,15.0})", formula);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,49 @@
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
package org.apache.poi.hssf.usermodel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.poi.hssf.record.CellValueRecordInterface;
|
||||
import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
|
||||
/**
|
||||
* Test utility class to get <tt>Ptg</tt> arrays out of formula cells
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class FormulaExtractor {
|
||||
|
||||
private FormulaExtractor() {
|
||||
// no instances of this class
|
||||
}
|
||||
|
||||
public static Ptg[] getPtgs(HSSFCell cell) {
|
||||
CellValueRecordInterface vr = cell.getCellValueRecord();
|
||||
if (!(vr instanceof FormulaRecordAggregate)) {
|
||||
throw new IllegalArgumentException("Not a formula cell");
|
||||
}
|
||||
FormulaRecordAggregate fra = (FormulaRecordAggregate) vr;
|
||||
List tokens = fra.getFormulaRecord().getParsedExpression();
|
||||
Ptg[] result = new Ptg[tokens.size()];
|
||||
tokens.toArray(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
@ -18,9 +18,11 @@
|
||||
package org.apache.poi.hssf.usermodel;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
import junit.framework.TestCase;
|
||||
@ -28,6 +30,7 @@ import junit.framework.TestCase;
|
||||
import org.apache.poi.ss.util.Region;
|
||||
|
||||
import org.apache.poi.hssf.HSSFTestDataSamples;
|
||||
import org.apache.poi.hssf.record.EmbeddedObjectRefSubRecord;
|
||||
import org.apache.poi.util.TempFile;
|
||||
|
||||
/**
|
||||
@ -951,4 +954,40 @@ public final class TestBugs extends TestCase {
|
||||
writeOutAndReadBack(wb);
|
||||
assertTrue("no errors writing sample xls", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Problems with extracting check boxes from
|
||||
* HSSFObjectData
|
||||
* @throws Exception
|
||||
*/
|
||||
public void test44840() throws Exception {
|
||||
HSSFWorkbook wb = openSample("WithCheckBoxes.xls");
|
||||
|
||||
// Take a look at the embeded objects
|
||||
List objects = wb.getAllEmbeddedObjects();
|
||||
assertEquals(1, objects.size());
|
||||
|
||||
HSSFObjectData obj = (HSSFObjectData)objects.get(0);
|
||||
assertNotNull(obj);
|
||||
|
||||
// Peek inside the underlying record
|
||||
EmbeddedObjectRefSubRecord rec = obj.findObjectRecord();
|
||||
assertNotNull(rec);
|
||||
|
||||
assertEquals(32, rec.field_1_stream_id_offset);
|
||||
assertEquals(0, rec.field_6_stream_id); // WRONG!
|
||||
assertEquals("Forms.CheckBox.1", rec.field_5_ole_classname);
|
||||
assertEquals(12, rec.remainingBytes.length);
|
||||
|
||||
// Doesn't have a directory
|
||||
assertFalse(obj.hasDirectoryEntry());
|
||||
assertNotNull(obj.getObjectData());
|
||||
assertEquals(12, obj.getObjectData().length);
|
||||
assertEquals("Forms.CheckBox.1", obj.getOLE2ClassName());
|
||||
|
||||
try {
|
||||
obj.getDirectory();
|
||||
fail();
|
||||
} catch(FileNotFoundException e) {}
|
||||
}
|
||||
}
|
||||
|
@ -257,9 +257,15 @@ public class TestHSSFDateUtil extends TestCase {
|
||||
// (who knows what they mean though...)
|
||||
"[$-F800]dddd\\,\\ mmm\\ dd\\,\\ yyyy",
|
||||
"[$-F900]ddd/mm/yyy",
|
||||
// These ones specify colours, who knew that was allowed?
|
||||
"[BLACK]dddd/mm/yy",
|
||||
"[yeLLow]yyyy-mm-dd"
|
||||
};
|
||||
for(int i=0; i<formats.length; i++) {
|
||||
assertTrue( HSSFDateUtil.isADateFormat(formatId, formats[i]) );
|
||||
assertTrue(
|
||||
formats[i] + " is a date format",
|
||||
HSSFDateUtil.isADateFormat(formatId, formats[i])
|
||||
);
|
||||
}
|
||||
|
||||
// Then time based ones too
|
||||
@ -270,7 +276,10 @@ public class TestHSSFDateUtil extends TestCase {
|
||||
"mm/dd HH:MM PM", "mm/dd HH:MM pm"
|
||||
};
|
||||
for(int i=0; i<formats.length; i++) {
|
||||
assertTrue( HSSFDateUtil.isADateFormat(formatId, formats[i]) );
|
||||
assertTrue(
|
||||
formats[i] + " is a datetime format",
|
||||
HSSFDateUtil.isADateFormat(formatId, formats[i])
|
||||
);
|
||||
}
|
||||
|
||||
// Then invalid ones
|
||||
@ -278,10 +287,14 @@ public class TestHSSFDateUtil extends TestCase {
|
||||
"yyyy*mm*dd",
|
||||
"0.0", "0.000",
|
||||
"0%", "0.0%",
|
||||
"[]Foo", "[BLACK]0.00%",
|
||||
"", null
|
||||
};
|
||||
for(int i=0; i<formats.length; i++) {
|
||||
assertFalse( HSSFDateUtil.isADateFormat(formatId, formats[i]) );
|
||||
assertFalse(
|
||||
formats[i] + " is not a date or datetime format",
|
||||
HSSFDateUtil.isADateFormat(formatId, formats[i])
|
||||
);
|
||||
}
|
||||
|
||||
// And these are ones we probably shouldn't allow,
|
||||
|
Loading…
Reference in New Issue
Block a user