From e685873465eef64a0f5ad3920801f641c0faf6a4 Mon Sep 17 00:00:00 2001
From: Nick Burch
+ * Primarily used by test cases when testing for specific parsing exceptions.
@@ -378,17 +336,17 @@ public class FormulaParser { * @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; + 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; @@ -398,390 +356,430 @@ public class FormulaParser { count+=ptg.getSize(); index++; } - + return count; } /** * Generates the variable function ptg for the formula. *
- * For IF Formulas, additional PTGs are added to the tokens
+ * For IF Formulas, additional PTGs are added to the tokens
* @param name
* @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, byte numArgs) {
- AbstractFunctionPtg retval = null;
-
- if (name.equals("IF")) {
- retval = new FuncVarPtg(AbstractFunctionPtg.ATTR_NAME, numArgs);
-
- //simulated pop, no bounds checking because this list better be populated by function()
- List argumentPointers = (List)this.functionTokens.get(0);
-
-
- 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 > (int)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));
-
- } else {
-
- retval = new FuncVarPtg(name,numArgs);
+ private AbstractFunctionPtg getFunction(String name, int numArgs, List argumentPointers) {
+
+ AbstractFunctionPtg retval = new FuncVarPtg(name, (byte)numArgs);
+ 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;
}
+
+ private static boolean isArgumentDelimiter(char ch) {
+ return ch == ',' || ch == ')';
+ }
/** get arguments to a function */
- private int Arguments() {
- int numArgs = 0;
- if (look != ')') {
- numArgs++;
- Expression();
- addArgumentPointer();
+ private int Arguments(List argumentPointers) {
+ SkipWhite();
+ if(look == ')') {
+ return 0;
}
- while (look == ',' || look == ';') { //TODO handle EmptyArgs
- if(look == ',') {
- Match(',');
+
+ boolean missedPrevArg = true;
+
+ int numArgs = 0;
+ while(true) {
+ SkipWhite();
+ if(isArgumentDelimiter(look)) {
+ if(missedPrevArg) {
+ tokens.add(new MissingArgPtg());
+ addArgumentPointer(argumentPointers);
+ numArgs++;
+ }
+ if(look == ')') {
+ break;
+ }
+ Match(',');
+ missedPrevArg = true;
+ continue;
}
- else {
- Match(';');
- }
- Expression();
- addArgumentPointer();
+ comparisonExpression();
+ addArgumentPointer(argumentPointers);
numArgs++;
+ missedPrevArg = false;
}
return numArgs;
}
/** Parse and Translate a Math Factor */
- private void Factor() {
- if (look == '-')
- {
- Match('-');
- Factor();
- tokens.add(new UnaryMinusPtg());
- }
- else if (look == '+') {
- Match('+');
- Factor();
- tokens.add(new UnaryPlusPtg());
- }
- else if (look == '(' ) {
- Match('(');
- Expression();
- Match(')');
- tokens.add(new ParenthesisPtg());
- } else if (IsAlpha(look) || look == '\''){
- Ident();
- } else if(look == '"') {
- StringLiteral();
- } else if (look == ')' || look == ',') {
- tokens.add(new MissingArgPtg());
- } else {
- String number2 = null;
- String exponent = null;
- String number1 = GetNum();
-
- if (look == '.') {
- GetChar();
- number2 = GetNum();
+ private void powerFactor() {
+ percentFactor();
+ while(true) {
+ SkipWhite();
+ if(look != '^') {
+ return;
}
-
- if (look == 'E') {
- GetChar();
-
- String sign = "";
- if (look == '+') {
- GetChar();
- } else if (look == '-') {
- GetChar();
- sign = "-";
- }
-
- String number = GetNum();
- if (number == null) {
- Expected("Integer");
- }
- exponent = sign + number;
- }
-
- if (number1 == null && number2 == null) {
- Expected("Integer");
- }
-
- tokens.add(getNumberPtgFromString(number1, number2, exponent));
+ Match('^');
+ percentFactor();
+ tokens.add(new PowerPtg());
}
}
- /**
- * Get a PTG for an integer from its string representation.
- * return Int or Number Ptg based on size of input
- */
- private Ptg getNumberPtgFromString(String number1, String number2, String exponent) {
+ private void percentFactor() {
+ tokens.add(parseSimpleFactor());
+ while(true) {
+ SkipWhite();
+ if(look != '%') {
+ return;
+ }
+ Match('%');
+ tokens.add(new PercentPtg());
+ }
+ }
+
+
+ /**
+ * factors (without ^ or % )
+ */
+ private Ptg parseSimpleFactor() {
+ SkipWhite();
+ switch(look) {
+ case '#':
+ return parseErrorLiteral();
+ case '-':
+ Match('-');
+ powerFactor();
+ return new UnaryMinusPtg();
+ case '+':
+ Match('+');
+ powerFactor();
+ return new UnaryPlusPtg();
+ case '(':
+ Match('(');
+ comparisonExpression();
+ Match(')');
+ return new ParenthesisPtg();
+ case '"':
+ return parseStringLiteral();
+ case ',':
+ case ')':
+ return new MissingArgPtg(); // TODO - not quite the right place to recognise a missing arg
+ }
+ if (IsAlpha(look) || look == '\''){
+ return parseIdent();
+ }
+ // else - assume number
+ return parseNumber();
+ }
+
+
+ private Ptg parseNumber() {
+ String number2 = null;
+ String exponent = null;
+ String number1 = GetNum();
+
+ if (look == '.') {
+ GetChar();
+ number2 = GetNum();
+ }
+
+ if (look == 'E') {
+ GetChar();
+
+ String sign = "";
+ if (look == '+') {
+ GetChar();
+ } else if (look == '-') {
+ GetChar();
+ sign = "-";
+ }
+
+ String number = GetNum();
+ if (number == null) {
+ throw expected("Integer");
+ }
+ exponent = sign + number;
+ }
+
+ if (number1 == null && number2 == null) {
+ throw expected("Integer");
+ }
+
+ return getNumberPtgFromString(number1, number2, exponent);
+ }
+
+
+ private ErrPtg parseErrorLiteral() {
+ Match('#');
+ String part1 = GetName().toUpperCase();
+
+ switch(part1.charAt(0)) {
+ case 'V':
+ if(part1.equals("VALUE")) {
+ Match('!');
+ return ErrPtg.VALUE_INVALID;
+ }
+ throw expected("#VALUE!");
+ case 'R':
+ if(part1.equals("REF")) {
+ Match('!');
+ return ErrPtg.REF_INVALID;
+ }
+ throw expected("#REF!");
+ case 'D':
+ if(part1.equals("DIV")) {
+ Match('/');
+ Match('0');
+ Match('!');
+ return ErrPtg.DIV_ZERO;
+ }
+ throw expected("#DIV/0!");
+ case 'N':
+ if(part1.equals("NAME")) {
+ Match('?'); // only one that ends in '?'
+ return ErrPtg.NAME_INVALID;
+ }
+ if(part1.equals("NUM")) {
+ Match('!');
+ return ErrPtg.NUM_ERROR;
+ }
+ if(part1.equals("NULL")) {
+ Match('!');
+ return ErrPtg.NULL_INTERSECTION;
+ }
+ if(part1.equals("N")) {
+ Match('/');
+ if(look != 'A' && look != 'a') {
+ throw expected("#N/A");
+ }
+ Match(look);
+ // Note - no '!' or '?' suffix
+ return ErrPtg.N_A;
+ }
+ throw expected("#NAME?, #NUM!, #NULL! or #N/A");
+
+ }
+ throw expected("#VALUE!, #REF!, #DIV/0!, #NAME?, #NUM!, #NULL! or #N/A");
+ }
+
+
+ /**
+ * Get a PTG for an integer from its string representation.
+ * return Int or Number Ptg based on size of input
+ */
+ private static Ptg getNumberPtgFromString(String number1, String number2, String exponent) {
StringBuffer number = new StringBuffer();
-
- if (number2 == null) {
- number.append(number1);
-
- if (exponent != null) {
- number.append('E');
- number.append(exponent);
- }
-
- String numberStr = number.toString();
-
- try {
- return new IntPtg(numberStr);
- } catch (NumberFormatException e) {
- return new NumberPtg(numberStr);
- }
- } else {
- if (number1 != null) {
- number.append(number1);
- }
-
- number.append('.');
- number.append(number2);
-
+
+ if (number2 == null) {
+ number.append(number1);
+
if (exponent != null) {
number.append('E');
number.append(exponent);
}
-
- return new NumberPtg(number.toString());
- }
- }
-
-
- private void StringLiteral()
- {
- // Can't use match here 'cuz it consumes whitespace
- // which we need to preserve inside the string.
- // - pete
- // Match('"');
- if (look != '"')
- Expected("\"");
- else
- {
- GetChar();
- StringBuffer Token = new StringBuffer();
- for (;;)
- {
- if (look == '"')
- {
- GetChar();
- SkipWhite(); //potential white space here since it doesnt matter up to the operator
- if (look == '"')
- Token.append("\"");
- else
- break;
- }
- else if (look == 0)
- {
- break;
- }
- else
- {
- Token.append(look);
- GetChar();
- }
- }
- tokens.add(new StringPtg(Token.toString()));
- }
- }
-
- /** Recognize and Translate a Multiply */
- private void Multiply(){
- Match('*');
- Factor();
- tokens.add(new MultiplyPtg());
-
- }
-
-
- /** Recognize and Translate a Divide */
- private void Divide() {
- Match('/');
- Factor();
- tokens.add(new DividePtg());
+ String numberStr = number.toString();
+ int intVal;
+ try {
+ intVal = Integer.parseInt(numberStr);
+ } catch (NumberFormatException e) {
+ return new NumberPtg(numberStr);
+ }
+ if (IntPtg.isInRange(intVal)) {
+ return new IntPtg(intVal);
+ }
+ return new NumberPtg(numberStr);
+ }
+
+ if (number1 != null) {
+ number.append(number1);
+ }
+
+ number.append('.');
+ number.append(number2);
+
+ if (exponent != null) {
+ number.append('E');
+ number.append(exponent);
+ }
+
+ return new NumberPtg(number.toString());
}
-
-
+
+
+ private StringPtg parseStringLiteral()
+ {
+ Match('"');
+
+ StringBuffer token = new StringBuffer();
+ while (true) {
+ if (look == '"') {
+ GetChar();
+ if (look != '"') {
+ break;
+ }
+ }
+ token.append(look);
+ GetChar();
+ }
+ return new StringPtg(token.toString());
+ }
+
/** Parse and Translate a Math Term */
- private void Term(){
- Factor();
- while (look == '*' || look == '/' || look == '^' || look == '&') {
-
- ///TODO do we need to do anything here??
- if (look == '*') Multiply();
- else if (look == '/') Divide();
- else if (look == '^') Power();
- else if (look == '&') Concat();
+ private void Term() {
+ powerFactor();
+ while(true) {
+ SkipWhite();
+ switch(look) {
+ case '*':
+ Match('*');
+ powerFactor();
+ tokens.add(new MultiplyPtg());
+ continue;
+ case '/':
+ Match('/');
+ powerFactor();
+ tokens.add(new DividePtg());
+ continue;
+ }
+ return; // finished with Term
}
}
- /** Recognize and Translate an Add */
- private void Add() {
- Match('+');
- Term();
- tokens.add(new AddPtg());
+ private void comparisonExpression() {
+ concatExpression();
+ while (true) {
+ SkipWhite();
+ switch(look) {
+ case '=':
+ case '>':
+ case '<':
+ Ptg comparisonToken = getComparisonToken();
+ concatExpression();
+ tokens.add(comparisonToken);
+ continue;
+ }
+ return; // finished with predicate expression
+ }
}
-
- /** Recognize and Translate a Concatination */
- private void Concat() {
- Match('&');
- Term();
- tokens.add(new ConcatPtg());
- }
-
- /** Recognize and Translate a test for Equality */
- private void Equal() {
- Match('=');
- Expression();
- tokens.add(new EqualPtg());
- }
-
- /** Recognize and Translate a Subtract */
- private void Subtract() {
- Match('-');
- Term();
- tokens.add(new SubtractPtg());
- }
- private void Power() {
- Match('^');
- Term();
- tokens.add(new PowerPtg());
+ private Ptg getComparisonToken() {
+ if(look == '=') {
+ Match(look);
+ return new EqualPtg();
+ }
+ boolean isGreater = look == '>';
+ Match(look);
+ if(isGreater) {
+ if(look == '=') {
+ Match('=');
+ return new GreaterEqualPtg();
+ }
+ return new GreaterThanPtg();
+ }
+ switch(look) {
+ case '=':
+ Match('=');
+ return new LessEqualPtg();
+ case '>':
+ Match('>');
+ return new NotEqualPtg();
+ }
+ return new LessThanPtg();
}
+
+ private void concatExpression() {
+ additiveExpression();
+ while (true) {
+ SkipWhite();
+ if(look != '&') {
+ break; // finished with concat expression
+ }
+ Match('&');
+ additiveExpression();
+ tokens.add(new ConcatPtg());
+ }
+ }
+
/** Parse and Translate an Expression */
- private void Expression() {
+ private void additiveExpression() {
Term();
- while (IsAddop(look)) {
- if (look == '+' ) Add();
- else if (look == '-') Subtract();
+ while (true) {
+ SkipWhite();
+ switch(look) {
+ case '+':
+ Match('+');
+ Term();
+ tokens.add(new AddPtg());
+ continue;
+ case '-':
+ Match('-');
+ Term();
+ tokens.add(new SubtractPtg());
+ continue;
+ }
+ return; // finished with additive expression
}
-
- /*
- * This isn't quite right since it would allow multiple comparison operators.
- */
-
- if(look == '=' || look == '>' || look == '<') {
- if (look == '=') Equal();
- else if (look == '>') GreaterThan();
- else if (look == '<') LessThan();
- return;
- }
-
-
}
-
- /** Recognize and Translate a Greater Than */
- private void GreaterThan() {
- Match('>');
- if(look == '=')
- GreaterEqual();
- else {
- Expression();
- tokens.add(new GreaterThanPtg());
- }
- }
-
- /** Recognize and Translate a Less Than */
- private void LessThan() {
- Match('<');
- if(look == '=')
- LessEqual();
- else if(look == '>')
- NotEqual();
- else {
- Expression();
- tokens.add(new LessThanPtg());
- }
- }
-
- /**
- * Recognize and translate Greater than or Equal
- *
- */
- private void GreaterEqual() {
- Match('=');
- Expression();
- tokens.add(new GreaterEqualPtg());
- }
-
- /**
- * Recognize and translate Less than or Equal
- *
- */
-
- private void LessEqual() {
- Match('=');
- Expression();
- tokens.add(new LessEqualPtg());
- }
-
- /**
- * Recognize and not Equal
- *
- */
-
- private void NotEqual() {
- Match('>');
- Expression();
- tokens.add(new NotEqualPtg());
- }
-
//{--------------------------------------------------------------}
//{ Parse and Translate an Assignment Statement }
/**
@@ -794,48 +792,46 @@ begin
end;
**/
-
-
- /** Initialize */
-
- private void init() {
- GetChar();
- SkipWhite();
- }
-
+
+
/** API call to execute the parsing of the formula
*
*/
public void parse() {
- synchronized (tokens) {
- init();
- Expression();
+ pointer=0;
+ GetChar();
+ comparisonExpression();
+
+ if(pointer <= formulaLength) {
+ String msg = "Unused input [" + formulaString.substring(pointer-1)
+ + "] after attempting to parse the formula [" + formulaString + "]";
+ throw new FormulaParseException(msg);
}
}
-
-
+
+
/*********************************
* PARSER IMPLEMENTATION ENDS HERE
* EXCEL SPECIFIC METHODS BELOW
*******************************/
-
- /** API call to retrive the array of Ptgs created as
+
+ /** API call to retrive the array of Ptgs created as
* a result of the parsing
*/
public Ptg[] getRPNPtg() {
return getRPNPtg(FORMULA_TYPE_CELL);
}
-
+
public Ptg[] getRPNPtg(int formulaType) {
Node node = createTree();
setRootLevelRVA(node, formulaType);
setParameterRVA(node,formulaType);
return (Ptg[]) tokens.toArray(new Ptg[0]);
}
-
+
private void setRootLevelRVA(Node n, int formulaType) {
//Pg 16, excelfileformat.pdf @ openoffice.org
- Ptg p = (Ptg) n.getValue();
+ Ptg p = n.getValue();
if (formulaType == FormulaParser.FORMULA_TYPE_NAMEDRANGE) {
if (p.getDefaultOperandClass() == Ptg.CLASS_REF) {
setClass(n,Ptg.CLASS_REF);
@@ -845,9 +841,9 @@ end;
} else {
setClass(n,Ptg.CLASS_VALUE);
}
-
+
}
-
+
private void setParameterRVA(Node n, int formulaType) {
Ptg p = n.getValue();
int numOperands = n.getNumChildren();
@@ -863,11 +859,11 @@ end;
for (int i =0;i
+ *
+ * REFERENCE: 5.114
+ *
+ * @author Josh Micich
+ */
+public final class CRNCountRecord extends Record {
+ public final static short sid = 0x59;
+
+ private static final short BASE_RECORD_SIZE = 4;
+
+
+ private int field_1_number_crn_records;
+ private int field_2_sheet_table_index;
+
+ public CRNCountRecord() {
+ throw new RuntimeException("incomplete code");
+ }
+
+ public CRNCountRecord(RecordInputStream in) {
+ super(in);
+ }
+
+ protected void validateSid(short id) {
+ if (id != sid) {
+ throw new RecordFormatException("NOT An XCT RECORD");
+ }
+ }
+
+ public int getNumberOfCRNs() {
+ return field_1_number_crn_records;
+ }
+
+
+ protected void fillFields(RecordInputStream in) {
+ field_1_number_crn_records = in.readShort();
+ if(field_1_number_crn_records < 0) {
+ // TODO - seems like the sign bit of this field might be used for some other purpose
+ // see example file for test case "TestBugs.test19599()"
+ field_1_number_crn_records = (short)-field_1_number_crn_records;
+ }
+ field_2_sheet_table_index = in.readShort();
+ }
+
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append(getClass().getName()).append(" [XCT");
+ sb.append(" nCRNs=").append(field_1_number_crn_records);
+ sb.append(" sheetIx=").append(field_2_sheet_table_index);
+ sb.append("]");
+ return sb.toString();
+ }
+
+ public int serialize(int offset, byte [] data) {
+ LittleEndian.putShort(data, 0 + offset, sid);
+ LittleEndian.putShort(data, 2 + offset, BASE_RECORD_SIZE);
+ LittleEndian.putShort(data, 4 + offset, (short)field_1_number_crn_records);
+ LittleEndian.putShort(data, 6 + offset, (short)field_2_sheet_table_index);
+ return getRecordSize();
+ }
+
+ public int getRecordSize() {
+ return BASE_RECORD_SIZE + 4;
+ }
+
+ /**
+ * return the non static version of the id for this record.
+ */
+ public short getSid() {
+ return sid;
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/record/CRNRecord.java b/src/java/org/apache/poi/hssf/record/CRNRecord.java
new file mode 100755
index 000000000..73b9e42df
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/CRNRecord.java
@@ -0,0 +1,99 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hssf.record;
+
+import org.apache.poi.hssf.record.constant.ConstantValueParser;
+import org.apache.poi.util.LittleEndian;
+
+/**
+ * Title: CRN
+ * Description: This record stores the contents of an external cell or cell range
+ * REFERENCE: 5.23
+ *
+ * @author josh micich
+ */
+public final class CRNRecord extends Record {
+ public final static short sid = 0x5A;
+
+ private int field_1_last_column_index;
+ private int field_2_first_column_index;
+ private int field_3_row_index;
+ private Object[] field_4_constant_values;
+
+ public CRNRecord() {
+ throw new RuntimeException("incomplete code");
+ }
+
+ public CRNRecord(RecordInputStream in) {
+ super(in);
+ }
+
+ protected void validateSid(short id) {
+ if (id != sid) {
+ throw new RecordFormatException("NOT An XCT RECORD");
+ }
+ }
+
+ public int getNumberOfCRNs() {
+ return field_1_last_column_index;
+ }
+
+
+ protected void fillFields(RecordInputStream in) {
+ field_1_last_column_index = in.readByte() & 0x00FF;
+ field_2_first_column_index = in.readByte() & 0x00FF;
+ field_3_row_index = in.readShort();
+ int nValues = field_1_last_column_index - field_2_first_column_index + 1;
+ field_4_constant_values = ConstantValueParser.parse(in, nValues);
+ }
+
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append(getClass().getName()).append(" [CRN");
+ sb.append(" rowIx=").append(field_3_row_index);
+ sb.append(" firstColIx=").append(field_2_first_column_index);
+ sb.append(" lastColIx=").append(field_1_last_column_index);
+ sb.append("]");
+ return sb.toString();
+ }
+ private int getDataSize() {
+ return 4 + ConstantValueParser.getEncodedSize(field_4_constant_values);
+ }
+
+ public int serialize(int offset, byte [] data) {
+ int dataSize = getDataSize();
+ LittleEndian.putShort(data, 0 + offset, sid);
+ LittleEndian.putShort(data, 2 + offset, (short) dataSize);
+ LittleEndian.putByte(data, 4 + offset, field_1_last_column_index);
+ LittleEndian.putByte(data, 5 + offset, field_2_first_column_index);
+ LittleEndian.putShort(data, 6 + offset, (short) field_3_row_index);
+ return getRecordSize();
+ }
+
+ public int getRecordSize() {
+ return getDataSize() + 4;
+ }
+
+ /**
+ * return the non static version of the id for this record.
+ */
+ public short getSid() {
+ return sid;
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java b/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java
new file mode 100755
index 000000000..771603c85
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java
@@ -0,0 +1,179 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hssf.record;
+
+import java.util.List;
+import java.util.Stack;
+
+import org.apache.poi.hssf.record.formula.Ptg;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.StringUtil;
+
+/**
+ * EXTERNALNAME
- * Description: A Extrenal Workbook Deciption (Sup Book)
+ * Title: Sup Book (EXTERNALBOOK)
+ * Description: A External Workbook Description (Suplemental Book)
* Its only a dummy record for making new ExternSheet Record
- * REFERENCE:
+ * REFERENCE: 5.38
* @author Libin Roman (Vista Portal LDT. Developer)
* @author Andrew C. Oliver (acoliver@apache.org)
*
*/
-public class SupBookRecord extends Record
-{
+public final class SupBookRecord extends Record {
+
public final static short sid = 0x1AE;
+
+ private static final short SMALL_RECORD_SIZE = 4;
+ private static final short TAG_INTERNAL_REFERENCES = 0x0401;
+ private static final short TAG_ADD_IN_FUNCTIONS = 0x3A01;
+
private short field_1_number_of_sheets;
- private short field_2_flag;
+ private UnicodeString field_2_encoded_url;
+ private UnicodeString[] field_3_sheet_names;
+ private boolean _isAddInFunctions;
-
- public SupBookRecord()
- {
- setFlag((short)0x401);
+
+ public static SupBookRecord createInternalReferences(short numberOfSheets) {
+ return new SupBookRecord(false, numberOfSheets);
+ }
+ public static SupBookRecord createAddInFunctions() {
+ return new SupBookRecord(true, (short)0);
+ }
+ public static SupBookRecord createExternalReferences(UnicodeString url, UnicodeString[] sheetNames) {
+ return new SupBookRecord(url, sheetNames);
+ }
+ private SupBookRecord(boolean isAddInFuncs, short numberOfSheets) {
+ // else not 'External References'
+ field_1_number_of_sheets = numberOfSheets;
+ field_2_encoded_url = null;
+ field_3_sheet_names = null;
+ _isAddInFunctions = isAddInFuncs;
+ }
+ public SupBookRecord(UnicodeString url, UnicodeString[] sheetNames) {
+ field_1_number_of_sheets = (short) sheetNames.length;
+ field_2_encoded_url = url;
+ field_3_sheet_names = sheetNames;
+ _isAddInFunctions = false;
}
/**
* Constructs a Extern Sheet record and sets its fields appropriately.
*
- * @param in the RecordInputstream to read the record from
+ * @param id id must be 0x16 or an exception will be throw upon validation
+ * @param size the size of the data area of the record
+ * @param data data of the record (should not contain sid/len)
*/
- public SupBookRecord(RecordInputStream in)
- {
+ public SupBookRecord(RecordInputStream in) {
super(in);
}
- protected void validateSid(short id)
- {
- if (id != sid)
- {
- throw new RecordFormatException("NOT An Supbook RECORD");
+ protected void validateSid(short id) {
+ if (id != sid) {
+ throw new RecordFormatException("NOT An ExternSheet RECORD");
}
}
- /**
- * @param in the RecordInputstream to read the record from
- */
- protected void fillFields(RecordInputStream in)
- {
- //For now We use it only for one case
- //When we need to add an named range when no named ranges was
- //before it
- field_1_number_of_sheets = in.readShort();
- field_2_flag = in.readShort();
+ public boolean isExternalReferences() {
+ return field_3_sheet_names != null;
}
+ public boolean isInternalReferences() {
+ return field_3_sheet_names == null && !_isAddInFunctions;
+ }
+ public boolean isAddInFunctions() {
+ return field_3_sheet_names == null && _isAddInFunctions;
+ }
+ /**
+ * called by the constructor, should set class level fields. Should throw
+ * runtime exception for bad/incomplete data.
+ *
+ * @param data raw data
+ * @param size size of data
+ * @param offset of the record's data (provided a big array of the file)
+ */
+ protected void fillFields(RecordInputStream in) {
+ field_1_number_of_sheets = in.readShort();
+
+ if(in.getLength() > SMALL_RECORD_SIZE) {
+ // 5.38.1 External References
+ _isAddInFunctions = false;
+ field_2_encoded_url = in.readUnicodeString();
+ UnicodeString[] sheetNames = new UnicodeString[field_1_number_of_sheets];
+ for (int i = 0; i < sheetNames.length; i++) {
+ sheetNames[i] = in.readUnicodeString();
+ }
+ field_3_sheet_names = sheetNames;
+ return;
+ }
+ // else not 'External References'
+ field_2_encoded_url = null;
+ field_3_sheet_names = null;
+
+ short nextShort = in.readShort();
+ if(nextShort == TAG_INTERNAL_REFERENCES) {
+ // 5.38.2 'Internal References'
+ _isAddInFunctions = false;
+ } else if(nextShort == TAG_ADD_IN_FUNCTIONS) {
+ // 5.38.3 'Add-In Functions'
+ _isAddInFunctions = true;
+ if(field_1_number_of_sheets != 1) {
+ throw new RuntimeException("Expected 0x0001 for number of sheets field in 'Add-In Functions' but got ("
+ + field_1_number_of_sheets + ")");
+ }
+ } else {
+ throw new RuntimeException("invalid EXTERNALBOOK code ("
+ + Integer.toHexString(nextShort) + ")");
+ }
+ }
- public String toString()
- {
- StringBuffer buffer = new StringBuffer();
- buffer.append("[SUPBOOK]\n");
- buffer.append("numberosheets = ").append(getNumberOfSheets()).append('\n');
- buffer.append("flag = ").append(getFlag()).append('\n');
- buffer.append("[/SUPBOOK]\n");
- return buffer.toString();
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append(getClass().getName()).append(" [SUPBOOK ");
+
+ if(isExternalReferences()) {
+ sb.append("External References");
+ sb.append(" nSheets=").append(field_1_number_of_sheets);
+ sb.append(" url=").append(field_2_encoded_url);
+ } else if(_isAddInFunctions) {
+ sb.append("Add-In Functions");
+ } else {
+ sb.append("Internal References ");
+ sb.append(" nSheets= ").append(field_1_number_of_sheets);
+ }
+ return sb.toString();
+ }
+ private int getDataSize() {
+ if(!isExternalReferences()) {
+ return SMALL_RECORD_SIZE;
+ }
+ int sum = 2; // u16 number of sheets
+ UnicodeRecordStats urs = new UnicodeRecordStats();
+ field_2_encoded_url.getRecordSize(urs);
+ sum += urs.recordSize;
+
+ for(int i=0; i
+ * The name matching is case insensitive.
+ * @return
+ * The name matching is case insensitive.
+ * @return the standard worksheet function index if found, otherwise FUNCTION_INDEX_EXTERNAL
+ */
+ protected static short lookupIndex(String name) {
+ Integer index = (Integer) map.getKeyForValue(name.toUpperCase());
if (index != null) return index.shortValue();
- return INDEX_EXTERNAL;
+ return FUNCTION_INDEX_EXTERNAL;
}
/**
@@ -115,7 +143,7 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
BinaryTree dmap = new BinaryTree();
dmap.put(new Integer(0),"COUNT");
- dmap.put(new Integer(1),"specialflag");
+ dmap.put(new Integer(1),FUNCTION_NAME_IF);
dmap.put(new Integer(2),"ISNA");
dmap.put(new Integer(3),"ISERROR");
dmap.put(new Integer(4),"SUM");
@@ -354,7 +382,7 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
dmap.put(new Integer(252),"FREQUENCY");
dmap.put(new Integer(253),"ADDTOOLBAR");
dmap.put(new Integer(254),"DELETETOOLBAR");
- dmap.put(new Integer(255),"externalflag");
+ dmap.put(new Integer(FUNCTION_INDEX_EXTERNAL),"externalflag");
dmap.put(new Integer(256),"RESETTOOLBAR");
dmap.put(new Integer(257),"EVALUATE");
dmap.put(new Integer(258),"GETTOOLBAR");
diff --git a/src/java/org/apache/poi/hssf/record/formula/IntPtg.java b/src/java/org/apache/poi/hssf/record/formula/IntPtg.java
index 257c089df..f4106b6aa 100644
--- a/src/java/org/apache/poi/hssf/record/formula/IntPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/IntPtg.java
@@ -1,4 +1,3 @@
-
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@@ -16,12 +15,6 @@
limitations under the License.
==================================================================== */
-
-/*
- * IntPtg.java
- *
- * Created on October 29, 2001, 7:37 PM
- */
package org.apache.poi.hssf.record.formula;
import org.apache.poi.util.LittleEndian;
@@ -29,64 +22,45 @@ import org.apache.poi.hssf.model.Workbook;
import org.apache.poi.hssf.record.RecordInputStream;
/**
- * Integer (unsigned short intger)
+ * Integer (unsigned short integer)
* Stores an unsigned short value (java int) in a formula
* @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 {
+ // 16 bit unsigned integer
+ private static final int MIN_VALUE = 0x0000;
+ private static final int MAX_VALUE = 0xFFFF;
+
+ /**
+ * Excel represents integers 0..65535 with the tInt token.
+ * @return o`v1w23-o#Y
+ *
+ *
+ *
+ * @author Josh Micich
+ */
+final class LinkTable {
+
+ private static final class CRNBlock {
+
+ private final CRNCountRecord _countRecord;
+ private final CRNRecord[] _crns;
+
+ public CRNBlock(RecordStream rs) {
+ _countRecord = (CRNCountRecord) rs.getNext();
+ int nCRNs = _countRecord.getNumberOfCRNs();
+ CRNRecord[] crns = new CRNRecord[nCRNs];
+ for (int i = 0; i < crns.length; i++) {
+ crns[i] = (CRNRecord) rs.getNext();
+ }
+ _crns = crns;
+ }
+ public CRNRecord[] getCrns() {
+ return (CRNRecord[]) _crns.clone();
+ }
+ }
+
+ private static final class ExternalBookBlock {
+ private final SupBookRecord _externalBookRecord;
+ private final ExternalNameRecord[] _externalNameRecords;
+ private final CRNBlock[] _crnBlocks;
+
+ public ExternalBookBlock(RecordStream rs) {
+ _externalBookRecord = (SupBookRecord) rs.getNext();
+ List temp = new ArrayList();
+ while(rs.peekNextClass() == ExternalNameRecord.class) {
+ temp.add(rs.getNext());
+ }
+ _externalNameRecords = new ExternalNameRecord[temp.size()];
+ temp.toArray(_externalNameRecords);
+
+ temp.clear();
+
+ while(rs.peekNextClass() == CRNCountRecord.class) {
+ temp.add(new CRNBlock(rs));
+ }
+ _crnBlocks = new CRNBlock[temp.size()];
+ temp.toArray(_crnBlocks);
+ }
+
+ public ExternalBookBlock(short numberOfSheets) {
+ _externalBookRecord = SupBookRecord.createInternalReferences(numberOfSheets);
+ _externalNameRecords = new ExternalNameRecord[0];
+ _crnBlocks = new CRNBlock[0];
+ }
+
+ public SupBookRecord getExternalBookRecord() {
+ return _externalBookRecord;
+ }
+
+ public String getNameText(int definedNameIndex) {
+ return _externalNameRecords[definedNameIndex].getText();
+ }
+ }
+
+ private final ExternalBookBlock[] _externalBookBlocks;
+ private final ExternSheetRecord _externSheetRecord;
+ private final List _definedNames;
+ private final int _recordCount;
+ private final WorkbookRecordList _workbookRecordList; // TODO - would be nice to remove this
+
+ public LinkTable(List inputList, int startIndex, WorkbookRecordList workbookRecordList) {
+
+ _workbookRecordList = workbookRecordList;
+ RecordStream rs = new RecordStream(inputList, startIndex);
+
+ List temp = new ArrayList();
+ while(rs.peekNextClass() == SupBookRecord.class) {
+ temp.add(new ExternalBookBlock(rs));
+ }
+ if(temp.size() < 1) {
+ throw new RuntimeException("Need at least one EXTERNALBOOK blocks");
+ }
+ _externalBookBlocks = new ExternalBookBlock[temp.size()];
+ temp.toArray(_externalBookBlocks);
+ temp.clear();
+
+ // If link table is present, there is always 1 of ExternSheetRecord
+ Record next = rs.getNext();
+ _externSheetRecord = (ExternSheetRecord)next;
+ _definedNames = new ArrayList();
+ // collect zero or more DEFINEDNAMEs id=0x18
+ while(rs.peekNextClass() == NameRecord.class) {
+ NameRecord nr = (NameRecord)rs.getNext();
+ _definedNames.add(nr);
+ }
+
+ _recordCount = rs.getCountRead();
+ _workbookRecordList.getRecords().addAll(inputList.subList(startIndex, startIndex + _recordCount));
+ }
+
+ public LinkTable(short numberOfSheets, WorkbookRecordList workbookRecordList) {
+ _workbookRecordList = workbookRecordList;
+ _definedNames = new ArrayList();
+ _externalBookBlocks = new ExternalBookBlock[] {
+ new ExternalBookBlock(numberOfSheets),
+ };
+ _externSheetRecord = new ExternSheetRecord();
+ _recordCount = 2;
+
+ // tell _workbookRecordList about the 2 new records
+
+ SupBookRecord supbook = _externalBookBlocks[0].getExternalBookRecord();
+
+ int idx = findFirstRecordLocBySid(CountryRecord.sid);
+ if(idx < 0) {
+ throw new RuntimeException("CountryRecord not found");
+ }
+ _workbookRecordList.add(idx+1, _externSheetRecord);
+ _workbookRecordList.add(idx+1, supbook);
+ }
+
+ /**
+ * TODO - would not be required if calling code used RecordStream or similar
+ */
+ public int getRecordCount() {
+ return _recordCount;
+ }
+
+
+ public NameRecord getSpecificBuiltinRecord(byte name, int sheetIndex) {
+
+ Iterator iterator = _definedNames.iterator();
+ while (iterator.hasNext()) {
+ NameRecord record = ( NameRecord ) iterator.next();
+
+ //print areas are one based
+ if (record.getBuiltInName() == name && record.getIndexToSheet() == sheetIndex) {
+ return record;
+ }
+ }
+
+ return null;
+ }
+
+ public void removeBuiltinRecord(byte name, int sheetIndex) {
+ //the name array is smaller so searching through it should be faster than
+ //using the findFirstXXXX methods
+ NameRecord record = getSpecificBuiltinRecord(name, sheetIndex);
+ if (record != null) {
+ _definedNames.remove(record);
+ }
+ // TODO - do we need "Workbook.records.remove(...);" similar to that in Workbook.removeName(int namenum) {}?
+ }
+
+ public int getNumNames() {
+ return _definedNames.size();
+ }
+
+ public NameRecord getNameRecord(int index) {
+ return (NameRecord) _definedNames.get(index);
+ }
+
+ public void addName(NameRecord name) {
+ _definedNames.add(name);
+
+ // TODO - this is messy
+ // Not the most efficient way but the other way was causing too many bugs
+ int idx = findFirstRecordLocBySid(ExternSheetRecord.sid);
+ if (idx == -1) idx = findFirstRecordLocBySid(SupBookRecord.sid);
+ if (idx == -1) idx = findFirstRecordLocBySid(CountryRecord.sid);
+ int countNames = _definedNames.size();
+ _workbookRecordList.add(idx+countNames, name);
+
+ }
+
+ public void removeName(int namenum) {
+ _definedNames.remove(namenum);
+ }
+
+ public short getIndexToSheet(short num) {
+ return _externSheetRecord.getREFRecordAt(num).getIndexToFirstSupBook();
+ }
+
+ public int getSheetIndexFromExternSheetIndex(int externSheetNumber) {
+ if (externSheetNumber >= _externSheetRecord.getNumOfREFStructures()) {
+ return -1;
+ }
+ return _externSheetRecord.getREFRecordAt(externSheetNumber).getIndexToFirstSupBook();
+ }
+
+ public short addSheetIndexToExternSheet(short sheetNumber) {
+
+ ExternSheetSubRecord record = new ExternSheetSubRecord();
+ record.setIndexToFirstSupBook(sheetNumber);
+ record.setIndexToLastSupBook(sheetNumber);
+ _externSheetRecord.addREFRecord(record);
+ _externSheetRecord.setNumOfREFStructures((short)(_externSheetRecord.getNumOfREFStructures() + 1));
+ return (short)(_externSheetRecord.getNumOfREFStructures() - 1);
+ }
+
+ public short checkExternSheet(int sheetNumber) {
+
+ //Trying to find reference to this sheet
+ int nESRs = _externSheetRecord.getNumOfREFStructures();
+ for(short i=0; i< nESRs; i++) {
+ ExternSheetSubRecord esr = _externSheetRecord.getREFRecordAt(i);
+
+ if (esr.getIndexToFirstSupBook() == sheetNumber
+ && esr.getIndexToLastSupBook() == sheetNumber){
+ return i;
+ }
+ }
+
+ //We Haven't found reference to this sheet
+ return addSheetIndexToExternSheet((short) sheetNumber);
+ }
+
+
+ /**
+ * copied from Workbook
+ */
+ private int findFirstRecordLocBySid(short sid) {
+ int index = 0;
+ for (Iterator iterator = _workbookRecordList.iterator(); iterator.hasNext(); ) {
+ Record record = ( Record ) iterator.next();
+
+ if (record.getSid() == sid) {
+ return index;
+ }
+ index ++;
+ }
+ return -1;
+ }
+
+ public int getNumberOfREFStructures() {
+ return _externSheetRecord.getNumOfREFStructures();
+ }
+
+ public String resolveNameXText(int refIndex, int definedNameIndex) {
+ short extBookIndex = _externSheetRecord.getREFRecordAt(refIndex).getIndexToSupBook();
+ return _externalBookBlocks[extBookIndex].getNameText(definedNameIndex);
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/model/RecordStream.java b/src/java/org/apache/poi/hssf/model/RecordStream.java
new file mode 100755
index 000000000..03177c7c2
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/model/RecordStream.java
@@ -0,0 +1,65 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hssf.model;
+
+import java.util.List;
+
+import org.apache.poi.hssf.record.Record;
+/**
+ * Simplifies iteration over a sequence of Record objects.
+ *
+ * @author Josh Micich
+ */
+final class RecordStream {
+
+ private final List _list;
+ private int _nextIndex;
+ private int _countRead;
+
+ public RecordStream(List inputList, int startIndex) {
+ _list = inputList;
+ _nextIndex = startIndex;
+ _countRead = 0;
+ }
+
+ public boolean hasNext() {
+ return _nextIndex < _list.size();
+ }
+
+ public Record getNext() {
+ if(_nextIndex >= _list.size()) {
+ throw new RuntimeException("Attempt to read past end of record stream");
+ }
+ _countRead ++;
+ return (Record) _list.get(_nextIndex++);
+ }
+
+ /**
+ * @return the {@link Class} of the next Record.
+ *
+ *
+ *
+ * null
if this stream is exhausted.
+ */
+ public Class peekNextClass() {
+ if(_nextIndex >= _list.size()) {
+ return null;
+ }
+ return _list.get(_nextIndex).getClass();
+ }
+
+ public int getCountRead() {
+ return _countRead;
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/model/Workbook.java b/src/java/org/apache/poi/hssf/model/Workbook.java
index 2ba50857c..9bf1c8f47 100644
--- a/src/java/org/apache/poi/hssf/model/Workbook.java
+++ b/src/java/org/apache/poi/hssf/model/Workbook.java
@@ -79,10 +79,8 @@ public class Workbook implements Model
*/
protected SSTRecord sst = null;
- /**
- * Holds the Extern Sheet with references to bound sheets
- */
- protected ExternSheetRecord externSheet= null;
+
+ private LinkTable linkTable; // optionally occurs if there are references in the document. (4.10.3)
/**
* holds the "boundsheet" records (aka bundlesheet) so that they can have their
@@ -92,8 +90,6 @@ public class Workbook implements Model
protected ArrayList formats = new ArrayList();
- protected ArrayList names = new ArrayList();
-
protected ArrayList hyperlinks = new ArrayList();
protected int numxfs = 0; // hold the number of extended format records
@@ -134,6 +130,7 @@ public class Workbook implements Model
new Integer(recs.size()));
Workbook retval = new Workbook();
ArrayList records = new ArrayList(recs.size() / 3);
+ retval.records.setRecords(records);
int k;
for (k = 0; k < recs.size(); k++) {
@@ -192,21 +189,16 @@ public class Workbook implements Model
retval.records.setBackuppos( k );
break;
case ExternSheetRecord.sid :
- if (log.check( POILogger.DEBUG ))
- log.log(DEBUG, "found extern sheet record at " + k);
- retval.externSheet = ( ExternSheetRecord ) rec;
- break;
+ throw new RuntimeException("Extern sheet is part of LinkTable");
case NameRecord.sid :
- if (log.check( POILogger.DEBUG ))
- log.log(DEBUG, "found name record at " + k);
- retval.names.add(rec);
- // retval.records.namepos = k;
- break;
+ throw new RuntimeException("DEFINEDNAME is part of LinkTable");
case SupBookRecord.sid :
if (log.check( POILogger.DEBUG ))
log.log(DEBUG, "found SupBook record at " + k);
+ retval.linkTable = new LinkTable(recs, k, retval.records);
// retval.records.supbookpos = k;
- break;
+ k+=retval.linkTable.getRecordCount() - 1;
+ continue;
case FormatRecord.sid :
if (log.check( POILogger.DEBUG ))
log.log(DEBUG, "found format record at " + k);
@@ -262,8 +254,6 @@ public class Workbook implements Model
break;
}
}
-
- retval.records.setRecords(records);
if (retval.windowOne == null) {
retval.windowOne = (WindowOneRecord) retval.createWindowOne();
@@ -283,6 +273,7 @@ public class Workbook implements Model
log.log( DEBUG, "creating new workbook from scratch" );
Workbook retval = new Workbook();
ArrayList records = new ArrayList( 30 );
+ retval.records.setRecords(records);
ArrayList formats = new ArrayList( 8 );
records.add( retval.createBOF() );
@@ -339,8 +330,9 @@ public class Workbook implements Model
records.add( retval.createStyle( k ) );
}
records.add( retval.createUseSelFS() );
- for ( int k = 0; k < 1; k++ )
- { // now just do 1
+
+ int nBoundSheets = 1; // now just do 1
+ for ( int k = 0; k < nBoundSheets; k++ ) {
BoundSheetRecord bsr =
(BoundSheetRecord) retval.createBoundSheet( k );
@@ -351,12 +343,14 @@ public class Workbook implements Model
// retval.records.supbookpos = retval.records.bspos + 1;
// retval.records.namepos = retval.records.supbookpos + 2;
records.add( retval.createCountry() );
+ for ( int k = 0; k < nBoundSheets; k++ ) {
+ retval.getOrCreateLinkTable().checkExternSheet(k);
+ }
retval.sst = (SSTRecord) retval.createSST();
records.add( retval.sst );
records.add( retval.createExtendedSST() );
records.add( retval.createEOF() );
- retval.records.setRecords(records);
if (log.check( POILogger.DEBUG ))
log.log( DEBUG, "exit create new workbook from scratch" );
return retval;
@@ -369,36 +363,20 @@ public class Workbook implements Model
* @param sheetIndex Index to match
* @return null if no builtin NameRecord matches
*/
- public NameRecord getSpecificBuiltinRecord(byte name, int sheetIndex)
- {
- Iterator iterator = names.iterator();
- while (iterator.hasNext()) {
- NameRecord record = ( NameRecord ) iterator.next();
-
- //print areas are one based
- if (record.getBuiltInName() == name && record.getIndexToSheet() == sheetIndex) {
- return record;
- }
- }
-
- return null;
-
- }
+ public NameRecord getSpecificBuiltinRecord(byte name, int sheetIndex)
+ {
+ return getOrCreateLinkTable().getSpecificBuiltinRecord(name, sheetIndex);
+ }
/**
* Removes the specified Builtin NameRecord that matches the name and index
* @param name byte representation of the builtin to match
* @param sheetIndex zero-based sheet reference
*/
- public void removeBuiltinRecord(byte name, int sheetIndex) {
- //the name array is smaller so searching through it should be faster than
- //using the findFirstXXXX methods
- NameRecord record = getSpecificBuiltinRecord(name, sheetIndex);
- if (record != null) {
- names.remove(record);
- }
-
- }
+ public void removeBuiltinRecord(byte name, int sheetIndex) {
+ linkTable.removeBuiltinRecord(name, sheetIndex);
+ // TODO - do we need "this.records.remove(...);" similar to that in this.removeName(int namenum) {}?
+ }
public int getNumRecords() {
return records.size();
@@ -614,6 +592,7 @@ public class Workbook implements Model
records.add(records.getBspos()+1, bsr);
records.setBspos( records.getBspos() + 1 );
boundsheets.add(bsr);
+ getOrCreateLinkTable().checkExternSheet(sheetnum);
fixTabIdRecord();
}
}
@@ -1824,14 +1803,26 @@ public class Workbook implements Model
protected Record createEOF() {
return new EOFRecord();
}
+
+ /**
+ * lazy initialization
+ * Note - creating the link table causes creation of 1 EXTERNALBOOK and 1 EXTERNALSHEET record
+ */
+ private LinkTable getOrCreateLinkTable() {
+ if(linkTable == null) {
+ linkTable = new LinkTable((short) getNumSheets(), records);
+ }
+ return linkTable;
+ }
public SheetReferences getSheetReferences() {
SheetReferences refs = new SheetReferences();
- if (externSheet != null) {
- for (int k = 0; k < externSheet.getNumOfREFStructures(); k++) {
+ if (linkTable != null) {
+ int numRefStructures = linkTable.getNumberOfREFStructures();
+ for (short k = 0; k < numRefStructures; k++) {
- String sheetName = findSheetNameFromExternSheet((short)k);
+ String sheetName = findSheetNameFromExternSheet(k);
refs.addSheetReference(sheetName, k);
}
@@ -1846,7 +1837,8 @@ public class Workbook implements Model
public String findSheetNameFromExternSheet(short num){
String result="";
- short indexToSheet = externSheet.getREFRecordAt(num).getIndexToFirstSupBook();
+ short indexToSheet = linkTable.getIndexToSheet(num);
+
if (indexToSheet>-1) { //error check, bail out gracefully!
result = getSheetName(indexToSheet);
}
@@ -1861,10 +1853,7 @@ public class Workbook implements Model
*/
public int getSheetIndexFromExternSheetIndex(int externSheetNumber)
{
- if (externSheetNumber >= externSheet.getNumOfREFStructures())
- return -1;
- else
- return externSheet.getREFRecordAt(externSheetNumber).getIndexToFirstSupBook();
+ return linkTable.getSheetIndexFromExternSheetIndex(externSheetNumber);
}
/** returns the extern sheet number for specific sheet number ,
@@ -1873,58 +1862,17 @@ public class Workbook implements Model
* @return index to extern sheet
*/
public short checkExternSheet(int sheetNumber){
-
- int i = 0;
- boolean flag = false;
- short result = 0;
-
- if (externSheet == null) {
- externSheet = createExternSheet();
- }
-
- //Trying to find reference to this sheet
- while (i < externSheet.getNumOfREFStructures() && !flag){
- ExternSheetSubRecord record = externSheet.getREFRecordAt(i);
-
- if (record.getIndexToFirstSupBook() == sheetNumber &&
- record.getIndexToLastSupBook() == sheetNumber){
- flag = true;
- result = (short) i;
- }
-
- ++i;
- }
-
- //We Havent found reference to this sheet
- if (!flag) {
- result = addSheetIndexToExternSheet((short) sheetNumber);
- }
-
- return result;
+ return getOrCreateLinkTable().checkExternSheet(sheetNumber);
}
- private short addSheetIndexToExternSheet(short sheetNumber){
- short result;
-
- ExternSheetSubRecord record = new ExternSheetSubRecord();
- record.setIndexToFirstSupBook(sheetNumber);
- record.setIndexToLastSupBook(sheetNumber);
- externSheet.addREFRecord(record);
- externSheet.setNumOfREFStructures((short)(externSheet.getNumOfREFStructures() + 1));
- result = (short)(externSheet.getNumOfREFStructures() - 1);
-
- return result;
- }
-
-
-
/** gets the total number of names
* @return number of names
*/
public int getNumNames(){
- int result = names.size();
-
- return result;
+ if(linkTable == null) {
+ return 0;
+ }
+ return linkTable.getNumNames();
}
/** gets the name record
@@ -1932,28 +1880,14 @@ public class Workbook implements Model
* @return name record
*/
public NameRecord getNameRecord(int index){
- NameRecord result = (NameRecord) names.get(index);
-
- return result;
-
+ return linkTable.getNameRecord(index);
}
/** creates new name
* @return new name record
*/
public NameRecord createName(){
-
- NameRecord name = new NameRecord();
-
- // Not the most efficient way but the other way was causing too many bugs
- int idx = findFirstRecordLocBySid(ExternSheetRecord.sid);
- if (idx == -1) idx = findFirstRecordLocBySid(SupBookRecord.sid);
- if (idx == -1) idx = findFirstRecordLocBySid(CountryRecord.sid);
-
- records.add(idx+names.size()+1, name);
- names.add(name);
-
- return name;
+ return addName(new NameRecord());
}
@@ -1962,67 +1896,41 @@ public class Workbook implements Model
*/
public NameRecord addName(NameRecord name)
{
- // Not the most efficient way but the other way was causing too many bugs
- int idx = findFirstRecordLocBySid(ExternSheetRecord.sid);
- if (idx == -1) idx = findFirstRecordLocBySid(SupBookRecord.sid);
- if (idx == -1) idx = findFirstRecordLocBySid(CountryRecord.sid);
- records.add(idx+names.size()+1, name);
- names.add(name);
+
+ getOrCreateLinkTable().addName(name);
return name;
}
- /**Generates a NameRecord to represent a built-in region
- * @return a new NameRecord unless the index is invalid
- */
- public NameRecord createBuiltInName(byte builtInName, int index)
- {
- if (index == -1 || index+1 > (int)Short.MAX_VALUE)
- throw new IllegalArgumentException("Index is not valid ["+index+"]");
-
- NameRecord name = new NameRecord(builtInName, (short)(index));
-
- addName(name);
-
- return name;
- }
+ /**Generates a NameRecord to represent a built-in region
+ * @return a new NameRecord unless the index is invalid
+ */
+ public NameRecord createBuiltInName(byte builtInName, int index)
+ {
+ if (index == -1 || index+1 > Short.MAX_VALUE)
+ throw new IllegalArgumentException("Index is not valid ["+index+"]");
+
+ NameRecord name = new NameRecord(builtInName, (short)(index));
+
+ addName(name);
+
+ return name;
+ }
/** removes the name
* @param namenum name index
*/
public void removeName(int namenum){
- if (names.size() > namenum) {
+
+ if (linkTable.getNumNames() > namenum) {
int idx = findFirstRecordLocBySid(NameRecord.sid);
records.remove(idx + namenum);
- names.remove(namenum);
+ linkTable.removeName(namenum);
}
}
- /** creates a new extern sheet record
- * @return the new extern sheet record
- */
- protected ExternSheetRecord createExternSheet(){
- ExternSheetRecord externSheet = new ExternSheetRecord();
-
- int idx = findFirstRecordLocBySid(CountryRecord.sid);
-
- records.add(idx+1, externSheet);
-// records.add(records.supbookpos + 1 , rec);
-
- //We also adds the supBook for internal reference
- SupBookRecord supbook = new SupBookRecord();
-
- supbook.setNumberOfSheets((short)getNumSheets());
- //supbook.setFlag();
-
- records.add(idx+1, supbook);
-// records.add(records.supbookpos + 1 , supbook);
-
- return externSheet;
- }
-
/**
* Returns a format index that matches the passed in format. It does not tie into HSSFDataFormat.
* @param format the format string
@@ -2419,5 +2327,14 @@ public class Workbook implements Model
writeProtect = null;
}
+ /**
+ * @param refIndex Index to REF entry in EXTERNSHEET record in the Link Table
+ * @param definedNameIndex zero-based to DEFINEDNAME or EXTERNALNAME record
+ * @return the string representation of the defined or external name
+ */
+ public String resolveNameXText(int refIndex, int definedNameIndex) {
+ return linkTable.resolveNameXText(refIndex, definedNameIndex);
+ }
}
+
diff --git a/src/java/org/apache/poi/hssf/record/CRNCountRecord.java b/src/java/org/apache/poi/hssf/record/CRNCountRecord.java
new file mode 100755
index 000000000..4c9e4425c
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/CRNCountRecord.java
@@ -0,0 +1,94 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hssf.record;
+
+import org.apache.poi.util.LittleEndian;
+/**
+ * XCT – CRN Count true
, this denotes the 'StdDocumentName'
+ */
+ public boolean isStdDocumentNameIdentifier() {
+ return (field_1_option_flag & OPT_STD_DOCUMENT_NAME) != 0;
+ }
+ public boolean isOLELink() {
+ return (field_1_option_flag & OPT_OLE_LINK) != 0;
+ }
+ public boolean isIconifiedPictureLink() {
+ return (field_1_option_flag & OPT_ICONIFIED_PICTURE_LINK) != 0;
+ }
+ /**
+ * @return the standard String representation of this name
+ */
+ public String getText() {
+ return field_4_name;
+ }
+
+
+ /**
+ * called by constructor, should throw runtime exception in the event of a
+ * record passed with a differing ID.
+ *
+ * @param id alleged id for this record
+ */
+ protected void validateSid(short id) {
+ if (id != sid) {
+ throw new RecordFormatException("NOT A valid ExternalName RECORD");
+ }
+ }
+
+ private int getDataSize(){
+ return 2 + 2 + field_4_name.length() + 2 + getNameDefinitionSize();
+ }
+
+ /**
+ * called by the class that is responsible for writing this sucker.
+ * Subclasses should implement this so that their data is passed back in a
+ * byte array.
+ *
+ * @param offset to begin writing at
+ * @param data byte array containing instance data
+ * @return number of bytes written
+ */
+ public int serialize( int offset, byte[] data ) {
+ // TODO - junit tests
+ int dataSize = getDataSize();
+
+ LittleEndian.putShort( data, 0 + offset, sid );
+ LittleEndian.putShort( data, 2 + offset, (short) dataSize );
+ LittleEndian.putShort( data, 4 + offset, field_1_option_flag );
+ LittleEndian.putShort( data, 6 + offset, field_2_index );
+ LittleEndian.putShort( data, 8 + offset, field_3_not_used );
+ short nameLen = (short) field_4_name.length();
+ LittleEndian.putShort( data, 10 + offset, nameLen );
+ StringUtil.putCompressedUnicode( field_4_name, data, 10 + offset );
+ short defLen = (short) getNameDefinitionSize();
+ LittleEndian.putShort( data, 12 + nameLen + offset, defLen );
+ Ptg.serializePtgStack(field_5_name_definition, data, 12 + nameLen + offset );
+ return dataSize + 4;
+ }
+
+ private int getNameDefinitionSize() {
+ int result = 0;
+ List list = field_5_name_definition;
+
+ for (int k = 0; k < list.size(); k++)
+ {
+ Ptg ptg = ( Ptg ) list.get(k);
+
+ result += ptg.getSize();
+ }
+ return result;
+ }
+
+
+ public int getRecordSize(){
+ return 6 + 2 + field_4_name.length() + 2 + getNameDefinitionSize();
+ }
+
+
+ protected void fillFields(RecordInputStream in) {
+ field_1_option_flag = in.readShort();
+ field_2_index = in.readShort();
+ field_3_not_used = in.readShort();
+ short nameLength = in.readShort();
+ field_4_name = in.readCompressedUnicode(nameLength);
+ short formulaLen = in.readShort();
+ field_5_name_definition = Ptg.createParsedExpressionTokens(formulaLen, in);
+ }
+
+ public short getSid() {
+ return sid;
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append(getClass().getName()).append(" [EXTERNALNAME ");
+ sb.append(" ").append(field_4_name);
+ sb.append(" ix=").append(field_2_index);
+ sb.append("]");
+ return sb.toString();
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/record/RecordFactory.java b/src/java/org/apache/poi/hssf/record/RecordFactory.java
index 0f164b447..f247a8d4b 100644
--- a/src/java/org/apache/poi/hssf/record/RecordFactory.java
+++ b/src/java/org/apache/poi/hssf/record/RecordFactory.java
@@ -77,7 +77,11 @@ public class RecordFactory
NoteRecord.class, ObjectProtectRecord.class, ScenarioProtectRecord.class,
FileSharingRecord.class, ChartTitleFormatRecord.class,
DVRecord.class, DVALRecord.class, UncalcedRecord.class,
- HyperlinkRecord.class
+ HyperlinkRecord.class,
+ ExternalNameRecord.class, // TODO - same changes in non-@deprecated version of this class
+ SupBookRecord.class,
+ CRNCountRecord.class,
+ CRNRecord.class,
};
}
private static Map recordsMap = recordsToMap(records);
diff --git a/src/java/org/apache/poi/hssf/record/SupBookRecord.java b/src/java/org/apache/poi/hssf/record/SupBookRecord.java
index 91a16f079..6755aa6f8 100644
--- a/src/java/org/apache/poi/hssf/record/SupBookRecord.java
+++ b/src/java/org/apache/poi/hssf/record/SupBookRecord.java
@@ -1,4 +1,3 @@
-
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@@ -15,72 +14,159 @@
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
-
package org.apache.poi.hssf.record;
+import org.apache.poi.hssf.record.UnicodeString.UnicodeRecordStats;
import org.apache.poi.util.LittleEndian;
/**
- * Title: Sup Book true
if this is an external function
+ */
+ public boolean isExternalFunction() {
+ return field_2_fnc_index == FUNCTION_INDEX_EXTERNAL;
+ }
public String toFormulaString(Workbook book) {
return getName();
@@ -73,39 +83,57 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
public String toFormulaString(String[] operands) {
StringBuffer buf = new StringBuffer();
-
- if (field_2_fnc_index != 1) {
- buf.append(getName());
- buf.append('(');
- }
- if (operands.length >0) {
- for (int i=0;itrue
if the name specifies a standard worksheet function,
+ * false
if the name should be assumed to be an external function.
+ */
+ public static final boolean isInternalFunctionName(String name) {
+ return map.containsValue(name.toUpperCase());
+ }
protected String lookupName(short index) {
return ((String)map.get(new Integer(index)));
}
- protected short lookupIndex(String name) {
- Integer index = (Integer) map.getKeyForValue(name);
+ /**
+ * Resolves internal function names into function indexes.
+ * true
if the specified value is within the range of values
+ * IntPtg can represent.
+ */
+ public static boolean isInRange(int i) {
+ return i>=MIN_VALUE && i <=MAX_VALUE;
+ }
-public class IntPtg
- extends Ptg
-{
public final static int SIZE = 3;
public final static byte sid = 0x1e;
private int field_1_value;
- private IntPtg() {
- //Required for clone methods
+ public IntPtg(RecordInputStream in) {
+ this(in.readUShort());
}
- public IntPtg(RecordInputStream in)
- {
- setValue(in.readUShort());
- }
-
-
- // IntPtg should be able to create itself, shouldnt have to call setValue
- public IntPtg(String formulaToken) {
- setValue(Integer.parseInt(formulaToken));
- }
- /**
- * Sets the wrapped value.
- * Normally you should call with a positive int.
- */
- public void setValue(int value)
- {
- if(value < 0 || value > (Short.MAX_VALUE + 1)*2 )
- throw new IllegalArgumentException("Unsigned short is out of range: " + value);
+ public IntPtg(int value) {
+ if(!isInRange(value)) {
+ throw new IllegalArgumentException("value is out of range: " + value);
+ }
field_1_value = value;
}
- /**
- * Returns the value as a short, which may have
- * been wrapped into negative numbers
- */
- public int getValue()
- {
+ public int getValue() {
return field_1_value;
}
- /**
- * Returns the value as an unsigned positive int.
- */
- public int getValueAsInt()
- {
- if(field_1_value < 0) {
- return (Short.MAX_VALUE + 1)*2 + field_1_value;
- }
- return field_1_value;
- }
public void writeBytes(byte [] array, int offset)
{
@@ -94,20 +68,25 @@ public class IntPtg
LittleEndian.putUShort(array, offset + 1, getValue());
}
- public int getSize()
- {
+ public int getSize() {
return SIZE;
}
- public String toFormulaString(Workbook book)
- {
- return "" + getValue();
+ public String toFormulaString(Workbook book) {
+ return String.valueOf(getValue());
+ }
+ public byte getDefaultOperandClass() {
+ return Ptg.CLASS_VALUE;
}
- public byte getDefaultOperandClass() {return Ptg.CLASS_VALUE;}
- public Object clone() {
- IntPtg ptg = new IntPtg();
- ptg.field_1_value = field_1_value;
- return ptg;
- }
+ public Object clone() {
+ return new IntPtg(field_1_value);
+ }
+ public String toString() {
+ StringBuffer sb = new StringBuffer(64);
+ sb.append(getClass().getName()).append(" [");
+ sb.append(field_1_value);
+ sb.append("]");
+ return sb.toString();
+ }
}
diff --git a/src/java/org/apache/poi/hssf/record/formula/NamePtg.java b/src/java/org/apache/poi/hssf/record/formula/NamePtg.java
index 01323c3a0..5405481a0 100644
--- a/src/java/org/apache/poi/hssf/record/formula/NamePtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/NamePtg.java
@@ -33,6 +33,7 @@ public class NamePtg
{
public final static short sid = 0x23;
private final static int SIZE = 5;
+ /** one-based index to defined name record */
private short field_1_label_index;
private short field_2_zero; // reserved must be 0
boolean xtra=false;
@@ -42,24 +43,32 @@ public class NamePtg
//Required for clone methods
}
- /** Creates new NamePtg */
-
- public NamePtg(String name, Workbook book)
- {
- final short n = (short) (book.getNumNames() + 1);
+ /**
+ * Creates new NamePtg and sets its name index to that of the corresponding defined name record
+ * in the workbook. The search for the name record is case insensitive. If it is not found,
+ * it gets created.
+ */
+ public NamePtg(String name, Workbook book) {
+ field_1_label_index = (short)(1+getOrCreateNameRecord(book, name)); // convert to 1-based
+ }
+ /**
+ * @return zero based index of the found or newly created defined name record.
+ */
+ private static final int getOrCreateNameRecord(Workbook book, String name) {
+ // perhaps this logic belongs in Workbook
+ int countNames = book.getNumNames();
NameRecord rec;
- for (short i = 1; i < n; i++) {
- rec = book.getNameRecord(i - 1);
+ for (int i = 0; i < countNames; i++) {
+ rec = book.getNameRecord(i);
if (name.equalsIgnoreCase(rec.getNameText())) {
- field_1_label_index = i;
- return;
+ return i;
}
}
rec = new NameRecord();
rec.setNameText(name);
rec.setNameTextLength((byte) name.length());
book.addName(rec);
- field_1_label_index = n;
+ return countNames;
}
/** Creates new NamePtg */
@@ -71,6 +80,13 @@ public class NamePtg
field_2_zero = in.readShort();
//if (data[offset+6]==0) xtra=true;
}
+
+ /**
+ * @return zero based index to a defined name record in the LinkTable
+ */
+ public int getIndex() {
+ return field_1_label_index-1; // convert to zero based
+ }
public void writeBytes(byte [] array, int offset)
{
diff --git a/src/java/org/apache/poi/hssf/record/formula/NameXPtg.java b/src/java/org/apache/poi/hssf/record/formula/NameXPtg.java
index b1f280a01..ccf5ab6fc 100644
--- a/src/java/org/apache/poi/hssf/record/formula/NameXPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/NameXPtg.java
@@ -25,13 +25,11 @@ import org.apache.poi.hssf.record.RecordInputStream;
*
* @author aviks
*/
-
-public class NameXPtg extends Ptg
-{
+public final class NameXPtg extends Ptg {
public final static short sid = 0x39;
private final static int SIZE = 7;
- private short field_1_ixals; // index to externsheet record
- private short field_2_ilbl; //index to name or externname table(1 based)
+ private short field_1_ixals; // index to REF entry in externsheet record
+ private short field_2_ilbl; //index to defined name or externname table(1 based)
private short field_3_reserved; // reserved must be 0
@@ -41,13 +39,6 @@ public class NameXPtg extends Ptg
/** Creates new NamePtg */
- public NameXPtg(String name)
- {
- //TODO
- }
-
- /** Creates new NamePtg */
-
public NameXPtg(RecordInputStream in)
{
field_1_ixals = in.readShort();
@@ -72,7 +63,8 @@ public class NameXPtg extends Ptg
public String toFormulaString(Workbook book)
{
- return "NO IDEA - NAME";
+ // -1 to convert definedNameIndex from 1-based to zero-based
+ return book.resolveNameXText(field_1_ixals, field_2_ilbl-1);
}
public byte getDefaultOperandClass() {return Ptg.CLASS_VALUE;}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
index 05cee4e8d..17bae63ca 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
@@ -639,7 +639,7 @@ public class HSSFCell
//only set to default if there is no extended format index already set
if (rec.getXFIndex() == (short)0) rec.setXFIndex(( short ) 0x0f);
- FormulaParser fp = new FormulaParser(formula+";",book);
+ FormulaParser fp = new FormulaParser(formula, book);
fp.parse();
Ptg[] ptg = fp.getRPNPtg();
int size = 0;
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
index 9f7b17ad8..8c7590d4e 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
@@ -400,7 +400,7 @@ public class HSSFSheet
//formula fields ( size and data )
String str_formula = obj_validation.getFirstFormula();
- FormulaParser fp = new FormulaParser(str_formula+";",book);
+ FormulaParser fp = new FormulaParser(str_formula, book);
fp.parse();
Stack ptg_arr = new Stack();
Ptg[] ptg = fp.getRPNPtg();
@@ -424,7 +424,7 @@ public class HSSFSheet
if ( obj_validation.getSecondFormula() != null )
{
str_formula = obj_validation.getSecondFormula();
- fp = new FormulaParser(str_formula+";",book);
+ fp = new FormulaParser(str_formula, book);
fp.parse();
ptg_arr = new Stack();
ptg = fp.getRPNPtg();
diff --git a/src/java/org/apache/poi/util/LittleEndian.java b/src/java/org/apache/poi/util/LittleEndian.java
index b883d71b1..2278649cb 100644
--- a/src/java/org/apache/poi/util/LittleEndian.java
+++ b/src/java/org/apache/poi/util/LittleEndian.java
@@ -245,6 +245,16 @@ public class LittleEndian
putNumber(data, offset, value, SHORT_SIZE);
}
+ /**
+ * executes:
+ * data[offset] = (byte)value;
+ *
* " 123 " -> 123.0
* ".123" -> 0.123
+ * These not supported yet:
* " $ 1,000.00 " -> 1000.0
* "$1.25E4" -> 12500.0
+ * "5**2" -> 500
+ * "250%" -> 2.5
*
* @param text
* @return null
if the specified text cannot be parsed as a number
*/
- public static Double parseDouble(String text) {
+ public static Double parseDouble(String pText) {
+ String text = pText.trim();
+ if(text.length() < 1) {
+ return null;
+ }
+ boolean isPositive = true;
+ if(text.charAt(0) == '-') {
+ isPositive = false;
+ text= text.substring(1).trim();
+ }
+
if(!Character.isDigit(text.charAt(0))) {
// avoid using NumberFormatException to tell when string is not a number
return null;
@@ -242,8 +252,26 @@ public final class OperandResolver {
} catch (NumberFormatException e) {
return null;
}
- return new Double(val);
+ return new Double(isPositive ? +val : -val);
}
-
+ /**
+ * @param ve must be a NumberEval, StringEval, BoolEval, or BlankEval
+ * @return the converted string value. never null
+ */
+ public static String coerceValueToString(ValueEval ve) {
+ if (ve instanceof StringValueEval) {
+ StringValueEval sve = (StringValueEval) ve;
+ return sve.getStringValue();
+ }
+ if (ve instanceof NumberEval) {
+ NumberEval neval = (NumberEval) ve;
+ return neval.getStringValue();
+ }
+
+ if (ve instanceof BlankEval) {
+ return "";
+ }
+ throw new IllegalArgumentException("Unexpected eval class (" + ve.getClass().getName() + ")");
+ }
}
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/PowerEval.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/PowerEval.java
index 437c24e40..651c5d2aa 100644
--- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/PowerEval.java
+++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/PowerEval.java
@@ -14,10 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-/*
- * Created on May 8, 2005
- *
- */
+
package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.Ptg;
@@ -27,15 +24,13 @@ import org.apache.poi.hssf.record.formula.PowerPtg;
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
*
*/
-public class PowerEval extends NumericOperationEval {
+public final class PowerEval extends NumericOperationEval {
private PowerPtg delegate;
private static final ValueEvalToNumericXlator NUM_XLATOR =
new ValueEvalToNumericXlator((short)
( ValueEvalToNumericXlator.BOOL_IS_PARSED
- | ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
- | ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
@@ -49,48 +44,40 @@ public class PowerEval extends NumericOperationEval {
return NUM_XLATOR;
}
- public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
- Eval retval = null;
+ public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
+ if(args.length != 2) {
+ return ErrorEval.VALUE_INVALID;
+ }
double d0 = 0;
double d1 = 0;
- switch (operands.length) {
- default: // will rarely happen. currently the parser itself fails.
- retval = ErrorEval.UNKNOWN_ERROR;
- break;
- case 2:
- ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
- if (ve instanceof NumericValueEval) {
- d0 = ((NumericValueEval) ve).getNumberValue();
- }
- else if (ve instanceof BlankEval) {
- // do nothing
- }
- else {
- retval = ErrorEval.VALUE_INVALID;
- }
-
- if (retval == null) { // no error yet
- ve = singleOperandEvaluate(operands[1], srcRow, srcCol);
- if (ve instanceof NumericValueEval) {
- d1 = ((NumericValueEval) ve).getNumberValue();
- }
- else if (ve instanceof BlankEval) {
- // do nothing
- }
- else {
- retval = ErrorEval.VALUE_INVALID;
- }
- }
- } // end switch
-
- if (retval == null) {
- double p = Math.pow(d0, d1);
- retval = (Double.isNaN(p))
- ? (ValueEval) ErrorEval.VALUE_INVALID
- : new NumberEval(p);
+ ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol);
+ if (ve instanceof NumericValueEval) {
+ d0 = ((NumericValueEval) ve).getNumberValue();
}
- return retval;
+ else if (ve instanceof BlankEval) {
+ // do nothing
+ }
+ else {
+ return ErrorEval.VALUE_INVALID;
+ }
+
+ ve = singleOperandEvaluate(args[1], srcRow, srcCol);
+ if (ve instanceof NumericValueEval) {
+ d1 = ((NumericValueEval) ve).getNumberValue();
+ }
+ else if (ve instanceof BlankEval) {
+ // do nothing
+ }
+ else {
+ return ErrorEval.VALUE_INVALID;
+ }
+
+ double p = Math.pow(d0, d1);
+ if (Double.isNaN(p)) {
+ return ErrorEval.VALUE_INVALID;
+ }
+ return new NumberEval(p);
}
public int getNumberOfOperands() {
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/Ref2DEval.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/Ref2DEval.java
index 7b24cb062..4dd055c6c 100644
--- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/Ref2DEval.java
+++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/Ref2DEval.java
@@ -14,47 +14,31 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-/*
- * Created on May 9, 2005
- *
- */
+
package org.apache.poi.hssf.record.formula.eval;
-import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.ReferencePtg;
/**
* @author adeshmukh
*
*/
-public class Ref2DEval implements RefEval {
+public final class Ref2DEval implements RefEval {
- private ValueEval value;
-
- private ReferencePtg delegate;
+ private final ValueEval value;
+ private final ReferencePtg delegate;
- private boolean evaluated;
-
- public Ref2DEval(Ptg ptg, ValueEval value, boolean evaluated) {
- this.value = value;
- this.delegate = (ReferencePtg) ptg;
- this.evaluated = evaluated;
+ public Ref2DEval(ReferencePtg ptg, ValueEval ve) {
+ value = ve;
+ delegate = ptg;
}
-
public ValueEval getInnerValueEval() {
return value;
}
-
public short getRow() {
return delegate.getRow();
}
-
public short getColumn() {
return delegate.getColumn();
}
-
- public boolean isEvaluated() {
- return evaluated;
- }
-
}
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/Ref3DEval.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/Ref3DEval.java
index dcf8f0f71..7ecca2678 100644
--- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/Ref3DEval.java
+++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/Ref3DEval.java
@@ -14,13 +14,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-/*
- * Created on May 9, 2005
- *
- */
+
package org.apache.poi.hssf.record.formula.eval;
-import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.Ref3DPtg;
/**
@@ -29,36 +25,23 @@ import org.apache.poi.hssf.record.formula.Ref3DPtg;
*/
public final class Ref3DEval implements RefEval {
- private ValueEval value;
+ private final ValueEval value;
+ private final Ref3DPtg delegate;
- private Ref3DPtg delegate;
-
- private boolean evaluated;
-
- public Ref3DEval(Ptg ptg, ValueEval value, boolean evaluated) {
- this.value = value;
- this.delegate = (Ref3DPtg) ptg;
- this.evaluated = evaluated;
+ public Ref3DEval(Ref3DPtg ptg, ValueEval ve) {
+ value = ve;
+ delegate = ptg;
}
-
public ValueEval getInnerValueEval() {
return value;
}
-
public short getRow() {
return delegate.getRow();
}
-
public short getColumn() {
return delegate.getColumn();
}
-
- public boolean isEvaluated() {
- return evaluated;
- }
-
public int getExternSheetIndex() {
return delegate.getExternSheetIndex();
}
-
}
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/RefEval.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/RefEval.java
index bb72adc4a..48a94b024 100644
--- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/RefEval.java
+++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/RefEval.java
@@ -14,11 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-/*
- * Created on May 9, 2005
- *
- *
- */
+
package org.apache.poi.hssf.record.formula.eval;
/**
@@ -52,18 +48,4 @@ public interface RefEval extends ValueEval {
* returns the row index.
*/
public short getRow();
-
- /**
- * returns true if this RefEval contains an
- * evaluated value instead of a direct value.
- * eg. say cell A1 has the value: ="test"
- * Then the RefEval representing A1 will return
- * isEvaluated() equal to false. On the other
- * hand, say cell A1 has the value: =B1 and
- * B1 has the value "test", then the RefEval
- * representing A1 will return isEvaluated()
- * equal to true.
- */
- public boolean isEvaluated();
-
}
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/SubtractEval.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/SubtractEval.java
index 4bd77029f..85a384529 100644
--- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/SubtractEval.java
+++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/SubtractEval.java
@@ -14,10 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-/*
- * Created on May 8, 2005
- *
- */
+
package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.Ptg;
@@ -27,15 +24,13 @@ import org.apache.poi.hssf.record.formula.SubtractPtg;
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
*
*/
-public class SubtractEval extends NumericOperationEval {
+public final class SubtractEval extends NumericOperationEval {
private SubtractPtg delegate;
private static final ValueEvalToNumericXlator NUM_XLATOR =
new ValueEvalToNumericXlator((short)
( ValueEvalToNumericXlator.BOOL_IS_PARSED
- | ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
- | ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
@@ -49,18 +44,28 @@ public class SubtractEval extends NumericOperationEval {
return NUM_XLATOR;
}
- public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
+ public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
+ if(args.length != 2) {
+ return ErrorEval.VALUE_INVALID;
+ }
Eval retval = null;
double d0 = 0;
double d1 = 0;
- switch (operands.length) {
- default: // will rarely happen. currently the parser itself fails.
- retval = ErrorEval.UNKNOWN_ERROR;
- break;
- case 2:
- ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
+ ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol);
+ if (ve instanceof NumericValueEval) {
+ d0 = ((NumericValueEval) ve).getNumberValue();
+ }
+ else if (ve instanceof BlankEval) {
+ // do nothing
+ }
+ else {
+ retval = ErrorEval.VALUE_INVALID;
+ }
+
+ if (retval == null) { // no error yet
+ ve = singleOperandEvaluate(args[1], srcRow, srcCol);
if (ve instanceof NumericValueEval) {
- d0 = ((NumericValueEval) ve).getNumberValue();
+ d1 = ((NumericValueEval) ve).getNumberValue();
}
else if (ve instanceof BlankEval) {
// do nothing
@@ -68,21 +73,8 @@ public class SubtractEval extends NumericOperationEval {
else {
retval = ErrorEval.VALUE_INVALID;
}
-
- if (retval == null) { // no error yet
- ve = singleOperandEvaluate(operands[1], srcRow, srcCol);
- if (ve instanceof NumericValueEval) {
- d1 = ((NumericValueEval) ve).getNumberValue();
- }
- else if (ve instanceof BlankEval) {
- // do nothing
- }
- else {
- retval = ErrorEval.VALUE_INVALID;
- }
- }
- } // end switch
-
+ }
+
if (retval == null) {
retval = (Double.isNaN(d0) || Double.isNaN(d1))
? (ValueEval) ErrorEval.VALUE_INVALID
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/UnaryMinusEval.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/UnaryMinusEval.java
index b4975eefc..ef6f533ea 100644
--- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/UnaryMinusEval.java
+++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/UnaryMinusEval.java
@@ -14,10 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-/*
- * Created on May 8, 2005
- *
- */
+
package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.Ptg;
@@ -27,14 +24,12 @@ import org.apache.poi.hssf.record.formula.UnaryMinusPtg;
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
*
*/
-public class UnaryMinusEval extends NumericOperationEval {
+public final class UnaryMinusEval extends NumericOperationEval {
private UnaryMinusPtg delegate;
private static final ValueEvalToNumericXlator NUM_XLATOR =
new ValueEvalToNumericXlator((short)
( ValueEvalToNumericXlator.BOOL_IS_PARSED
- | ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
- | ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
@@ -49,32 +44,24 @@ public class UnaryMinusEval extends NumericOperationEval {
return NUM_XLATOR;
}
- public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
- ValueEval retval = null;
+ public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
+ if(args.length != 1) {
+ return ErrorEval.VALUE_INVALID;
+ }
double d = 0;
- switch (operands.length) {
- default:
- retval = ErrorEval.UNKNOWN_ERROR;
- break;
- case 1:
- ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
- if (ve instanceof NumericValueEval) {
- d = ((NumericValueEval) ve).getNumberValue();
- }
- else if (ve instanceof BlankEval) {
- // do nothing
- }
- else if (ve instanceof ErrorEval) {
- retval = ve;
- }
+ ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol);
+ if (ve instanceof NumericValueEval) {
+ d = ((NumericValueEval) ve).getNumberValue();
+ }
+ else if (ve instanceof BlankEval) {
+ // do nothing
+ }
+ else if (ve instanceof ErrorEval) {
+ return ve;
}
- if (retval == null) {
- retval = new NumberEval(-d);
- }
-
- return retval;
+ return new NumberEval(-d);
}
public int getNumberOfOperands() {
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/UnaryPlusEval.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/UnaryPlusEval.java
index 7816b228f..edcc7bee7 100644
--- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/UnaryPlusEval.java
+++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/UnaryPlusEval.java
@@ -14,10 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-/*
- * Created on May 8, 2005
- *
- */
+
package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.Ptg;
@@ -27,114 +24,38 @@ import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
*
*/
-public class UnaryPlusEval implements OperationEval /*extends NumericOperationEval*/ {
+public final class UnaryPlusEval implements OperationEval {
private UnaryPlusPtg delegate;
- /*
- * COMMENT FOR COMMENTED CODE IN THIS FILE
- *
- * In excel the programmer seems to not have cared to
- * think about how strings were handled in other numeric
- * operations when he/she was implementing this operation :P
- *
- * Here's what I mean:
- *
- * Q. If the formula -"hello" evaluates to #VALUE! in excel, what should
- * the formula +"hello" evaluate to?
- *
- * A. +"hello" evaluates to "hello" (what the...?)
- *
- */
-
-
-// private static final ValueEvalToNumericXlator NUM_XLATOR =
-// new ValueEvalToNumericXlator((short)
-// ( ValueEvalToNumericXlator.BOOL_IS_PARSED
-// | ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
-// | ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
-// | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
-// | ValueEvalToNumericXlator.STRING_IS_PARSED
-// ));
-
-
/**
* called by reflection
*/
public UnaryPlusEval(Ptg ptg) {
this.delegate = (UnaryPlusPtg) ptg;
}
-
-// protected ValueEvalToNumericXlator getXlator() {
-// return NUM_XLATOR;
-// }
- public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
- ValueEval retval = null;
-
- switch (operands.length) {
- default:
- retval = ErrorEval.UNKNOWN_ERROR;
- break;
- case 1:
-
-// ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
-// if (ve instanceof NumericValueEval) {
-// d = ((NumericValueEval) ve).getNumberValue();
-// }
-// else if (ve instanceof BlankEval) {
-// // do nothing
-// }
-// else if (ve instanceof ErrorEval) {
-// retval = ve;
-// }
- if (operands[0] instanceof RefEval) {
- RefEval re = (RefEval) operands[0];
- retval = re.getInnerValueEval();
- }
- else if (operands[0] instanceof AreaEval) {
- AreaEval ae = (AreaEval) operands[0];
- if (ae.contains(srcRow, srcCol)) { // circular ref!
- retval = ErrorEval.CIRCULAR_REF_ERROR;
- }
- else if (ae.isRow()) {
- if (ae.containsColumn(srcCol)) {
- ValueEval ve = ae.getValueAt(ae.getFirstRow(), srcCol);
- if (ve instanceof RefEval) {
- ve = ((RefEval) ve).getInnerValueEval();
- }
- retval = ve;
- }
- else {
- retval = ErrorEval.VALUE_INVALID;
- }
- }
- else if (ae.isColumn()) {
- if (ae.containsRow(srcRow)) {
- ValueEval ve = ae.getValueAt(srcRow, ae.getFirstColumn());
- if (ve instanceof RefEval) {
- ve = ((RefEval) ve).getInnerValueEval();
- }
- retval = ve;
- }
- else {
- retval = ErrorEval.VALUE_INVALID;
- }
- }
- else {
- retval = ErrorEval.VALUE_INVALID;
- }
- }
- else {
- retval = (ValueEval) operands[0];
- }
- }
-
- if (retval instanceof BlankEval) {
- retval = new NumberEval(0);
- }
-
- return retval;
+ public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
+ if(args.length != 1) {
+ return ErrorEval.VALUE_INVALID;
+ }
+ double d;
+ try {
+ ValueEval ve = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
+ if(ve instanceof BlankEval) {
+ return NumberEval.ZERO;
+ }
+ if(ve instanceof StringEval) {
+ // Note - asymmetric with UnaryMinus
+ // -"hello" evaluates to #VALUE!
+ // but +"hello" evaluates to "hello"
+ return ve;
+ }
+ d = OperandResolver.coerceValueToDouble(ve);
+ } catch (EvaluationException e) {
+ return e.getErrorEval();
+ }
+ return new NumberEval(+d);
}
public int getNumberOfOperands() {
@@ -144,5 +65,4 @@ public class UnaryPlusEval implements OperationEval /*extends NumericOperationEv
public int getType() {
return delegate.getType();
}
-
}
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/ValueEvalToNumericXlator.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/ValueEvalToNumericXlator.java
index 5ffa2faee..1abcf34d2 100644
--- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/ValueEvalToNumericXlator.java
+++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/ValueEvalToNumericXlator.java
@@ -24,7 +24,7 @@ package org.apache.poi.hssf.record.formula.eval;
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
*
*/
-public class ValueEvalToNumericXlator {
+public final class ValueEvalToNumericXlator {
public static final int STRING_IS_PARSED = 0x0001;
public static final int BOOL_IS_PARSED = 0x0002;
@@ -34,26 +34,18 @@ public class ValueEvalToNumericXlator {
public static final int REF_BOOL_IS_PARSED = 0x0010;
public static final int REF_BLANK_IS_PARSED = 0x0020;
- public static final int EVALUATED_REF_STRING_IS_PARSED = 0x0040;
- public static final int EVALUATED_REF_BOOL_IS_PARSED = 0x0080;
- public static final int EVALUATED_REF_BLANK_IS_PARSED = 0x0100;
-
- public static final int STRING_TO_BOOL_IS_PARSED = 0x0200;
- public static final int REF_STRING_TO_BOOL_IS_PARSED = 0x0400;
-
public static final int STRING_IS_INVALID_VALUE = 0x0800;
- public static final int REF_STRING_IS_INVALID_VALUE = 0x1000;
-
-// public static final int BOOL_IS_BLANK = 0x2000;
-// public static final int REF_BOOL_IS_BLANK = 0x4000;
-// public static final int STRING_IS_BLANK = 0x8000;
-// public static final int REF_STRING_IS_BLANK = 0x10000;
private final int flags;
public ValueEvalToNumericXlator(int flags) {
- this.flags = flags;
+
+ if (false) { // uncomment to see who is using this class
+ System.err.println(new Throwable().getStackTrace()[1].getClassName() + "\t0x"
+ + Integer.toHexString(flags).toUpperCase());
+ }
+ this.flags = flags;
}
/**
@@ -71,7 +63,7 @@ public class ValueEvalToNumericXlator {
// most common case - least worries :)
else if (eval instanceof NumberEval) {
- retval = (NumberEval) eval;
+ retval = eval;
}
// booleval
@@ -125,50 +117,33 @@ public class ValueEvalToNumericXlator {
* @param eval
*/
private ValueEval xlateRefEval(RefEval reval) {
- ValueEval retval = null;
- ValueEval eval = (ValueEval) reval.getInnerValueEval();
+ ValueEval eval = reval.getInnerValueEval();
// most common case - least worries :)
if (eval instanceof NumberEval) {
- retval = (NumberEval) eval;
+ return eval;
}
- // booleval
- else if (eval instanceof BoolEval) {
- retval = ((flags & REF_BOOL_IS_PARSED) > 0)
+ if (eval instanceof BoolEval) {
+ return ((flags & REF_BOOL_IS_PARSED) > 0)
? (ValueEval) eval
: BlankEval.INSTANCE;
}
- // stringeval
- else if (eval instanceof StringEval) {
- retval = xlateRefStringEval((StringEval) eval);
+ if (eval instanceof StringEval) {
+ return xlateRefStringEval((StringEval) eval);
}
- // erroreval
- else if (eval instanceof ErrorEval) {
- retval = eval;
+ if (eval instanceof ErrorEval) {
+ return eval;
}
- // refeval
- else if (eval instanceof RefEval) {
- RefEval re = (RefEval) eval;
- retval = xlateRefEval(re);
+ if (eval instanceof BlankEval) {
+ return xlateBlankEval(REF_BLANK_IS_PARSED);
}
- else if (eval instanceof BlankEval) {
- retval = xlateBlankEval(reval.isEvaluated() ? EVALUATED_REF_BLANK_IS_PARSED : REF_BLANK_IS_PARSED);
- }
-
- // probably AreaEval ? then not acceptable.
- else {
- throw new RuntimeException("Invalid ValueEval type passed for conversion: " + eval.getClass());
- }
-
-
-
-
- return retval;
+ throw new RuntimeException("Invalid ValueEval type passed for conversion: ("
+ + eval.getClass().getName() + ")");
}
/**
@@ -176,93 +151,38 @@ public class ValueEvalToNumericXlator {
* @param eval
*/
private ValueEval xlateStringEval(StringEval eval) {
- ValueEval retval = null;
+
if ((flags & STRING_IS_PARSED) > 0) {
String s = eval.getStringValue();
- try {
- double d = Double.parseDouble(s);
- retval = new NumberEval(d);
- }
- catch (Exception e) {
- if ((flags & STRING_TO_BOOL_IS_PARSED) > 0) {
- try {
- boolean b = Boolean.getBoolean(s);
- retval = b ? BoolEval.TRUE : BoolEval.FALSE;
- }
- catch (Exception e2) { retval = ErrorEval.VALUE_INVALID; }
- }
- else {
- retval = ErrorEval.VALUE_INVALID;
- }
+ Double d = OperandResolver.parseDouble(s);
+ if(d == null) {
+ return ErrorEval.VALUE_INVALID;
}
+ return new NumberEval(d.doubleValue());
}
- else if ((flags & STRING_TO_BOOL_IS_PARSED) > 0) {
- String s = eval.getStringValue();
- try {
- boolean b = Boolean.getBoolean(s);
- retval = b ? BoolEval.TRUE : BoolEval.FALSE;
- }
- catch (Exception e) { retval = ErrorEval.VALUE_INVALID; }
- }
-
// strings are errors?
- else if ((flags & STRING_IS_INVALID_VALUE) > 0) {
- retval = ErrorEval.VALUE_INVALID;
+ if ((flags & STRING_IS_INVALID_VALUE) > 0) {
+ return ErrorEval.VALUE_INVALID;
}
// ignore strings
- else {
- retval = xlateBlankEval(BLANK_IS_PARSED);
- }
- return retval;
+ return xlateBlankEval(BLANK_IS_PARSED);
}
/**
* uses the relevant flags to decode the StringEval
* @param eval
*/
- private ValueEval xlateRefStringEval(StringEval eval) {
- ValueEval retval = null;
+ private ValueEval xlateRefStringEval(StringEval sve) {
if ((flags & REF_STRING_IS_PARSED) > 0) {
- StringEval sve = (StringEval) eval;
String s = sve.getStringValue();
- try {
- double d = Double.parseDouble(s);
- retval = new NumberEval(d);
- }
- catch (Exception e) {
- if ((flags & REF_STRING_TO_BOOL_IS_PARSED) > 0) {
- try {
- boolean b = Boolean.getBoolean(s);
- retval = b ? BoolEval.TRUE : BoolEval.FALSE;
- }
- catch (Exception e2) { retval = ErrorEval.VALUE_INVALID; }
- }
- else {
- retval = ErrorEval.VALUE_INVALID;
- }
+ Double d = OperandResolver.parseDouble(s);
+ if(d == null) {
+ return ErrorEval.VALUE_INVALID;
}
+ return new NumberEval(d.doubleValue());
}
- else if ((flags & REF_STRING_TO_BOOL_IS_PARSED) > 0) {
- StringEval sve = (StringEval) eval;
- String s = sve.getStringValue();
- try {
- boolean b = Boolean.getBoolean(s);
- retval = b ? BoolEval.TRUE : BoolEval.FALSE;;
- }
- catch (Exception e) { retval = ErrorEval.VALUE_INVALID; }
- }
-
- // strings are errors?
- else if ((flags & REF_STRING_IS_INVALID_VALUE) > 0) {
- retval = ErrorEval.VALUE_INVALID;
- }
-
// strings are blanks
- else {
- retval = BlankEval.INSTANCE;
- }
- return retval;
+ return BlankEval.INSTANCE;
}
-
}
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Avedev.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Avedev.java
index 592402b80..bf888b97d 100644
--- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Avedev.java
+++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Avedev.java
@@ -33,8 +33,8 @@ import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
public class Avedev extends MultiOperandNumericFunction {
private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
- new ValueEvalToNumericXlator((short) (0
- // ValueEvalToNumericXlator.BOOL_IS_PARSED
+ new ValueEvalToNumericXlator((short) (
+ ValueEvalToNumericXlator.BOOL_IS_PARSED
//| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED
@@ -44,7 +44,6 @@ public class Avedev extends MultiOperandNumericFunction {
//| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE
//| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
- | ValueEvalToNumericXlator.EVALUATED_REF_BLANK_IS_PARSED
));
/**
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Average.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Average.java
index 349110917..404304071 100644
--- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Average.java
+++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Average.java
@@ -33,8 +33,8 @@ import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
public class Average extends MultiOperandNumericFunction {
private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
- new ValueEvalToNumericXlator((short) (0
- // ValueEvalToNumericXlator.BOOL_IS_PARSED
+ new ValueEvalToNumericXlator((short) (
+ ValueEvalToNumericXlator.BOOL_IS_PARSED
//| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED
@@ -44,7 +44,6 @@ public class Average extends MultiOperandNumericFunction {
//| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE
//| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
- | ValueEvalToNumericXlator.EVALUATED_REF_BLANK_IS_PARSED
));
/**
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/FinanceFunction.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/FinanceFunction.java
index 8eb7e841d..c054c6dac 100644
--- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/FinanceFunction.java
+++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/FinanceFunction.java
@@ -14,10 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-/*
- * Created on Jun 20, 2005
- *
- */
+
package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.eval.BoolEval;
@@ -38,13 +35,10 @@ public abstract class FinanceFunction extends NumericFunction {
new ValueEvalToNumericXlator((short) (0
| ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
- | ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
- | ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
| ValueEvalToNumericXlator.BLANK_IS_PARSED
| ValueEvalToNumericXlator.REF_BLANK_IS_PARSED
- | ValueEvalToNumericXlator.EVALUATED_REF_BLANK_IS_PARSED
//| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE
//| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
@@ -56,11 +50,11 @@ public abstract class FinanceFunction extends NumericFunction {
* if they desire to return a different ValueEvalToNumericXlator instance
* than the default.
*/
- protected ValueEvalToNumericXlator getXlator() {
+ protected final ValueEvalToNumericXlator getXlator() {
return DEFAULT_NUM_XLATOR;
}
- protected ValueEval singleOperandNumericAsBoolean(Eval eval, int srcRow, short srcCol) {
+ protected final ValueEval singleOperandNumericAsBoolean(Eval eval, int srcRow, short srcCol) {
ValueEval retval = null;
retval = singleOperandEvaluate(eval, srcRow, srcCol);
if (retval instanceof NumericValueEval) {
@@ -74,5 +68,4 @@ public abstract class FinanceFunction extends NumericFunction {
}
return retval;
}
-
}
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/If.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/If.java
index 90dbd591b..7aba5db72 100644
--- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/If.java
+++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/If.java
@@ -28,28 +28,22 @@ import org.apache.poi.hssf.record.formula.eval.Eval;
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
*
*/
-public class If implements Function {
+public final class If implements Function {
+
+ public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
- public Eval evaluate(Eval[] evals, int srcCellRow, short srcCellCol) {
- Eval retval = null;
Eval evalWhenFalse = BoolEval.FALSE;
- switch (evals.length) {
+ switch (args.length) {
case 3:
- evalWhenFalse = evals[2];
+ evalWhenFalse = args[2];
case 2:
- BoolEval beval = (BoolEval) evals[0];
+ BoolEval beval = (BoolEval) args[0]; // TODO - class cast exception
if (beval.getBooleanValue()) {
- retval = evals[1];
+ return args[1];
}
- else {
- retval = evalWhenFalse;
- }
- break;
+ return evalWhenFalse;
default:
- retval = ErrorEval.UNKNOWN_ERROR;
+ return ErrorEval.VALUE_INVALID;
}
- return retval;
}
-
-
}
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Isblank.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Isblank.java
index 6e8f84b34..c0e482e5a 100644
--- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Isblank.java
+++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Isblank.java
@@ -14,79 +14,35 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-/*
- * Created on May 15, 2005
- *
- */
+
package org.apache.poi.hssf.record.formula.functions;
-import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.BlankEval;
import org.apache.poi.hssf.record.formula.eval.BoolEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval;
-import org.apache.poi.hssf.record.formula.eval.RefEval;
+import org.apache.poi.hssf.record.formula.eval.EvaluationException;
+import org.apache.poi.hssf.record.formula.eval.OperandResolver;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
/**
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
*
*/
-public class Isblank implements Function {
+public final class Isblank implements Function {
- public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
- ValueEval retval = null;
- boolean b = false;
-
- switch (operands.length) {
- default:
- retval = ErrorEval.VALUE_INVALID;
- break;
- case 1:
- if (operands[0] instanceof BlankEval) {
- b = true;
- }
- else if (operands[0] instanceof AreaEval) {
- AreaEval ae = (AreaEval) operands[0];
- if (ae.contains(srcCellRow, srcCellCol)) { // circular ref!
- retval = ErrorEval.CIRCULAR_REF_ERROR;
- }
- else if (ae.isRow()) {
- if (ae.containsColumn(srcCellCol)) {
- ValueEval ve = ae.getValueAt(ae.getFirstRow(), srcCellCol);
- b = (ve instanceof BlankEval);
- }
- else {
- b = false;
- }
- }
- else if (ae.isColumn()) {
- if (ae.containsRow(srcCellRow)) {
- ValueEval ve = ae.getValueAt(srcCellRow, ae.getFirstColumn());
- b = (ve instanceof BlankEval);
- }
- else {
- b = false;
- }
- }
- else {
- b = false;
- }
- }
- else if (operands[0] instanceof RefEval) {
- RefEval re = (RefEval) operands[0];
- b = (!re.isEvaluated()) && re.getInnerValueEval() instanceof BlankEval;
- }
- else {
- b = false;
- }
- }
-
- if (retval == null) {
- retval = b
- ? BoolEval.TRUE
- : BoolEval.FALSE;
- }
- return retval;
- }
+ public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
+ if(args.length != 1) {
+ return ErrorEval.VALUE_INVALID;
+ }
+ Eval arg = args[0];
+
+ ValueEval singleCellValue;
+ try {
+ singleCellValue = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
+ } catch (EvaluationException e) {
+ return BoolEval.FALSE;
+ }
+ return BoolEval.valueOf(singleCellValue instanceof BlankEval);
+ }
}
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Len.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Len.java
index c0cb39b26..0bc49b407 100644
--- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Len.java
+++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Len.java
@@ -14,125 +14,36 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-/*
- * Created on May 15, 2005
- *
- */
+
package org.apache.poi.hssf.record.formula.functions;
-import org.apache.poi.hssf.record.formula.eval.AreaEval;
-import org.apache.poi.hssf.record.formula.eval.BlankEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval;
+import org.apache.poi.hssf.record.formula.eval.EvaluationException;
import org.apache.poi.hssf.record.formula.eval.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.RefEval;
-import org.apache.poi.hssf.record.formula.eval.StringValueEval;
+import org.apache.poi.hssf.record.formula.eval.OperandResolver;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
/**
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
*
*/
-public class Len extends TextFunction {
+public final class Len extends TextFunction {
+ public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
+
+ if(args.length != 1) {
+ return ErrorEval.VALUE_INVALID;
+ }
+
+ try {
+ ValueEval veval = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
- public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
- ValueEval retval = null;
- String s = null;
-
- switch (operands.length) {
- default:
- retval = ErrorEval.VALUE_INVALID;
- break;
- case 1:
- ValueEval ve = singleOperandEvaluate(operands[0], srcCellRow, srcCellCol);
- if (ve instanceof StringValueEval) {
- StringValueEval sve = (StringValueEval) ve;
- s = sve.getStringValue();
- }
- else if (ve instanceof RefEval) {
- RefEval re = (RefEval) ve;
- ValueEval ive = re.getInnerValueEval();
- if (ive instanceof BlankEval) {
- s = re.isEvaluated() ? "0" : null;
- }
- else if (ive instanceof StringValueEval) {
- s = ((StringValueEval) ive).getStringValue();
- }
- else if (ive instanceof BlankEval) {}
- else {
- retval = ErrorEval.VALUE_INVALID;
- }
- }
- else if (ve instanceof BlankEval) {}
- else {
- retval = ErrorEval.VALUE_INVALID;
- break;
- }
- }
-
- if (retval == null) {
- s = (s == null) ? EMPTY_STRING : s;
- retval = new NumberEval(s.length());
- }
-
- return retval;
- }
-
-
- protected ValueEval singleOperandEvaluate(Eval eval, int srcRow, short srcCol) {
- ValueEval retval;
- if (eval instanceof AreaEval) {
- AreaEval ae = (AreaEval) eval;
- if (ae.contains(srcRow, srcCol)) { // circular ref!
- retval = ErrorEval.CIRCULAR_REF_ERROR;
- }
- else if (ae.isRow()) {
- if (ae.containsColumn(srcCol)) {
- ValueEval ve = ae.getValueAt(ae.getFirstRow(), srcCol);
- retval = attemptXlateToText(ve);
- }
- else {
- retval = ErrorEval.VALUE_INVALID;
- }
- }
- else if (ae.isColumn()) {
- if (ae.containsRow(srcRow)) {
- ValueEval ve = ae.getValueAt(srcRow, ae.getFirstColumn());
- retval = attemptXlateToText(ve);
- }
- else {
- retval = ErrorEval.VALUE_INVALID;
- }
- }
- else {
- retval = ErrorEval.VALUE_INVALID;
- }
- }
- else {
- retval = attemptXlateToText((ValueEval) eval);
- }
- return retval;
- }
-
-
- /**
- * converts from Different ValueEval types to StringEval.
- * Note: AreaEvals are not handled, if arg is an AreaEval,
- * the returned value is ErrorEval.VALUE_INVALID
- * @param ve
- */
- protected ValueEval attemptXlateToText(ValueEval ve) {
- ValueEval retval;
- if (ve instanceof StringValueEval || ve instanceof RefEval) {
- retval = ve;
- }
- else if (ve instanceof BlankEval) {
- retval = ve;
- }
- else {
- retval = ErrorEval.VALUE_INVALID;
- }
- return retval;
- }
+ String str = OperandResolver.coerceValueToString(veval);
+
+ return new NumberEval(str.length());
+ } catch (EvaluationException e) {
+ return e.getErrorEval();
+ }
+ }
}
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Maxa.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Maxa.java
index 21e30de0c..e25db7b74 100644
--- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Maxa.java
+++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Maxa.java
@@ -14,10 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-/*
- * Created on May 15, 2005
- *
- */
+
package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
@@ -30,12 +27,11 @@ import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
*
*/
-public class Maxa extends MultiOperandNumericFunction {
+public final class Maxa extends MultiOperandNumericFunction {
private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
new ValueEvalToNumericXlator((short) (
ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
- | ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED
//| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
//| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Mid.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Mid.java
index 517e89fee..7f30aa4ce 100644
--- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Mid.java
+++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Mid.java
@@ -23,7 +23,6 @@ import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
import org.apache.poi.hssf.record.formula.eval.StringEval;
-import org.apache.poi.hssf.record.formula.eval.StringValueEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
/**
@@ -35,7 +34,7 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
*
* @author Manda Wilson < wilson at c bio dot msk cc dot org >
*/
-public class Mid extends TextFunction {
+public class Mid implements Function {
/**
* Returns a specific number of characters from a text string, starting at
* the position you specify, based on the number of characters you specify.
@@ -52,7 +51,8 @@ public class Mid extends TextFunction {
int numChars;
try {
- text = evaluateTextArg(args[0], srcCellRow, srcCellCol);
+ ValueEval evText = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
+ text = OperandResolver.coerceValueToString(evText);
int startCharNum = evaluateNumberArg(args[1], srcCellRow, srcCellCol);
numChars = evaluateNumberArg(args[2], srcCellRow, srcCellCol);
startIx = startCharNum - 1; // convert to zero-based
@@ -79,7 +79,7 @@ public class Mid extends TextFunction {
}
- public static int evaluateNumberArg(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException {
+ private static int evaluateNumberArg(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException {
ValueEval ev = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
if (ev instanceof BlankEval) {
// Note - for start_num arg, blank causes error(#VALUE!),
@@ -89,12 +89,4 @@ public class Mid extends TextFunction {
return OperandResolver.coerceValueToInt(ev);
}
-
- private static String evaluateTextArg(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException {
- ValueEval ev = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
- if (ev instanceof StringValueEval) {
- return ((StringValueEval) ev).getStringValue();
- }
- throw EvaluationException.invalidValue();
- }
}
\ No newline at end of file
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Mina.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Mina.java
index a998a870f..21ba47b56 100644
--- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Mina.java
+++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Mina.java
@@ -14,10 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-/*
- * Created on May 15, 2005
- *
- */
+
package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
@@ -35,7 +32,6 @@ public class Mina extends MultiOperandNumericFunction {
new ValueEvalToNumericXlator((short) (
ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
- | ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED
//| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
//| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/MultiOperandNumericFunction.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/MultiOperandNumericFunction.java
index f7dc60fea..0e7cce217 100644
--- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/MultiOperandNumericFunction.java
+++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/MultiOperandNumericFunction.java
@@ -76,32 +76,9 @@ public abstract class MultiOperandNumericFunction extends NumericFunction {
}
}
-
- private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
- new ValueEvalToNumericXlator((short) (
- ValueEvalToNumericXlator.BOOL_IS_PARSED
- | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
- | ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
- //| ValueEvalToNumericXlator.STRING_IS_PARSED
- | ValueEvalToNumericXlator.REF_STRING_IS_PARSED
- | ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
- //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED
- //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
- //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE
- //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
- ));
-
private static final int DEFAULT_MAX_NUM_OPERANDS = 30;
- /**
- * this is the default impl for the factory method getXlator
- * of the super class NumericFunction. Subclasses can override this method
- * if they desire to return a different ValueEvalToNumericXlator instance
- * than the default.
- */
- protected ValueEvalToNumericXlator getXlator() {
- return DEFAULT_NUM_XLATOR;
- }
+ protected abstract ValueEvalToNumericXlator getXlator();
/**
* Maximum number of operands accepted by this function.
@@ -160,9 +137,7 @@ public abstract class MultiOperandNumericFunction extends NumericFunction {
* HSSFFormulaEvaluator where we store an array
* of RefEvals as the "values" array.
*/
- RefEval re = (values[j] instanceof RefEval)
- ? new Ref2DEval(null, ((RefEval) values[j]).getInnerValueEval(), true)
- : new Ref2DEval(null, values[j], false);
+ RefEval re = new Ref2DEval(null, values[j]);
ValueEval ve = singleOperandEvaluate(re, srcRow, srcCol);
if (ve instanceof NumericValueEval) {
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/NumericFunction.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/NumericFunction.java
index 7b43317a3..fd96f1495 100644
--- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/NumericFunction.java
+++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/NumericFunction.java
@@ -39,10 +39,8 @@ public abstract class NumericFunction implements Function {
new ValueEvalToNumericXlator((short) (
ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
- | ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
- | ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
//| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Stdev.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Stdev.java
index fef7e0346..7995e66c3 100644
--- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Stdev.java
+++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Stdev.java
@@ -33,8 +33,8 @@ import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
public class Stdev extends MultiOperandNumericFunction {
private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
- new ValueEvalToNumericXlator((short) (0
- // ValueEvalToNumericXlator.BOOL_IS_PARSED
+ new ValueEvalToNumericXlator((short) (
+ ValueEvalToNumericXlator.BOOL_IS_PARSED
//| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED
@@ -44,7 +44,6 @@ public class Stdev extends MultiOperandNumericFunction {
//| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE
//| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
- | ValueEvalToNumericXlator.EVALUATED_REF_BLANK_IS_PARSED
));
/**
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Sumsq.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Sumsq.java
index f4e1959be..b74b4161a 100644
--- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Sumsq.java
+++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Sumsq.java
@@ -33,18 +33,18 @@ import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
public class Sumsq extends MultiOperandNumericFunction {
private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
new ValueEvalToNumericXlator((short) (
- // ValueEvalToNumericXlator.BOOL_IS_PARSED
+ ValueEvalToNumericXlator.BOOL_IS_PARSED
//| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
- //| ValueEvalToNumericXlator.STRING_IS_PARSED
+ | ValueEvalToNumericXlator.STRING_IS_PARSED
//| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
//| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
//| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE
//| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
- ValueEvalToNumericXlator.REF_BLANK_IS_PARSED
- | ValueEvalToNumericXlator.BLANK_IS_PARSED
+ //| ValueEvalToNumericXlator.REF_BLANK_IS_PARSED
+ //| ValueEvalToNumericXlator.BLANK_IS_PARSED
));
protected ValueEvalToNumericXlator getXlator() {
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Sumx2my2.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Sumx2my2.java
index 8e3122407..30ad5ec23 100644
--- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Sumx2my2.java
+++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Sumx2my2.java
@@ -14,50 +14,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-/*
- * Created on May 15, 2005
- *
- */
+
package org.apache.poi.hssf.record.formula.functions;
-import org.apache.poi.hssf.record.formula.eval.ErrorEval;
-import org.apache.poi.hssf.record.formula.eval.Eval;
-import org.apache.poi.hssf.record.formula.eval.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-
/**
+ * Implementation of Excel function SUMX2MY2()
+ *
+ * Calculates the sum of differences of squares in two arrays of the same size.
+ * Syntax:
+ * SUMX2MY2(arrayX, arrayY)
+ *
+ * result = Σi: 0..n(xi2-yi2)
+ *
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
- *
*/
-public class Sumx2my2 extends XYNumericFunction {
+public final class Sumx2my2 extends XYNumericFunction {
-
- public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
- ValueEval retval = null;
- double[][] values = null;
-
- int checkLen = 0; // check to see that all array lengths are equal
- switch (operands.length) {
- default:
- retval = ErrorEval.VALUE_INVALID;
- break;
- case 2:
- values = getValues(operands, srcCellRow, srcCellCol);
- if (values==null
- || values[X] == null || values[Y] == null
- || values[X].length == 0 || values[Y].length == 0
- || values[X].length != values[Y].length) {
- retval = ErrorEval.VALUE_INVALID;
- }
- }
-
- if (retval == null) {
- double d = MathX.sumx2my2(values[X], values[Y]);
- retval = (Double.isNaN(d) || Double.isInfinite(d))
- ? (ValueEval) ErrorEval.NUM_ERROR
- : new NumberEval(d);
- }
-
- return retval;
+ protected double evaluate(double[] xArray, double[] yArray) {
+ return MathX.sumx2my2(xArray, yArray);
}
}
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Sumx2py2.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Sumx2py2.java
index deb7675a4..dfd730d12 100644
--- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Sumx2py2.java
+++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Sumx2py2.java
@@ -14,50 +14,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-/*
- * Created on May 15, 2005
- *
- */
+
package org.apache.poi.hssf.record.formula.functions;
-import org.apache.poi.hssf.record.formula.eval.ErrorEval;
-import org.apache.poi.hssf.record.formula.eval.Eval;
-import org.apache.poi.hssf.record.formula.eval.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-
/**
+ * Implementation of Excel function SUMX2PY2()
+ *
+ * Calculates the sum of squares in two arrays of the same size.
+ * Syntax:
+ * SUMX2PY2(arrayX, arrayY)
+ *
+ * result = Σi: 0..n(xi2+yi2)
+ *
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
- *
*/
-public class Sumx2py2 extends XYNumericFunction {
+public final class Sumx2py2 extends XYNumericFunction {
-
- public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
- ValueEval retval = null;
- double[][] values = null;
-
- int checkLen = 0; // check to see that all array lengths are equal
- switch (operands.length) {
- default:
- retval = ErrorEval.VALUE_INVALID;
- break;
- case 2:
- values = getValues(operands, srcCellRow, srcCellCol);
- if (values==null
- || values[X] == null || values[Y] == null
- || values[X].length == 0 || values[Y].length == 0
- || values[X].length != values[Y].length) {
- retval = ErrorEval.VALUE_INVALID;
- }
- }
-
- if (retval == null) {
- double d = MathX.sumx2py2(values[X], values[Y]);
- retval = (Double.isNaN(d) || Double.isInfinite(d))
- ? (ValueEval) ErrorEval.NUM_ERROR
- : new NumberEval(d);
- }
-
- return retval;
+ protected double evaluate(double[] xArray, double[] yArray) {
+ return MathX.sumx2py2(xArray, yArray);
}
}
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Sumxmy2.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Sumxmy2.java
index c62a0b762..a1b2fec9b 100644
--- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Sumxmy2.java
+++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Sumxmy2.java
@@ -14,50 +14,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-/*
- * Created on May 15, 2005
- *
- */
+
package org.apache.poi.hssf.record.formula.functions;
-import org.apache.poi.hssf.record.formula.eval.ErrorEval;
-import org.apache.poi.hssf.record.formula.eval.Eval;
-import org.apache.poi.hssf.record.formula.eval.NumberEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEval;
-
/**
+ * Implementation of Excel function SUMXMY2()
+ *
+ * Calculates the sum of squares of differences between two arrays of the same size.
+ * Syntax:
+ * SUMXMY2(arrayX, arrayY)
+ *
+ * result = Σi: 0..n(xi-yi)2
+ *
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
- *
*/
-public class Sumxmy2 extends XYNumericFunction {
+public final class Sumxmy2 extends XYNumericFunction {
-
- public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
- ValueEval retval = null;
- double[][] values = null;
-
- int checkLen = 0; // check to see that all array lengths are equal
- switch (operands.length) {
- default:
- retval = ErrorEval.VALUE_INVALID;
- break;
- case 2:
- values = getValues(operands, srcCellRow, srcCellCol);
- if (values==null
- || values[X] == null || values[Y] == null
- || values[X].length == 0 || values[Y].length == 0
- || values[X].length != values[Y].length) {
- retval = ErrorEval.VALUE_INVALID;
- }
- }
-
- if (retval == null) {
- double d = MathX.sumxmy2(values[X], values[Y]);
- retval = (Double.isNaN(d) || Double.isInfinite(d))
- ? (ValueEval) ErrorEval.NUM_ERROR
- : new NumberEval(d);
- }
-
- return retval;
+ protected double evaluate(double[] xArray, double[] yArray) {
+ return MathX.sumxmy2(xArray, yArray);
}
}
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Trim.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Trim.java
index 5e9d91c7c..87e29ee34 100644
--- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Trim.java
+++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/Trim.java
@@ -16,12 +16,11 @@
*/
package org.apache.poi.hssf.record.formula.functions;
-import org.apache.poi.hssf.record.formula.eval.BlankEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval;
-import org.apache.poi.hssf.record.formula.eval.NumberEval;
+import org.apache.poi.hssf.record.formula.eval.EvaluationException;
+import org.apache.poi.hssf.record.formula.eval.OperandResolver;
import org.apache.poi.hssf.record.formula.eval.StringEval;
-import org.apache.poi.hssf.record.formula.eval.StringValueEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
/**
@@ -30,46 +29,25 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
* value is string.
* @author Manda Wilson < wilson at c bio dot msk cc dot org >
*/
-public class Trim extends TextFunction {
+public final class Trim extends TextFunction {
- /**
- * Removes leading and trailing spaces from value if evaluated
- * operand value is string.
- * Returns StringEval only if evaluated operand is of type string
- * (and is not blank or null) or number. If evaluated operand is
- * of type string and is blank or null, or if evaluated operand is
- * of type blank, returns BlankEval. Otherwise returns ErrorEval.
- *
- * @see org.apache.poi.hssf.record.formula.eval.Eval
- */
- public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
- Eval retval = ErrorEval.VALUE_INVALID;
- String str = null;
-
- switch (operands.length) {
- default:
- break;
- case 1:
- ValueEval veval = singleOperandEvaluate(operands[0], srcCellRow, srcCellCol);
- if (veval instanceof StringValueEval) {
- StringValueEval sve = (StringValueEval) veval;
- str = sve.getStringValue();
- if (str == null || str.trim().equals("")) {
- return BlankEval.INSTANCE;
- }
- }
- else if (veval instanceof NumberEval) {
- NumberEval neval = (NumberEval) veval;
- str = neval.getStringValue();
- }
- else if (veval instanceof BlankEval) {
- return BlankEval.INSTANCE;
- }
- }
-
- if (str != null) {
- retval = new StringEval(str.trim());
- }
- return retval;
- }
+ public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
+
+ if(args.length != 1) {
+ return ErrorEval.VALUE_INVALID;
+ }
+
+ try {
+ ValueEval veval = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
+
+ String str = OperandResolver.coerceValueToString(veval);
+ str = str.trim();
+ if(str.length() < 1) {
+ return StringEval.EMPTY_INSTANCE;
+ }
+ return new StringEval(str);
+ } catch (EvaluationException e) {
+ return e.getErrorEval();
+ }
+ }
}
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/XYNumericFunction.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/XYNumericFunction.java
index 1e6955ad9..b989c33a2 100644
--- a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/XYNumericFunction.java
+++ b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/XYNumericFunction.java
@@ -14,154 +14,151 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-/*
- * Created on May 29, 2005
- *
- */
+
package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval;
+import org.apache.poi.hssf.record.formula.eval.EvaluationException;
import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.RefEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
/**
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
*
*/
-public abstract class XYNumericFunction extends NumericFunction {
+public abstract class XYNumericFunction implements Function {
protected static final int X = 0;
protected static final int Y = 1;
-
- private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
- new ValueEvalToNumericXlator((short) (
- ValueEvalToNumericXlator.BOOL_IS_PARSED
- | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
- | ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
- //| ValueEvalToNumericXlator.STRING_IS_PARSED
- | ValueEvalToNumericXlator.REF_STRING_IS_PARSED
- | ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
- //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED
- //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
- //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE
- //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
- ));
-
- /**
- * this is the default impl for the factory method getXlator
- * of the super class NumericFunction. Subclasses can override this method
- * if they desire to return a different ValueEvalToNumericXlator instance
- * than the default.
- */
- protected ValueEvalToNumericXlator getXlator() {
- return DEFAULT_NUM_XLATOR;
- }
- protected int getMaxNumOperands() {
- return 30;
+ protected static final class DoubleArrayPair {
+
+ private final double[] _xArray;
+ private final double[] _yArray;
+
+ public DoubleArrayPair(double[] xArray, double[] yArray) {
+ _xArray = xArray;
+ _yArray = yArray;
+ }
+ public double[] getXArray() {
+ return _xArray;
+ }
+ public double[] getYArray() {
+ return _yArray;
+ }
}
+
+ public final Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
+ if(args.length != 2) {
+ return ErrorEval.VALUE_INVALID;
+ }
+
+ double[][] values;
+ try {
+ values = getValues(args[0], args[1]);
+ } catch (EvaluationException e) {
+ return e.getErrorEval();
+ }
+ if (values==null
+ || values[X] == null || values[Y] == null
+ || values[X].length == 0 || values[Y].length == 0
+ || values[X].length != values[Y].length) {
+ return ErrorEval.VALUE_INVALID;
+ }
+
+ double d = evaluate(values[X], values[Y]);
+ if (Double.isNaN(d) || Double.isInfinite(d)) {
+ return ErrorEval.NUM_ERROR;
+ }
+ return new NumberEval(d);
+ }
+ protected abstract double evaluate(double[] xArray, double[] yArray);
+
/**
* Returns a double array that contains values for the numeric cells
* from among the list of operands. Blanks and Blank equivalent cells
* are ignored. Error operands or cells containing operands of type
* that are considered invalid and would result in #VALUE! error in
* excel cause this function to return null.
- *
- * @param xops
- * @param yops
- * @param srcRow
- * @param srcCol
*/
- protected double[][] getNumberArray(Eval[] xops, Eval[] yops, int srcRow, short srcCol) {
- double[][] retval = new double[2][30];
+ private static double[][] getNumberArray(Eval[] xops, Eval[] yops) throws EvaluationException {
+
+ // check for errors first: size mismatch, value errors in x, value errors in y
+
+ int nArrayItems = xops.length;
+ if(nArrayItems != yops.length) {
+ throw new EvaluationException(ErrorEval.NA);
+ }
+ for (int i = 0; i < xops.length; i++) {
+ Eval eval = xops[i];
+ if (eval instanceof ErrorEval) {
+ throw new EvaluationException((ErrorEval) eval);
+ }
+ }
+ for (int i = 0; i < yops.length; i++) {
+ Eval eval = yops[i];
+ if (eval instanceof ErrorEval) {
+ throw new EvaluationException((ErrorEval) eval);
+ }
+ }
+
+ double[] xResult = new double[nArrayItems];
+ double[] yResult = new double[nArrayItems];
+
int count = 0;
- if (xops.length > getMaxNumOperands()
- || yops.length > getMaxNumOperands()
- || xops.length != yops.length) {
- retval = null;
- }
- else {
-
- for (int i=0, iSize=xops.length; inull
.
+ */
+ private static ValueEval dereferenceValue(ValueEval evaluationResult, int srcRowNum, short srcColNum) {
+ if (evaluationResult instanceof RefEval) {
+ RefEval rv = (RefEval) evaluationResult;
return rv.getInnerValueEval();
}
- if (value instanceof AreaEval) {
- AreaEval ae = (AreaEval) value;
+ if (evaluationResult instanceof AreaEval) {
+ AreaEval ae = (AreaEval) evaluationResult;
if (ae.isRow()) {
if(ae.isColumn()) {
return ae.getValues()[0];
@@ -460,7 +490,7 @@ public class HSSFFormulaEvaluator {
}
return ErrorEval.VALUE_INVALID;
}
- return value;
+ return evaluationResult;
}
private static Eval invokeOperation(OperationEval operation, Eval[] ops, int srcRowNum, short srcColNum,
@@ -486,7 +516,7 @@ public class HSSFFormulaEvaluator {
// (eg C:C)
// TODO: Handle whole column ranges properly
if(row1 == -1 && row0 >= 0) {
- row1 = (short)sheet.getLastRowNum();
+ row1 = (short)sheet.getLastRowNum();
}
ValueEval[] values = new ValueEval[(row1 - row0 + 1) * (col1 - col0 + 1)];
@@ -514,7 +544,7 @@ public class HSSFFormulaEvaluator {
// (eg C:C)
// TODO: Handle whole column ranges properly
if(row1 == -1 && row0 >= 0) {
- row1 = (short)xsheet.getLastRowNum();
+ row1 = (short)xsheet.getLastRowNum();
}
ValueEval[] values = new ValueEval[(row1 - row0 + 1) * (col1 - col0 + 1)];
@@ -631,22 +661,22 @@ public class HSSFFormulaEvaluator {
private static Ref2DEval createRef2DEval(ReferencePtg ptg, HSSFCell cell,
HSSFRow row, HSSFSheet sheet, HSSFWorkbook workbook) {
if (cell == null) {
- return new Ref2DEval(ptg, BlankEval.INSTANCE, false);
+ return new Ref2DEval(ptg, BlankEval.INSTANCE);
}
switch (cell.getCellType()) {
case HSSFCell.CELL_TYPE_NUMERIC:
- return new Ref2DEval(ptg, new NumberEval(cell.getNumericCellValue()), false);
+ return new Ref2DEval(ptg, new NumberEval(cell.getNumericCellValue()));
case HSSFCell.CELL_TYPE_STRING:
- return new Ref2DEval(ptg, new StringEval(cell.getRichStringCellValue().getString()), false);
+ return new Ref2DEval(ptg, new StringEval(cell.getRichStringCellValue().getString()));
case HSSFCell.CELL_TYPE_FORMULA:
- return new Ref2DEval(ptg, internalEvaluate(cell, row, sheet, workbook), true);
+ return new Ref2DEval(ptg, internalEvaluate(cell, row, sheet, workbook));
case HSSFCell.CELL_TYPE_BOOLEAN:
- return new Ref2DEval(ptg, BoolEval.valueOf(cell.getBooleanCellValue()), false);
+ return new Ref2DEval(ptg, BoolEval.valueOf(cell.getBooleanCellValue()));
case HSSFCell.CELL_TYPE_BLANK:
- return new Ref2DEval(ptg, BlankEval.INSTANCE, false);
+ return new Ref2DEval(ptg, BlankEval.INSTANCE);
case HSSFCell.CELL_TYPE_ERROR:
- return new Ref2DEval(ptg, ErrorEval.valueOf(cell.getErrorCellValue()), false);
+ return new Ref2DEval(ptg, ErrorEval.valueOf(cell.getErrorCellValue()));
}
throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
}
@@ -657,21 +687,21 @@ public class HSSFFormulaEvaluator {
private static Ref3DEval createRef3DEval(Ref3DPtg ptg, HSSFCell cell,
HSSFRow row, HSSFSheet sheet, HSSFWorkbook workbook) {
if (cell == null) {
- return new Ref3DEval(ptg, BlankEval.INSTANCE, false);
+ return new Ref3DEval(ptg, BlankEval.INSTANCE);
}
switch (cell.getCellType()) {
case HSSFCell.CELL_TYPE_NUMERIC:
- return new Ref3DEval(ptg, new NumberEval(cell.getNumericCellValue()), false);
+ return new Ref3DEval(ptg, new NumberEval(cell.getNumericCellValue()));
case HSSFCell.CELL_TYPE_STRING:
- return new Ref3DEval(ptg, new StringEval(cell.getRichStringCellValue().getString()), false);
+ return new Ref3DEval(ptg, new StringEval(cell.getRichStringCellValue().getString()));
case HSSFCell.CELL_TYPE_FORMULA:
- return new Ref3DEval(ptg, internalEvaluate(cell, row, sheet, workbook), true);
+ return new Ref3DEval(ptg, internalEvaluate(cell, row, sheet, workbook));
case HSSFCell.CELL_TYPE_BOOLEAN:
- return new Ref3DEval(ptg, BoolEval.valueOf(cell.getBooleanCellValue()), false);
+ return new Ref3DEval(ptg, BoolEval.valueOf(cell.getBooleanCellValue()));
case HSSFCell.CELL_TYPE_BLANK:
- return new Ref3DEval(ptg, BlankEval.INSTANCE, false);
+ return new Ref3DEval(ptg, BlankEval.INSTANCE);
case HSSFCell.CELL_TYPE_ERROR:
- return new Ref3DEval(ptg, ErrorEval.valueOf(cell.getErrorCellValue()), false);
+ return new Ref3DEval(ptg, ErrorEval.valueOf(cell.getErrorCellValue()));
}
throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
}
diff --git a/src/scratchpad/testcases/org/apache/poi/hssf/eventusermodel/TestMissingRecordAwareHSSFListener.java b/src/scratchpad/testcases/org/apache/poi/hssf/eventusermodel/TestMissingRecordAwareHSSFListener.java
index 9c2cb2b87..a038a964e 100644
--- a/src/scratchpad/testcases/org/apache/poi/hssf/eventusermodel/TestMissingRecordAwareHSSFListener.java
+++ b/src/scratchpad/testcases/org/apache/poi/hssf/eventusermodel/TestMissingRecordAwareHSSFListener.java
@@ -16,14 +16,14 @@
==================================================================== */
package org.apache.poi.hssf.eventusermodel;
-import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
-import org.apache.poi.hssf.eventusermodel.HSSFListener;
-
import java.io.File;
import java.io.FileInputStream;
+import java.io.IOException;
import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
-import org.apache.poi.hssf.eventusermodel.HSSFRequest;
import org.apache.poi.hssf.eventusermodel.dummyrecord.LastCellOfRowDummyRecord;
import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingCellDummyRecord;
import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingRowDummyRecord;
@@ -31,31 +31,33 @@ import org.apache.poi.hssf.record.LabelSSTRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.RowRecord;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
-
-import junit.framework.TestCase;
-
-public class TestMissingRecordAwareHSSFListener extends TestCase {
- private String dirname;
+/**
+ * Tests for MissingRecordAwareHSSFListener
+ */
+public final class TestMissingRecordAwareHSSFListener extends TestCase {
- public TestMissingRecordAwareHSSFListener() {
- dirname = System.getProperty("HSSF.testdata.path");
- }
-
- public void testMissingRowRecords() throws Exception {
+ private Record[] r;
+
+ public void setUp() {
+ String dirname = System.getProperty("HSSF.testdata.path");
File f = new File(dirname + "/MissingBits.xls");
-
+
HSSFRequest req = new HSSFRequest();
MockHSSFListener mockListen = new MockHSSFListener();
MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(mockListen);
req.addListenerForAllRecords(listener);
- POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(f));
HSSFEventFactory factory = new HSSFEventFactory();
- factory.processWorkbookEvents(req, fs);
-
- // Check we got the dummy records
- Record[] r = (Record[])
- mockListen.records.toArray(new Record[mockListen.records.size()]);
+ try {
+ POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(f));
+ factory.processWorkbookEvents(req, fs);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ r = mockListen.getRecords();
+ }
+
+ public void testMissingRowRecords() throws Exception {
// We have rows 0, 1, 2, 20 and 21
int row0 = -1;
@@ -105,20 +107,6 @@ public class TestMissingRecordAwareHSSFListener extends TestCase {
}
public void testEndOfRowRecords() throws Exception {
- File f = new File(dirname + "/MissingBits.xls");
-
- HSSFRequest req = new HSSFRequest();
- MockHSSFListener mockListen = new MockHSSFListener();
- MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(mockListen);
- req.addListenerForAllRecords(listener);
-
- POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(f));
- HSSFEventFactory factory = new HSSFEventFactory();
- factory.processWorkbookEvents(req, fs);
-
- // Check we got the dummy records
- Record[] r = (Record[])
- mockListen.records.toArray(new Record[mockListen.records.size()]);
// Find the cell at 0,0
int cell00 = -1;
@@ -240,20 +228,6 @@ public class TestMissingRecordAwareHSSFListener extends TestCase {
public void testMissingCellRecords() throws Exception {
- File f = new File(dirname + "/MissingBits.xls");
-
- HSSFRequest req = new HSSFRequest();
- MockHSSFListener mockListen = new MockHSSFListener();
- MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(mockListen);
- req.addListenerForAllRecords(listener);
-
- POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(f));
- HSSFEventFactory factory = new HSSFEventFactory();
- factory.processWorkbookEvents(req, fs);
-
- // Check we got the dummy records
- Record[] r = (Record[])
- mockListen.records.toArray(new Record[mockListen.records.size()]);
// Find the cell at 0,0
int cell00 = -1;
@@ -352,25 +326,35 @@ public class TestMissingRecordAwareHSSFListener extends TestCase {
assertEquals(10, mc.getColumn());
}
- private static class MockHSSFListener implements HSSFListener {
- private MockHSSFListener() {}
- private ArrayList records = new ArrayList();
+ private static final class MockHSSFListener implements HSSFListener {
+ public MockHSSFListener() {}
+ private final List _records = new ArrayList();
public void processRecord(Record record) {
- records.add(record);
+ _records.add(record);
if(record instanceof MissingRowDummyRecord) {
MissingRowDummyRecord mr = (MissingRowDummyRecord)record;
- System.out.println("Got dummy row " + mr.getRowNumber());
+ log("Got dummy row " + mr.getRowNumber());
}
if(record instanceof MissingCellDummyRecord) {
MissingCellDummyRecord mc = (MissingCellDummyRecord)record;
- System.out.println("Got dummy cell " + mc.getRow() + " " + mc.getColumn());
+ log("Got dummy cell " + mc.getRow() + " " + mc.getColumn());
}
if(record instanceof LastCellOfRowDummyRecord) {
LastCellOfRowDummyRecord lc = (LastCellOfRowDummyRecord)record;
- System.out.println("Got end-of row, row was " + lc.getRow() + ", last column was " + lc.getLastColumnNumber());
+ log("Got end-of row, row was " + lc.getRow() + ", last column was " + lc.getLastColumnNumber());
}
}
+ private static void log(String msg) {
+ if(false) { // successful tests should be quiet
+ System.out.println(msg);
+ }
+ }
+ public Record[] getRecords() {
+ Record[] result = new Record[_records.size()];
+ _records.toArray(result);
+ return result;
+ }
}
}
diff --git a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/eval/AllFormulaEvalTests.java b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/eval/AllFormulaEvalTests.java
new file mode 100755
index 000000000..3260c371c
--- /dev/null
+++ b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/eval/AllFormulaEvalTests.java
@@ -0,0 +1,38 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hssf.record.formula.eval;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Collects all tests the package org.apache.poi.hssf.record.formula.eval.
+ *
+ * @author Josh Micich
+ */
+public class AllFormulaEvalTests {
+
+ public static Test suite() {
+ TestSuite result = new TestSuite("Tests for org.apache.poi.hssf.record.formula.eval");
+ result.addTestSuite(TestCircularReferences.class);
+ result.addTestSuite(TestExternalFunction.class);
+ result.addTestSuite(TestFormulasFromSpreadsheet.class);
+ result.addTestSuite(TestUnaryPlusEval.class);
+ return result;
+ }
+}
diff --git a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/eval/TestExternalFunction.java b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/eval/TestExternalFunction.java
new file mode 100755
index 000000000..27e333865
--- /dev/null
+++ b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/eval/TestExternalFunction.java
@@ -0,0 +1,61 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hssf.record.formula.eval;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
+import org.apache.poi.hssf.usermodel.HSSFName;
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue;
+/**
+ *
+ * @author Josh Micich
+ */
+public final class TestExternalFunction extends TestCase {
+
+ /**
+ * Checks that an external function can get invoked from the formula evaluator.
+ */
+ public void testInvoke() {
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet sheet = wb.createSheet();
+ wb.setSheetName(0, "Sheet1");
+ HSSFRow row = sheet.createRow(0);
+ HSSFCell cell = row.createCell((short)0);
+
+ HSSFName hssfName = wb.createName();
+ hssfName.setNameName("myFunc");
+
+ cell.setCellFormula("myFunc()");
+ String actualFormula=cell.getCellFormula();
+ assertEquals("myFunc()", actualFormula);
+
+ HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb);
+ fe.setCurrentRow(row);
+ CellValue evalResult = fe.evaluate(cell);
+
+ // Check the return value from ExternalFunction.evaluate()
+ // TODO - make this test assert something more interesting as soon as ExternalFunction works a bit better
+ assertEquals(HSSFCell.CELL_TYPE_ERROR, evalResult.getCellType());
+ assertEquals(ErrorEval.FUNCTION_NOT_IMPLEMENTED.getErrorCode(), evalResult.getErrorValue());
+ }
+}
diff --git a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java
index 8d21956ed..d3e9c4c41 100755
--- a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java
+++ b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java
@@ -31,10 +31,13 @@ public final class AllIndividualFunctionEvaluationTests {
// TODO - have this suite incorporated into a higher level one
public static Test suite() {
TestSuite result = new TestSuite("Tests for org.apache.poi.hssf.record.formula.functions");
+ result.addTestSuite(TestAverage.class);
result.addTestSuite(TestCountFuncs.class);
result.addTestSuite(TestDate.class);
result.addTestSuite(TestFinanceLib.class);
result.addTestSuite(TestIndex.class);
+ result.addTestSuite(TestIsBlank.class);
+ result.addTestSuite(TestLen.class);
result.addTestSuite(TestMid.class);
result.addTestSuite(TestMathX.class);
result.addTestSuite(TestMatch.class);
@@ -43,6 +46,8 @@ public final class AllIndividualFunctionEvaluationTests {
result.addTestSuite(TestSumproduct.class);
result.addTestSuite(TestStatsLib.class);
result.addTestSuite(TestTFunc.class);
+ result.addTestSuite(TestTrim.class);
+ result.addTestSuite(TestXYNumericFunction.class);
return result;
}
diff --git a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/EvalFactory.java b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/EvalFactory.java
index 958c48664..a6e262b86 100755
--- a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/EvalFactory.java
+++ b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/EvalFactory.java
@@ -58,6 +58,6 @@ final class EvalFactory {
* Creates a single RefEval (with value zero)
*/
public static RefEval createRefEval(String refStr) {
- return new Ref2DEval(new ReferencePtg(refStr), ZERO, true);
+ return new Ref2DEval(new ReferencePtg(refStr), ZERO);
}
}
diff --git a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/NumericFunctionInvoker.java b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/NumericFunctionInvoker.java
index 2093d35b1..d47723134 100755
--- a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/NumericFunctionInvoker.java
+++ b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/NumericFunctionInvoker.java
@@ -111,9 +111,6 @@ public final class NumericFunctionInvoker {
if(errorCodesAreEqual(ee, ErrorEval.FUNCTION_NOT_IMPLEMENTED)) {
return "Function not implemented";
}
- if(errorCodesAreEqual(ee, ErrorEval.UNKNOWN_ERROR)) {
- return "Unknown error";
- }
if(errorCodesAreEqual(ee, ErrorEval.VALUE_INVALID)) {
return "Error code: #VALUE! (invalid value)";
}
diff --git a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestAverage.java b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestAverage.java
new file mode 100755
index 000000000..4f0e5fff2
--- /dev/null
+++ b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestAverage.java
@@ -0,0 +1,103 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hssf.record.formula.functions;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.hssf.record.formula.eval.BlankEval;
+import org.apache.poi.hssf.record.formula.eval.BoolEval;
+import org.apache.poi.hssf.record.formula.eval.ErrorEval;
+import org.apache.poi.hssf.record.formula.eval.Eval;
+import org.apache.poi.hssf.record.formula.eval.NumberEval;
+import org.apache.poi.hssf.record.formula.eval.ValueEval;
+/**
+ * Tests for Excel function AVERAGE()
+ *
+ * @author Josh Micich
+ */
+public final class TestAverage extends TestCase {
+
+
+ private static Eval invokeAverage(Eval[] args) {
+ return new Average().evaluate(args, -1, (short)-1);
+ }
+
+ private void confirmAverage(Eval[] args, double expected) {
+ Eval result = invokeAverage(args);
+ assertEquals(NumberEval.class, result.getClass());
+ assertEquals(expected, ((NumberEval)result).getNumberValue(), 0);
+ }
+
+ private void confirmAverage(Eval[] args, ErrorEval expectedError) {
+ Eval result = invokeAverage(args);
+ assertEquals(ErrorEval.class, result.getClass());
+ assertEquals(expectedError.getErrorCode(), ((ErrorEval)result).getErrorCode());
+ }
+
+ public void testBasic() {
+
+ ValueEval[] values = {
+ new NumberEval(1),
+ new NumberEval(2),
+ new NumberEval(3),
+ new NumberEval(4),
+ };
+
+ confirmAverage(values, 2.5);
+
+ values = new ValueEval[] {
+ new NumberEval(1),
+ new NumberEval(2),
+ BlankEval.INSTANCE,
+ new NumberEval(3),
+ BlankEval.INSTANCE,
+ new NumberEval(4),
+ BlankEval.INSTANCE,
+ };
+
+ confirmAverage(values, 2.5);
+ }
+
+ /**
+ * Valid cases where values are not pure numbers
+ */
+ public void testUnusualArgs() {
+ ValueEval[] values = {
+ new NumberEval(1),
+ new NumberEval(2),
+ BoolEval.TRUE,
+ BoolEval.FALSE,
+ };
+
+ confirmAverage(values, 1.0);
+
+ }
+
+ // currently disabled because MultiOperandNumericFunction.getNumberArray(Eval[], int, short)
+ // does not handle error values properly yet
+ public void XtestErrors() {
+ ValueEval[] values = {
+ new NumberEval(1),
+ ErrorEval.NAME_INVALID,
+ new NumberEval(3),
+ ErrorEval.DIV_ZERO,
+ };
+ confirmAverage(values, ErrorEval.NAME_INVALID);
+
+ }
+}
diff --git a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestCountFuncs.java b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestCountFuncs.java
index fbaace921..ae93a2d41 100755
--- a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestCountFuncs.java
+++ b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestCountFuncs.java
@@ -125,7 +125,7 @@ public final class TestCountFuncs extends TestCase {
};
Area2DEval arg0 = new Area2DEval(new AreaPtg("C1:C6"), values);
- Ref2DEval criteriaArg = new Ref2DEval(new ReferencePtg("A1"), new NumberEval(25), true);
+ Ref2DEval criteriaArg = new Ref2DEval(new ReferencePtg("A1"), new NumberEval(25));
Eval[] args= { arg0, criteriaArg, };
double actual = NumericFunctionInvoker.invoke(new Countif(), args);
diff --git a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestIsBlank.java b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestIsBlank.java
new file mode 100755
index 000000000..7ce2bd245
--- /dev/null
+++ b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestIsBlank.java
@@ -0,0 +1,62 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hssf.record.formula.functions;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue;
+/**
+ * Tests for Excel function ISBLANK()
+ *
+ * @author Josh Micich
+ */
+public final class TestIsBlank extends TestCase {
+
+
+
+ public void test3DArea() {
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet sheet1 = wb.createSheet();
+ wb.setSheetName(0, "Sheet1");
+ wb.createSheet();
+ wb.setSheetName(1, "Sheet2");
+ HSSFRow row = sheet1.createRow(0);
+ HSSFCell cell = row.createCell((short)0);
+
+
+ cell.setCellFormula("isblank(Sheet2!A1:A1)");
+
+ HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet1, wb);
+ fe.setCurrentRow(row);
+ CellValue result = fe.evaluate(cell);
+ assertEquals(HSSFCell.CELL_TYPE_BOOLEAN, result.getCellType());
+ assertEquals(true, result.getBooleanValue());
+
+ cell.setCellFormula("isblank(D7:D7)");
+
+ result = fe.evaluate(cell);
+ assertEquals(HSSFCell.CELL_TYPE_BOOLEAN, result.getCellType());
+ assertEquals(true, result.getBooleanValue());
+
+ }
+}
diff --git a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestLen.java b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestLen.java
new file mode 100755
index 000000000..a96fb4e2b
--- /dev/null
+++ b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestLen.java
@@ -0,0 +1,73 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hssf.record.formula.functions;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.hssf.record.formula.eval.BlankEval;
+import org.apache.poi.hssf.record.formula.eval.BoolEval;
+import org.apache.poi.hssf.record.formula.eval.ErrorEval;
+import org.apache.poi.hssf.record.formula.eval.Eval;
+import org.apache.poi.hssf.record.formula.eval.NumberEval;
+import org.apache.poi.hssf.record.formula.eval.StringEval;
+/**
+ * Tests for Excel function LEN()
+ *
+ * @author Josh Micich
+ */
+public final class TestLen extends TestCase {
+
+
+ private static Eval invokeLen(Eval text) {
+ Eval[] args = new Eval[] { text, };
+ return new Len().evaluate(args, -1, (short)-1);
+ }
+
+ private void confirmLen(Eval text, int expected) {
+ Eval result = invokeLen(text);
+ assertEquals(NumberEval.class, result.getClass());
+ assertEquals(expected, ((NumberEval)result).getNumberValue(), 0);
+ }
+
+ private void confirmLen(Eval text, ErrorEval expectedError) {
+ Eval result = invokeLen(text);
+ assertEquals(ErrorEval.class, result.getClass());
+ assertEquals(expectedError.getErrorCode(), ((ErrorEval)result).getErrorCode());
+ }
+
+ public void testBasic() {
+
+ confirmLen(new StringEval("galactic"), 8);
+ }
+
+ /**
+ * Valid cases where text arg is not exactly a string
+ */
+ public void testUnusualArgs() {
+
+ // text (first) arg type is number, other args are strings with fractional digits
+ confirmLen(new NumberEval(123456), 6);
+ confirmLen(BoolEval.FALSE, 5);
+ confirmLen(BoolEval.TRUE, 4);
+ confirmLen(BlankEval.INSTANCE, 0);
+ }
+
+ public void testErrors() {
+ confirmLen(ErrorEval.NAME_INVALID, ErrorEval.NAME_INVALID);
+ }
+}
diff --git a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestMid.java b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestMid.java
index ab9f609d1..dc3d595ae 100755
--- a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestMid.java
+++ b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestMid.java
@@ -77,13 +77,14 @@ public final class TestMid extends TestCase {
// startPos is 1x1 area ref, numChars is cell ref
AreaEval aeStart = new Area2DEval(new AreaPtg("A1:A1"), new ValueEval[] { new NumberEval(2), } );
- RefEval reNumChars = new Ref2DEval(new ReferencePtg("B1"), new NumberEval(3),false);
+ RefEval reNumChars = new Ref2DEval(new ReferencePtg("B1"), new NumberEval(3));
confirmMid(new StringEval("galactic"), aeStart, reNumChars, "ala");
confirmMid(new StringEval("galactic"), new NumberEval(3.1), BlankEval.INSTANCE, "");
confirmMid(new StringEval("galactic"), new NumberEval(3), BoolEval.FALSE, "");
confirmMid(new StringEval("galactic"), new NumberEval(3), BoolEval.TRUE, "l");
+ confirmMid(BlankEval.INSTANCE, new NumberEval(3), BoolEval.TRUE, "");
}
diff --git a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestSumproduct.java b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestSumproduct.java
index 9fbc9e42f..73043911f 100755
--- a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestSumproduct.java
+++ b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestSumproduct.java
@@ -50,7 +50,7 @@ public final class TestSumproduct extends TestCase {
public void testScalarSimple() {
- RefEval refEval = new Ref2DEval(new ReferencePtg("A1"), new NumberEval(3), true);
+ RefEval refEval = new Ref2DEval(new ReferencePtg("A1"), new NumberEval(3));
Eval[] args = {
refEval,
new NumberEval(2),
diff --git a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestTFunc.java b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestTFunc.java
index 6ecc3619c..4d63cad1c 100755
--- a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestTFunc.java
+++ b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestTFunc.java
@@ -50,7 +50,7 @@ public final class TestTFunc extends TestCase {
* where cell A1 has the specified innerValue
*/
private Eval invokeTWithReference(ValueEval innerValue) {
- Eval arg = new Ref2DEval(new ReferencePtg((short)1, (short)1, false, false), innerValue, true);
+ Eval arg = new Ref2DEval(new ReferencePtg((short)1, (short)1, false, false), innerValue);
return invokeT(arg);
}
diff --git a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestTrim.java b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestTrim.java
new file mode 100755
index 000000000..076ac1fc7
--- /dev/null
+++ b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestTrim.java
@@ -0,0 +1,78 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hssf.record.formula.functions;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.hssf.record.formula.eval.BlankEval;
+import org.apache.poi.hssf.record.formula.eval.BoolEval;
+import org.apache.poi.hssf.record.formula.eval.ErrorEval;
+import org.apache.poi.hssf.record.formula.eval.Eval;
+import org.apache.poi.hssf.record.formula.eval.NumberEval;
+import org.apache.poi.hssf.record.formula.eval.StringEval;
+/**
+ * Tests for Excel function TRIM()
+ *
+ * @author Josh Micich
+ */
+public final class TestTrim extends TestCase {
+
+
+ private static Eval invokeTrim(Eval text) {
+ Eval[] args = new Eval[] { text, };
+ return new Trim().evaluate(args, -1, (short)-1);
+ }
+
+ private void confirmTrim(Eval text, String expected) {
+ Eval result = invokeTrim(text);
+ assertEquals(StringEval.class, result.getClass());
+ assertEquals(expected, ((StringEval)result).getStringValue());
+ }
+
+ private void confirmTrim(Eval text, ErrorEval expectedError) {
+ Eval result = invokeTrim(text);
+ assertEquals(ErrorEval.class, result.getClass());
+ assertEquals(expectedError.getErrorCode(), ((ErrorEval)result).getErrorCode());
+ }
+
+ public void testBasic() {
+
+ confirmTrim(new StringEval(" hi "), "hi");
+ confirmTrim(new StringEval("hi "), "hi");
+ confirmTrim(new StringEval(" hi"), "hi");
+ confirmTrim(new StringEval(" hi there "), "hi there");
+ confirmTrim(new StringEval(""), "");
+ confirmTrim(new StringEval(" "), "");
+ }
+
+ /**
+ * Valid cases where text arg is not exactly a string
+ */
+ public void testUnusualArgs() {
+
+ // text (first) arg type is number, other args are strings with fractional digits
+ confirmTrim(new NumberEval(123456), "123456");
+ confirmTrim(BoolEval.FALSE, "FALSE");
+ confirmTrim(BoolEval.TRUE, "TRUE");
+ confirmTrim(BlankEval.INSTANCE, "");
+ }
+
+ public void testErrors() {
+ confirmTrim(ErrorEval.NAME_INVALID, ErrorEval.NAME_INVALID);
+ }
+}
diff --git a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestXYNumericFunction.java b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestXYNumericFunction.java
new file mode 100755
index 000000000..c9f043bd3
--- /dev/null
+++ b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestXYNumericFunction.java
@@ -0,0 +1,139 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hssf.record.formula.functions;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.hssf.record.formula.AreaPtg;
+import org.apache.poi.hssf.record.formula.eval.Area2DEval;
+import org.apache.poi.hssf.record.formula.eval.ErrorEval;
+import org.apache.poi.hssf.record.formula.eval.Eval;
+import org.apache.poi.hssf.record.formula.eval.NumberEval;
+import org.apache.poi.hssf.record.formula.eval.ValueEval;
+/**
+ * Tests for Excel functions SUMX2MY2(), SUMX2PY2(), SUMXMY2()
+ *
+ * @author Josh Micich
+ */
+public final class TestXYNumericFunction extends TestCase {
+ private static final Function SUM_SQUARES = new Sumx2py2();
+ private static final Function DIFF_SQUARES = new Sumx2my2();
+ private static final Function SUM_SQUARES_OF_DIFFS = new Sumxmy2();
+
+ private static Eval invoke(Function function, Eval xArray, Eval yArray) {
+ Eval[] args = new Eval[] { xArray, yArray, };
+ return function.evaluate(args, -1, (short)-1);
+ }
+
+ private void confirm(Function function, Eval xArray, Eval yArray, double expected) {
+ Eval result = invoke(function, xArray, yArray);
+ assertEquals(NumberEval.class, result.getClass());
+ assertEquals(expected, ((NumberEval)result).getNumberValue(), 0);
+ }
+ private void confirmError(Function function, Eval xArray, Eval yArray, ErrorEval expectedError) {
+ Eval result = invoke(function, xArray, yArray);
+ assertEquals(ErrorEval.class, result.getClass());
+ assertEquals(expectedError.getErrorCode(), ((ErrorEval)result).getErrorCode());
+ }
+
+ private void confirmError(Eval xArray, Eval yArray, ErrorEval expectedError) {
+ confirmError(SUM_SQUARES, xArray, yArray, expectedError);
+ confirmError(DIFF_SQUARES, xArray, yArray, expectedError);
+ confirmError(SUM_SQUARES_OF_DIFFS, xArray, yArray, expectedError);
+ }
+
+ public void testBasic() {
+ ValueEval[] xValues = {
+ new NumberEval(1),
+ new NumberEval(2),
+ };
+ ValueEval areaEvalX = createAreaEval(xValues);
+ confirm(SUM_SQUARES, areaEvalX, areaEvalX, 10.0);
+ confirm(DIFF_SQUARES, areaEvalX, areaEvalX, 0.0);
+ confirm(SUM_SQUARES_OF_DIFFS, areaEvalX, areaEvalX, 0.0);
+
+ ValueEval[] yValues = {
+ new NumberEval(3),
+ new NumberEval(4),
+ };
+ ValueEval areaEvalY = createAreaEval(yValues);
+ confirm(SUM_SQUARES, areaEvalX, areaEvalY, 30.0);
+ confirm(DIFF_SQUARES, areaEvalX, areaEvalY, -20.0);
+ confirm(SUM_SQUARES_OF_DIFFS, areaEvalX, areaEvalY, 8.0);
+ }
+
+ /**
+ * number of items in array is not limited to 30
+ */
+ public void testLargeArrays() {
+ ValueEval[] xValues = createMockNumberArray(100, 3);
+ ValueEval[] yValues = createMockNumberArray(100, 2);
+
+ confirm(SUM_SQUARES, createAreaEval(xValues), createAreaEval(yValues), 1300.0);
+ confirm(DIFF_SQUARES, createAreaEval(xValues), createAreaEval(yValues), 500.0);
+ confirm(SUM_SQUARES_OF_DIFFS, createAreaEval(xValues), createAreaEval(yValues), 100.0);
+ }
+
+
+ private ValueEval[] createMockNumberArray(int size, double value) {
+ ValueEval[] result = new ValueEval[size];
+ for (int i = 0; i < result.length; i++) {
+ result[i] = new NumberEval(value);
+ }
+ return result;
+ }
+
+ private static ValueEval createAreaEval(ValueEval[] values) {
+ String refStr = "A1:A" + values.length;
+ return new Area2DEval(new AreaPtg(refStr), values);
+ }
+
+ public void testErrors() {
+ ValueEval[] xValues = {
+ ErrorEval.REF_INVALID,
+ new NumberEval(2),
+ };
+ ValueEval areaEvalX = createAreaEval(xValues);
+ ValueEval[] yValues = {
+ new NumberEval(2),
+ ErrorEval.NULL_INTERSECTION,
+ };
+ ValueEval areaEvalY = createAreaEval(yValues);
+ ValueEval[] zValues = { // wrong size
+ new NumberEval(2),
+ };
+ ValueEval areaEvalZ = createAreaEval(zValues);
+
+ // if either arg is an error, that error propagates
+ confirmError(ErrorEval.REF_INVALID, ErrorEval.NAME_INVALID, ErrorEval.REF_INVALID);
+ confirmError(areaEvalX, ErrorEval.NAME_INVALID, ErrorEval.NAME_INVALID);
+ confirmError(ErrorEval.NAME_INVALID, areaEvalX, ErrorEval.NAME_INVALID);
+
+ // array sizes must match
+ confirmError(areaEvalX, areaEvalZ, ErrorEval.NA);
+ confirmError(areaEvalZ, areaEvalY, ErrorEval.NA);
+
+ // any error in an array item propagates up
+ confirmError(areaEvalX, areaEvalX, ErrorEval.REF_INVALID);
+
+ // search for errors array by array, not pair by pair
+ confirmError(areaEvalX, areaEvalY, ErrorEval.REF_INVALID);
+ confirmError(areaEvalY, areaEvalX, ErrorEval.NULL_INTERSECTION);
+
+ }
+}
diff --git a/src/testcases/org/apache/poi/hssf/HSSFTests.java b/src/testcases/org/apache/poi/hssf/HSSFTests.java
index 7a1d51903..5b597a67c 100644
--- a/src/testcases/org/apache/poi/hssf/HSSFTests.java
+++ b/src/testcases/org/apache/poi/hssf/HSSFTests.java
@@ -14,7 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
-
+
package org.apache.poi.hssf;
import junit.framework.Test;
@@ -25,80 +25,8 @@ import org.apache.poi.hssf.eventmodel.TestModelFactory;
import org.apache.poi.hssf.model.TestDrawingManager;
import org.apache.poi.hssf.model.TestFormulaParser;
import org.apache.poi.hssf.model.TestSheet;
-import org.apache.poi.hssf.record.TestAreaFormatRecord;
-import org.apache.poi.hssf.record.TestAreaRecord;
-import org.apache.poi.hssf.record.TestAxisLineFormatRecord;
-import org.apache.poi.hssf.record.TestAxisOptionsRecord;
-import org.apache.poi.hssf.record.TestAxisParentRecord;
-import org.apache.poi.hssf.record.TestAxisRecord;
-import org.apache.poi.hssf.record.TestAxisUsedRecord;
-import org.apache.poi.hssf.record.TestBarRecord;
-import org.apache.poi.hssf.record.TestBoundSheetRecord;
-import org.apache.poi.hssf.record.TestCategorySeriesAxisRecord;
-import org.apache.poi.hssf.record.TestChartRecord;
-import org.apache.poi.hssf.record.TestDatRecord;
-import org.apache.poi.hssf.record.TestDataFormatRecord;
-import org.apache.poi.hssf.record.TestDefaultDataLabelTextPropertiesRecord;
-import org.apache.poi.hssf.record.TestFontBasisRecord;
-import org.apache.poi.hssf.record.TestFontIndexRecord;
-import org.apache.poi.hssf.record.TestFormulaRecord;
-import org.apache.poi.hssf.record.TestFrameRecord;
-import org.apache.poi.hssf.record.TestLegendRecord;
-import org.apache.poi.hssf.record.TestLineFormatRecord;
-import org.apache.poi.hssf.record.TestLinkedDataRecord;
-import org.apache.poi.hssf.record.TestNameRecord;
-import org.apache.poi.hssf.record.TestNumberFormatIndexRecord;
-import org.apache.poi.hssf.record.TestObjectLinkRecord;
-import org.apache.poi.hssf.record.TestPaletteRecord;
-import org.apache.poi.hssf.record.TestPlotAreaRecord;
-import org.apache.poi.hssf.record.TestPlotGrowthRecord;
-import org.apache.poi.hssf.record.TestRecordFactory;
-import org.apache.poi.hssf.record.TestSCLRecord;
-import org.apache.poi.hssf.record.TestSSTDeserializer;
-import org.apache.poi.hssf.record.TestSSTRecord;
-import org.apache.poi.hssf.record.TestSSTRecordSizeCalculator;
-import org.apache.poi.hssf.record.TestSeriesChartGroupIndexRecord;
-import org.apache.poi.hssf.record.TestSeriesIndexRecord;
-import org.apache.poi.hssf.record.TestSeriesLabelsRecord;
-import org.apache.poi.hssf.record.TestSeriesListRecord;
-import org.apache.poi.hssf.record.TestSeriesRecord;
-import org.apache.poi.hssf.record.TestSeriesTextRecord;
-import org.apache.poi.hssf.record.TestSeriesToChartGroupRecord;
-import org.apache.poi.hssf.record.TestSheetPropertiesRecord;
-import org.apache.poi.hssf.record.TestStringRecord;
-import org.apache.poi.hssf.record.TestSupBookRecord;
-import org.apache.poi.hssf.record.TestTextRecord;
-import org.apache.poi.hssf.record.TestTickRecord;
-import org.apache.poi.hssf.record.TestUnicodeString;
-import org.apache.poi.hssf.record.TestUnitsRecord;
-import org.apache.poi.hssf.record.TestValueRangeRecord;
-import org.apache.poi.hssf.record.aggregates.TestRowRecordsAggregate;
-import org.apache.poi.hssf.record.aggregates.TestValueRecordsAggregate;
-import org.apache.poi.hssf.record.formula.AllFormulaTests;
-import org.apache.poi.hssf.usermodel.TestBugs;
-import org.apache.poi.hssf.usermodel.TestCellStyle;
-import org.apache.poi.hssf.usermodel.TestCloneSheet;
-import org.apache.poi.hssf.usermodel.TestEscherGraphics;
-import org.apache.poi.hssf.usermodel.TestEscherGraphics2d;
-import org.apache.poi.hssf.usermodel.TestFontDetails;
-import org.apache.poi.hssf.usermodel.TestFormulas;
-import org.apache.poi.hssf.usermodel.TestHSSFCell;
-import org.apache.poi.hssf.usermodel.TestHSSFClientAnchor;
-import org.apache.poi.hssf.usermodel.TestHSSFComment;
-import org.apache.poi.hssf.usermodel.TestHSSFDateUtil;
-import org.apache.poi.hssf.usermodel.TestHSSFHeaderFooter;
-import org.apache.poi.hssf.usermodel.TestHSSFPalette;
-import org.apache.poi.hssf.usermodel.TestHSSFRichTextString;
-import org.apache.poi.hssf.usermodel.TestHSSFRow;
-import org.apache.poi.hssf.usermodel.TestHSSFSheet;
-import org.apache.poi.hssf.usermodel.TestHSSFSheetOrder;
-import org.apache.poi.hssf.usermodel.TestHSSFSheetSetOrder;
-import org.apache.poi.hssf.usermodel.TestHSSFWorkbook;
-import org.apache.poi.hssf.usermodel.TestNamedRange;
-import org.apache.poi.hssf.usermodel.TestReadWriteChart;
-import org.apache.poi.hssf.usermodel.TestSanityChecker;
-import org.apache.poi.hssf.usermodel.TestSheetShiftRows;
-import org.apache.poi.hssf.usermodel.TestWorkbook;
+import org.apache.poi.hssf.record.AllRecordTests;
+import org.apache.poi.hssf.usermodel.AllUserModelTests;
import org.apache.poi.hssf.util.TestAreaReference;
import org.apache.poi.hssf.util.TestCellReference;
import org.apache.poi.hssf.util.TestRKUtil;
@@ -112,113 +40,30 @@ import org.apache.poi.hssf.util.TestSheetReferences;
*
* @author Andrew C. Oliver acoliver@apache.org
*/
-public final class HSSFTests
-{
+public final class HSSFTests {
- public static void main(String[] args)
- {
+ public static void main(String[] args) {
junit.textui.TestRunner.run(suite());
}
- public static Test suite()
- {
- TestSuite suite =
- new TestSuite("Tests for org.apache.poi.hssf");
- //$JUnit-BEGIN$
-
- suite.addTest(new TestSuite(TestBugs.class));
- suite.addTest(new TestSuite(TestCloneSheet.class));
- suite.addTest(new TestSuite(TestEscherGraphics.class));
- suite.addTest(new TestSuite(TestEscherGraphics2d.class));
- suite.addTest(new TestSuite(TestFontDetails.class));
- suite.addTest(new TestSuite(TestHSSFClientAnchor.class));
- suite.addTest(new TestSuite(TestHSSFHeaderFooter.class));
- suite.addTest(new TestSuite(TestHSSFRichTextString.class));
- suite.addTest(new TestSuite(TestHSSFSheetOrder.class));
- suite.addTest(new TestSuite(TestHSSFSheetSetOrder.class));
- suite.addTest(new TestSuite(TestHSSFWorkbook.class));
- suite.addTest(new TestSuite(TestSanityChecker.class));
- suite.addTest(new TestSuite(TestSheetShiftRows.class));
-
- suite.addTest(new TestSuite(TestCellStyle.class));
- suite.addTest(new TestSuite(TestFormulas.class));
- suite.addTest(new TestSuite(TestHSSFCell.class));
- suite.addTest(new TestSuite(TestHSSFDateUtil.class));
- suite.addTest(new TestSuite(TestHSSFPalette.class));
- suite.addTest(new TestSuite(TestHSSFRow.class));
- suite.addTest(new TestSuite(TestHSSFSheet.class));
- suite.addTest(new TestSuite(TestNamedRange.class));
- suite.addTest(new TestSuite(TestReadWriteChart.class));
- suite.addTest(new TestSuite(TestWorkbook.class));
-
+ public static Test suite() {
+ TestSuite suite = new TestSuite("Tests for org.apache.poi.hssf");
+ // $JUnit-BEGIN$
+ suite.addTest(AllUserModelTests.suite());
+ suite.addTest(AllRecordTests.suite());
suite.addTest(new TestSuite(TestFormulaParser.class));
- suite.addTest(new TestSuite(TestAreaFormatRecord.class));
- suite.addTest(new TestSuite(TestAreaRecord.class));
- suite.addTest(new TestSuite(TestAxisLineFormatRecord.class));
- suite.addTest(new TestSuite(TestAxisOptionsRecord.class));
- suite.addTest(new TestSuite(TestAxisParentRecord.class));
- suite.addTest(new TestSuite(TestAxisRecord.class));
- suite.addTest(new TestSuite(TestAxisUsedRecord.class));
- suite.addTest(new TestSuite(TestBarRecord.class));
- suite.addTest(new TestSuite(TestBoundSheetRecord.class));
- suite.addTest(new TestSuite(TestCategorySeriesAxisRecord.class));
- suite.addTest(new TestSuite(TestChartRecord.class));
- suite.addTest(new TestSuite(TestDatRecord.class));
- suite.addTest(new TestSuite(TestDataFormatRecord.class));
- suite.addTest(
- new TestSuite(TestDefaultDataLabelTextPropertiesRecord.class));
- suite.addTest(new TestSuite(TestFontBasisRecord.class));
- suite.addTest(new TestSuite(TestFontIndexRecord.class));
- suite.addTest(new TestSuite(TestFormulaRecord.class));
- suite.addTest(new TestSuite(TestFrameRecord.class));
- suite.addTest(new TestSuite(TestLegendRecord.class));
- suite.addTest(new TestSuite(TestLineFormatRecord.class));
- suite.addTest(new TestSuite(TestLinkedDataRecord.class));
- suite.addTest(new TestSuite(TestNumberFormatIndexRecord.class));
- suite.addTest(new TestSuite(TestObjectLinkRecord.class));
- suite.addTest(new TestSuite(TestPaletteRecord.class));
- suite.addTest(new TestSuite(TestPlotAreaRecord.class));
- suite.addTest(new TestSuite(TestPlotGrowthRecord.class));
- suite.addTest(new TestSuite(TestRecordFactory.class));
- suite.addTest(new TestSuite(TestSCLRecord.class));
- suite.addTest(new TestSuite(TestSSTDeserializer.class));
- suite.addTest(new TestSuite(TestSSTRecord.class));
- suite.addTest(new TestSuite(TestSSTRecordSizeCalculator.class));
- suite.addTest(new TestSuite(TestSeriesChartGroupIndexRecord.class));
- suite.addTest(new TestSuite(TestSeriesIndexRecord.class));
- suite.addTest(new TestSuite(TestSeriesLabelsRecord.class));
- suite.addTest(new TestSuite(TestSeriesListRecord.class));
- suite.addTest(new TestSuite(TestSeriesRecord.class));
- suite.addTest(new TestSuite(TestSeriesTextRecord.class));
- suite.addTest(new TestSuite(TestSeriesToChartGroupRecord.class));
- suite.addTest(new TestSuite(TestSheetPropertiesRecord.class));
- suite.addTest(new TestSuite(TestStringRecord.class));
- suite.addTest(new TestSuite(TestSupBookRecord.class));
- suite.addTest(new TestSuite(TestTextRecord.class));
- suite.addTest(new TestSuite(TestTickRecord.class));
- suite.addTest(new TestSuite(TestUnicodeString.class));
- suite.addTest(new TestSuite(TestUnitsRecord.class));
- suite.addTest(new TestSuite(TestValueRangeRecord.class));
- suite.addTest(new TestSuite(TestRowRecordsAggregate.class));
suite.addTest(new TestSuite(TestAreaReference.class));
suite.addTest(new TestSuite(TestCellReference.class));
- suite.addTest(new TestSuite(TestRangeAddress.class));
+ suite.addTest(new TestSuite(TestRangeAddress.class));
suite.addTest(new TestSuite(TestRKUtil.class));
suite.addTest(new TestSuite(TestSheetReferences.class));
-
-
- suite.addTest(AllFormulaTests.suite());
- suite.addTest(new TestSuite(TestValueRecordsAggregate.class));
- suite.addTest(new TestSuite(TestNameRecord.class));
- suite.addTest(new TestSuite(TestEventRecordFactory.class));
- suite.addTest(new TestSuite(TestModelFactory.class));
- suite.addTest(new TestSuite(TestDrawingManager.class));
- suite.addTest(new TestSuite(TestSheet.class));
-
- suite.addTest(new TestSuite(TestHSSFComment.class));
- //$JUnit-END$
+ suite.addTest(new TestSuite(TestEventRecordFactory.class));
+ suite.addTest(new TestSuite(TestModelFactory.class));
+ suite.addTest(new TestSuite(TestDrawingManager.class));
+ suite.addTest(new TestSuite(TestSheet.class));
+ // $JUnit-END$
return suite;
}
}
diff --git a/src/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls b/src/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls
index 97766d6aaa13fb4633854f7b88bdb43c850a7427..6260d878bc89ada3ebe92c7fa615baa1c387cde3 100644
GIT binary patch
delta 25973
zcmai-4}1+*`v0FZGs(UACqY6&$dv>^a3x6mi6q3IEv;BeTiv3BAS8%oOPeSyZCSRX
z9WC98DvEDuqrFb~A&Xq+
ASt-??HE{OSN&iyL#ddH5^A1TYjQQp`0*r2`@c
zp
dW5voZ^$v~1=2DC@=o
QOqvqoqVM6ZKut9Uq&7n
zWnhng!pqPeFM?N