changes/status for #44675, #44695, #44691

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@642904 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Josh Micich 2008-03-31 06:55:04 +00:00
parent ede1814a1d
commit 5b0efa8e57
23 changed files with 233 additions and 224 deletions

View File

@ -37,6 +37,7 @@
<!-- Don't forget to update status.xml too! --> <!-- Don't forget to update status.xml too! -->
<release version="3.0.3-beta1" date="2008-04-??"> <release version="3.0.3-beta1" date="2008-04-??">
<action dev="POI-DEVELOPERS" type="add">Various fixes: Recognising var-arg built-in functions #44675, ExternalNameRecord serialisation bug #44695, PMT() bug #44691</action>
<action dev="POI-DEVELOPERS" type="add">30311 - More work on Conditional Formatting</action> <action dev="POI-DEVELOPERS" type="add">30311 - More work on Conditional Formatting</action>
<action dev="POI-DEVELOPERS" type="add">Move the Formula Evaluator code out of scratchpad</action> <action dev="POI-DEVELOPERS" type="add">Move the Formula Evaluator code out of scratchpad</action>
<action dev="POI-DEVELOPERS" type="add">Move the missing record aware eventusermodel code out of scratchpad</action> <action dev="POI-DEVELOPERS" type="add">Move the missing record aware eventusermodel code out of scratchpad</action>

View File

@ -34,6 +34,7 @@
<!-- Don't forget to update changes.xml too! --> <!-- Don't forget to update changes.xml too! -->
<changes> <changes>
<release version="3.0.3-beta1" date="2008-04-??"> <release version="3.0.3-beta1" date="2008-04-??">
<action dev="POI-DEVELOPERS" type="add">Various fixes: Recognising var-arg built-in functions #44675, ExternalNameRecord serialisation bug #44695, PMT() bug #44691</action>
<action dev="POI-DEVELOPERS" type="add">30311 - More work on Conditional Formatting</action> <action dev="POI-DEVELOPERS" type="add">30311 - More work on Conditional Formatting</action>
<action dev="POI-DEVELOPERS" type="add">Move the Formula Evaluator code out of scratchpad</action> <action dev="POI-DEVELOPERS" type="add">Move the Formula Evaluator code out of scratchpad</action>
<action dev="POI-DEVELOPERS" type="add">Move the missing record aware eventusermodel code out of scratchpad</action> <action dev="POI-DEVELOPERS" type="add">Move the missing record aware eventusermodel code out of scratchpad</action>

View File

@ -28,8 +28,6 @@ import org.apache.poi.hssf.record.formula.*;
import org.apache.poi.hssf.record.formula.function.FunctionMetadata; import org.apache.poi.hssf.record.formula.function.FunctionMetadata;
import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry; import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
/** /**
* This class parses a formula string into a List of tokens in RPN order. * This class parses a formula string into a List of tokens in RPN order.
* Inspired by * Inspired by
@ -48,11 +46,11 @@ import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
* @author Pavel Krupets (pkrupets at palmtreebusiness dot com) * @author Pavel Krupets (pkrupets at palmtreebusiness dot com)
*/ */
public final class FormulaParser { public final class FormulaParser {
/** /**
* Specific exception thrown when a supplied formula does not parse properly.<br/> * Specific exception thrown when a supplied formula does not parse properly.<br/>
* Primarily used by test cases when testing for specific parsing exceptions.</p> * Primarily used by test cases when testing for specific parsing exceptions.</p>
* *
*/ */
static final class FormulaParseException extends RuntimeException { static final class FormulaParseException extends RuntimeException {
// This class was given package scope until it would become clear that it is useful to // This class was given package scope until it would become clear that it is useful to

View File

@ -37,7 +37,7 @@ public final class ExternalNameRecord extends Record {
private static final int OPT_PICTURE_LINK = 0x0004; private static final int OPT_PICTURE_LINK = 0x0004;
private static final int OPT_STD_DOCUMENT_NAME = 0x0008; private static final int OPT_STD_DOCUMENT_NAME = 0x0008;
private static final int OPT_OLE_LINK = 0x0010; private static final int OPT_OLE_LINK = 0x0010;
// private static final int OPT_CLIP_FORMAT_MASK = 0x7FE0; // private static final int OPT_CLIP_FORMAT_MASK = 0x7FE0;
private static final int OPT_ICONIFIED_PICTURE_LINK= 0x8000; private static final int OPT_ICONIFIED_PICTURE_LINK= 0x8000;

View File

@ -23,27 +23,27 @@ import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
/** /**
* This class provides the base functionality for Excel sheet functions * This class provides the base functionality for Excel sheet functions
* There are two kinds of function Ptgs - tFunc and tFuncVar * There are two kinds of function Ptgs - tFunc and tFuncVar
* Therefore, this class will have ONLY two subclasses * Therefore, this class will have ONLY two subclasses
* @author Avik Sengupta * @author Avik Sengupta
* @author Andrew C. Oliver (acoliver at apache dot org) * @author Andrew C. Oliver (acoliver at apache dot org)
*/ */
public abstract class AbstractFunctionPtg extends OperationPtg { public abstract class AbstractFunctionPtg extends OperationPtg {
/** /**
* The name of the IF function (i.e. "IF"). Extracted as a constant for clarity. * The name of the IF function (i.e. "IF"). Extracted as a constant for clarity.
*/ */
public static final String FUNCTION_NAME_IF = "IF"; public static final String FUNCTION_NAME_IF = "IF";
/** All external functions have function index 255 */ /** All external functions have function index 255 */
private static final short FUNCTION_INDEX_EXTERNAL = 255; private static final short FUNCTION_INDEX_EXTERNAL = 255;
protected byte returnClass; protected byte returnClass;
protected byte[] paramClass; protected byte[] paramClass;
protected byte field_1_num_args; protected byte field_1_num_args;
protected short field_2_fnc_index; protected short field_2_fnc_index;
public String toString() { public String toString() {
StringBuffer sb = new StringBuffer(64); StringBuffer sb = new StringBuffer(64);
sb.append(getClass().getName()).append(" ["); sb.append(getClass().getName()).append(" [");
@ -51,17 +51,17 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
sb.append("]"); sb.append("]");
return sb.toString(); return sb.toString();
} }
public int getType() { public int getType() {
return -1; return -1;
} }
public short getFunctionIndex() { public short getFunctionIndex() {
return field_2_fnc_index; return field_2_fnc_index;
} }
public String getName() { public String getName() {
return lookupName(field_2_fnc_index); return lookupName(field_2_fnc_index);
} }
@ -72,14 +72,14 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
public boolean isExternalFunction() { public boolean isExternalFunction() {
return field_2_fnc_index == FUNCTION_INDEX_EXTERNAL; return field_2_fnc_index == FUNCTION_INDEX_EXTERNAL;
} }
public String toFormulaString(Workbook book) { public String toFormulaString(Workbook book) {
return getName(); return getName();
} }
public String toFormulaString(String[] operands) { public String toFormulaString(String[] operands) {
StringBuffer buf = new StringBuffer(); StringBuffer buf = new StringBuffer();
if(isExternalFunction()) { if(isExternalFunction()) {
buf.append(operands[0]); // first operand is actually the function name buf.append(operands[0]); // first operand is actually the function name
appendArgs(buf, 1, operands); appendArgs(buf, 1, operands);
@ -100,23 +100,23 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
} }
buf.append(")"); buf.append(")");
} }
public abstract void writeBytes(byte[] array, int offset); public abstract void writeBytes(byte[] array, int offset);
public abstract int getSize(); public abstract int getSize();
/** /**
* Used to detect whether a function name found in a formula is one of the standard excel functions * Used to detect whether a function name found in a formula is one of the standard excel functions
* <p> * <p>
* The name matching is case insensitive. * The name matching is case insensitive.
* @return <code>true</code> if the name specifies a standard worksheet function, * @return <code>true</code> if the name specifies a standard worksheet function,
* <code>false</code> if the name should be assumed to be an external function. * <code>false</code> if the name should be assumed to be an external function.
*/ */
public static final boolean isInternalFunctionName(String name) { public static final boolean isInternalFunctionName(String name) {
short ix = FunctionMetadataRegistry.lookupIndexByName(name.toUpperCase()); short ix = FunctionMetadataRegistry.lookupIndexByName(name.toUpperCase());
return ix >= 0; return ix >= 0;
} }
protected String lookupName(short index) { protected String lookupName(short index) {
if(index == FunctionMetadataRegistry.FUNCTION_INDEX_EXTERNAL) { if(index == FunctionMetadataRegistry.FUNCTION_INDEX_EXTERNAL) {
return "#external#"; return "#external#";
@ -127,7 +127,7 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
} }
return fm.getName(); return fm.getName();
} }
/** /**
* Resolves internal function names into function indexes. * Resolves internal function names into function indexes.
* <p> * <p>
@ -145,7 +145,7 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
public byte getDefaultOperandClass() { public byte getDefaultOperandClass() {
return returnClass; return returnClass;
} }
public byte getParameterClass(int index) { public byte getParameterClass(int index) {
try { try {
return paramClass[index]; return paramClass[index];

View File

@ -27,11 +27,11 @@ import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
* @author Danny Mui (dmui at apache dot org) (Leftover handling) * @author Danny Mui (dmui at apache dot org) (Leftover handling)
*/ */
public final class FuncPtg extends AbstractFunctionPtg { public final class FuncPtg extends AbstractFunctionPtg {
public final static byte sid = 0x21; public final static byte sid = 0x21;
public final static int SIZE = 3; public final static int SIZE = 3;
private int numParams=0; private int numParams=0;
/** /**
* FuncPtgs are defined to be 4 bytes but the actual FuncPtg uses only 2 bytes. * FuncPtgs are defined to be 4 bytes but the actual FuncPtg uses only 2 bytes.
* If we have leftOvers that are read from the file we should serialize them back out. * If we have leftOvers that are read from the file we should serialize them back out.
@ -39,18 +39,18 @@ public final class FuncPtg extends AbstractFunctionPtg {
* If the leftovers are removed, a prompt "Warning: Data may have been lost occurs in Excel" * If the leftovers are removed, a prompt "Warning: Data may have been lost occurs in Excel"
*/ */
//protected byte[] leftOvers = null; //protected byte[] leftOvers = null;
private FuncPtg() { private FuncPtg() {
//Required for clone methods //Required for clone methods
} }
/**Creates new function pointer from a byte array /**Creates new function pointer from a byte array
* usually called while reading an excel file. * usually called while reading an excel file.
*/ */
public FuncPtg(RecordInputStream in) { public FuncPtg(RecordInputStream in) {
//field_1_num_args = data[ offset + 0 ]; //field_1_num_args = data[ offset + 0 ];
field_2_fnc_index = in.readShort(); field_2_fnc_index = in.readShort();
FunctionMetadata fm = FunctionMetadataRegistry.getFunctionByIndex(field_2_fnc_index); FunctionMetadata fm = FunctionMetadataRegistry.getFunctionByIndex(field_2_fnc_index);
if(fm == null) { if(fm == null) {
throw new RuntimeException("Invalid built-in function index (" + field_2_fnc_index + ")"); throw new RuntimeException("Invalid built-in function index (" + field_2_fnc_index + ")");
@ -62,12 +62,12 @@ public final class FuncPtg extends AbstractFunctionPtg {
numParams = numberOfParameters; numParams = numberOfParameters;
paramClass = new byte[] { Ptg.CLASS_VALUE, }; // TODO paramClass = new byte[] { Ptg.CLASS_VALUE, }; // TODO
} }
public void writeBytes(byte[] array, int offset) { public void writeBytes(byte[] array, int offset) {
array[offset+0]= (byte) (sid + ptgClass); array[offset+0]= (byte) (sid + ptgClass);
LittleEndian.putShort(array,offset+1,field_2_fnc_index); LittleEndian.putShort(array,offset+1,field_2_fnc_index);
} }
public int getNumberOfOperands() { public int getNumberOfOperands() {
return numParams; return numParams;
} }
@ -79,11 +79,11 @@ public final class FuncPtg extends AbstractFunctionPtg {
ptg.setClass(ptgClass); ptg.setClass(ptgClass);
return ptg; return ptg;
} }
public int getSize() { public int getSize() {
return SIZE; return SIZE;
} }
public String toString() { public String toString() {
StringBuffer sb = new StringBuffer(64); StringBuffer sb = new StringBuffer(64);
sb.append(getClass().getName()).append(" ["); sb.append(getClass().getName()).append(" [");

View File

@ -15,7 +15,6 @@
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.hssf.record.formula; package org.apache.poi.hssf.record.formula;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.hssf.record.RecordInputStream;
@ -27,22 +26,22 @@ import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
* @author Jason Height (jheight at chariot dot net dot au) * @author Jason Height (jheight at chariot dot net dot au)
*/ */
public final class FuncVarPtg extends AbstractFunctionPtg{ public final class FuncVarPtg extends AbstractFunctionPtg{
public final static byte sid = 0x22; public final static byte sid = 0x22;
private final static int SIZE = 4; private final static int SIZE = 4;
private FuncVarPtg() { private FuncVarPtg() {
//Required for clone methods //Required for clone methods
} }
/**Creates new function pointer from a byte array /**Creates new function pointer from a byte array
* usually called while reading an excel file. * usually called while reading an excel file.
*/ */
public FuncVarPtg(RecordInputStream in) { public FuncVarPtg(RecordInputStream in) {
field_1_num_args = in.readByte(); field_1_num_args = in.readByte();
field_2_fnc_index = in.readShort(); field_2_fnc_index = in.readShort();
} }
/** /**
* Create a function ptg from a string tokenised by the parser * Create a function ptg from a string tokenised by the parser
*/ */
@ -59,17 +58,17 @@ public final class FuncVarPtg extends AbstractFunctionPtg{
paramClass = new byte[] {Ptg.CLASS_VALUE}; paramClass = new byte[] {Ptg.CLASS_VALUE};
} }
} }
public void writeBytes(byte[] array, int offset) { public void writeBytes(byte[] array, int offset) {
array[offset+0]=(byte) (sid + ptgClass); array[offset+0]=(byte) (sid + ptgClass);
array[offset+1]=field_1_num_args; array[offset+1]=field_1_num_args;
LittleEndian.putShort(array,offset+2,field_2_fnc_index); LittleEndian.putShort(array,offset+2,field_2_fnc_index);
} }
public int getNumberOfOperands() { public int getNumberOfOperands() {
return field_1_num_args; return field_1_num_args;
} }
public Object clone() { public Object clone() {
FuncVarPtg ptg = new FuncVarPtg(); FuncVarPtg ptg = new FuncVarPtg();
ptg.field_1_num_args = field_1_num_args; ptg.field_1_num_args = field_1_num_args;
@ -77,11 +76,11 @@ public final class FuncVarPtg extends AbstractFunctionPtg{
ptg.setClass(ptgClass); ptg.setClass(ptgClass);
return ptg; return ptg;
} }
public int getSize() { public int getSize() {
return SIZE; return SIZE;
} }
public String toString() { public String toString() {
StringBuffer sb = new StringBuffer(64); StringBuffer sb = new StringBuffer(64);
sb.append(getClass().getName()).append(" ["); sb.append(getClass().getName()).append(" [");

View File

@ -23,7 +23,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
/** /**
* Temporarily collects <tt>FunctionMetadata</tt> instances for creation of a * Temporarily collects <tt>FunctionMetadata</tt> instances for creation of a
* <tt>FunctionMetadataRegistry</tt>. * <tt>FunctionMetadataRegistry</tt>.
* *
* @author Josh Micich * @author Josh Micich

View File

@ -17,6 +17,7 @@
package org.apache.poi.hssf.record.formula.function; package org.apache.poi.hssf.record.formula.function;
/** /**
* Holds information about Excel built-in functions.
* *
* @author Josh Micich * @author Josh Micich
*/ */
@ -46,7 +47,7 @@ public final class FunctionMetadata {
return _maxParams; return _maxParams;
} }
public boolean hasFixedArgsLength() { public boolean hasFixedArgsLength() {
return _minParams == _maxParams; return _minParams == _maxParams;
} }
public String toString() { public String toString() {
StringBuffer sb = new StringBuffer(64); StringBuffer sb = new StringBuffer(64);

View File

@ -46,7 +46,7 @@ final class FunctionMetadataReader {
public static FunctionMetadataRegistry createRegistry() { public static FunctionMetadataRegistry createRegistry() {
InputStream is = FunctionMetadataReader.class.getResourceAsStream(METADATA_FILE_NAME); InputStream is = FunctionMetadataReader.class.getResourceAsStream(METADATA_FILE_NAME);
if(is == null) { if (is == null) {
throw new RuntimeException("resource '" + METADATA_FILE_NAME + "' not found"); throw new RuntimeException("resource '" + METADATA_FILE_NAME + "' not found");
} }

View File

@ -19,7 +19,11 @@ package org.apache.poi.hssf.record.formula.function;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
/**
* Allows clients to get <tt>FunctionMetadata</tt> instances for any built-in function of Excel.
*
* @author Josh Micich
*/
public final class FunctionMetadataRegistry { public final class FunctionMetadataRegistry {
/** /**
* The name of the IF function (i.e. "IF"). Extracted as a constant for clarity. * The name of the IF function (i.e. "IF"). Extracted as a constant for clarity.
@ -35,7 +39,6 @@ public final class FunctionMetadataRegistry {
private static FunctionMetadataRegistry getInstance() { private static FunctionMetadataRegistry getInstance() {
if (_instance == null) { if (_instance == null) {
_instance = FunctionMetadataReader.createRegistry(); _instance = FunctionMetadataReader.createRegistry();
// _instance = POIFunctionMetadataCreator.createInstance();
} }
return _instance; return _instance;
} }

View File

@ -46,15 +46,15 @@ public final class Pmt extends FinanceFunction {
if(args.length < 3 || args.length > 5) { if(args.length < 3 || args.length > 5) {
return ErrorEval.VALUE_INVALID; return ErrorEval.VALUE_INVALID;
} }
try { try {
// evaluate first three (always present) args // evaluate first three (always present) args
double rate = evalArg(args[0], srcRow, srcCol); double rate = evalArg(args[0], srcRow, srcCol);
double nper = evalArg(args[1], srcRow, srcCol); double nper = evalArg(args[1], srcRow, srcCol);
double pv = evalArg(args[2], srcRow, srcCol); double pv = evalArg(args[2], srcRow, srcCol);
double fv = 0; double fv = 0;
boolean arePaymentsAtPeriodBeginning = false; boolean arePaymentsAtPeriodBeginning = false;
switch (args.length) { switch (args.length) {
case 5: case 5:
ValueEval ve = singleOperandNumericAsBoolean(args[4], srcRow, srcCol); ValueEval ve = singleOperandNumericAsBoolean(args[4], srcRow, srcCol);
@ -67,10 +67,10 @@ public final class Pmt extends FinanceFunction {
} }
double d = FinanceLib.pmt(rate, nper, pv, fv, arePaymentsAtPeriodBeginning); double d = FinanceLib.pmt(rate, nper, pv, fv, arePaymentsAtPeriodBeginning);
if (Double.isNaN(d)) { if (Double.isNaN(d)) {
return (ValueEval) ErrorEval.VALUE_INVALID; return ErrorEval.VALUE_INVALID;
} }
if (Double.isInfinite(d)) { if (Double.isInfinite(d)) {
return (ValueEval) ErrorEval.NUM_ERROR; return ErrorEval.NUM_ERROR;
} }
return new NumberEval(d); return new NumberEval(d);
} catch (EvaluationException e) { } catch (EvaluationException e) {

View File

@ -14,7 +14,7 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.hssf.model; package org.apache.poi.hssf.model;
import junit.framework.AssertionFailedError; import junit.framework.AssertionFailedError;
@ -54,7 +54,7 @@ import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.hssf.usermodel.HSSFWorkbook;
/** /**
* Test the low level formula parser functionality. High level tests are to * Test the low level formula parser functionality. High level tests are to
* be done via usermodel/HSSFCell.setFormulaValue() . * be done via usermodel/HSSFCell.setFormulaValue() .
* Some tests are also done in scratchpad, if they need * Some tests are also done in scratchpad, if they need
* HSSFFormulaEvaluator, which is there * HSSFFormulaEvaluator, which is there
@ -71,7 +71,7 @@ public final class TestFormulaParser extends TestCase {
assertNotNull("Ptg array should not be null", result); assertNotNull("Ptg array should not be null", result);
return result; return result;
} }
public void testSimpleFormula() { public void testSimpleFormula() {
FormulaParser fp = new FormulaParser("2+2",null); FormulaParser fp = new FormulaParser("2+2",null);
fp.parse(); fp.parse();
@ -86,9 +86,9 @@ public final class TestFormulaParser extends TestCase {
assertTrue("",(ptgs[0] instanceof IntPtg)); assertTrue("",(ptgs[0] instanceof IntPtg));
assertTrue("",(ptgs[1] instanceof IntPtg)); assertTrue("",(ptgs[1] instanceof IntPtg));
assertTrue("",(ptgs[2] instanceof AddPtg)); assertTrue("",(ptgs[2] instanceof AddPtg));
} }
public void testFormulaWithSpace2() { public void testFormulaWithSpace2() {
Ptg[] ptgs; Ptg[] ptgs;
FormulaParser fp; FormulaParser fp;
@ -97,7 +97,7 @@ public final class TestFormulaParser extends TestCase {
ptgs = fp.getRPNPtg(); ptgs = fp.getRPNPtg();
assertTrue("five tokens expected, got "+ptgs.length,ptgs.length == 5); assertTrue("five tokens expected, got "+ptgs.length,ptgs.length == 5);
} }
public void testFormulaWithSpaceNRef() { public void testFormulaWithSpaceNRef() {
Ptg[] ptgs; Ptg[] ptgs;
FormulaParser fp; FormulaParser fp;
@ -106,7 +106,7 @@ public final class TestFormulaParser extends TestCase {
ptgs = fp.getRPNPtg(); ptgs = fp.getRPNPtg();
assertTrue("two tokens expected, got "+ptgs.length,ptgs.length == 2); assertTrue("two tokens expected, got "+ptgs.length,ptgs.length == 2);
} }
public void testFormulaWithString() { public void testFormulaWithString() {
Ptg[] ptgs; Ptg[] ptgs;
FormulaParser fp; FormulaParser fp;
@ -172,7 +172,7 @@ public final class TestFormulaParser extends TestCase {
} }
/** /**
* Make sure the ptgs are generated properly with two functions embedded * Make sure the ptgs are generated properly with two functions embedded
* *
@ -225,7 +225,7 @@ public final class TestFormulaParser extends TestCase {
assertEquals("4 Ptgs expected", 4, asts.length); assertEquals("4 Ptgs expected", 4, asts.length);
} }
/** /**
* Bug Reported by xt-jens.riis@nokia.com (Jens Riis) * Bug Reported by xt-jens.riis@nokia.com (Jens Riis)
* Refers to Bug <a href="http://issues.apache.org/bugzilla/show_bug.cgi?id=17582">#17582</a> * Refers to Bug <a href="http://issues.apache.org/bugzilla/show_bug.cgi?id=17582">#17582</a>
@ -247,7 +247,7 @@ public final class TestFormulaParser extends TestCase {
} }
public void testSimpleLogical() { public void testSimpleLogical() {
FormulaParser fp=new FormulaParser("IF(A1<A2,B1,B2)",null); FormulaParser fp=new FormulaParser("IF(A1<A2,B1,B2)",null);
fp.parse(); fp.parse();
@ -255,10 +255,10 @@ public final class TestFormulaParser extends TestCase {
assertTrue("Ptg array should not be null", ptgs !=null); assertTrue("Ptg array should not be null", ptgs !=null);
assertEquals("Ptg array length", 9, ptgs.length); assertEquals("Ptg array length", 9, ptgs.length);
assertEquals("3rd Ptg is less than",LessThanPtg.class,ptgs[2].getClass()); assertEquals("3rd Ptg is less than",LessThanPtg.class,ptgs[2].getClass());
} }
public void testParenIf() { public void testParenIf() {
FormulaParser fp=new FormulaParser("IF((A1+A2)<=3,\"yes\",\"no\")",null); FormulaParser fp=new FormulaParser("IF((A1+A2)<=3,\"yes\",\"no\")",null);
fp.parse(); fp.parse();
@ -281,7 +281,7 @@ public final class TestFormulaParser extends TestCase {
assertEquals("15th Ptg is not the inner IF variable function ptg",FuncVarPtg.class,ptgs[14].getClass()); assertEquals("15th Ptg is not the inner IF variable function ptg",FuncVarPtg.class,ptgs[14].getClass());
} }
public void testMacroFunction() { public void testMacroFunction() {
Workbook w = Workbook.createWorkbook(); Workbook w = Workbook.createWorkbook();
FormulaParser fp = new FormulaParser("FOO()", w); FormulaParser fp = new FormulaParser("FOO()", w);
@ -291,7 +291,7 @@ public final class TestFormulaParser extends TestCase {
// the name gets encoded as the first arg // the name gets encoded as the first arg
NamePtg tname = (NamePtg) ptg[0]; NamePtg tname = (NamePtg) ptg[0];
assertEquals("FOO", tname.toFormulaString(w)); assertEquals("FOO", tname.toFormulaString(w));
AbstractFunctionPtg tfunc = (AbstractFunctionPtg) ptg[1]; AbstractFunctionPtg tfunc = (AbstractFunctionPtg) ptg[1];
assertTrue(tfunc.isExternalFunction()); assertTrue(tfunc.isExternalFunction());
} }
@ -302,9 +302,9 @@ public final class TestFormulaParser extends TestCase {
Ptg[] ptg = fp.getRPNPtg(); Ptg[] ptg = fp.getRPNPtg();
assertTrue("first ptg is string",ptg[0] instanceof StringPtg); assertTrue("first ptg is string",ptg[0] instanceof StringPtg);
assertTrue("second ptg is string",ptg[1] instanceof StringPtg); assertTrue("second ptg is string",ptg[1] instanceof StringPtg);
} }
public void testConcatenate(){ public void testConcatenate(){
FormulaParser fp = new FormulaParser("CONCATENATE(\"first\",\"second\")",null); FormulaParser fp = new FormulaParser("CONCATENATE(\"first\",\"second\")",null);
fp.parse(); fp.parse();
@ -312,7 +312,7 @@ public final class TestFormulaParser extends TestCase {
assertTrue("first ptg is string",ptg[0] instanceof StringPtg); assertTrue("first ptg is string",ptg[0] instanceof StringPtg);
assertTrue("second ptg is string",ptg[1] instanceof StringPtg); assertTrue("second ptg is string",ptg[1] instanceof StringPtg);
} }
public void testWorksheetReferences() public void testWorksheetReferences()
{ {
HSSFWorkbook wb = new HSSFWorkbook(); HSSFWorkbook wb = new HSSFWorkbook();
@ -330,7 +330,7 @@ public final class TestFormulaParser extends TestCase {
cell = row.createCell((short)1); cell = row.createCell((short)1);
cell.setCellFormula("'Quotes Needed Here &#$@'!A1"); cell.setCellFormula("'Quotes Needed Here &#$@'!A1");
} }
public void testUnaryMinus() public void testUnaryMinus()
{ {
FormulaParser fp = new FormulaParser("-A1", null); FormulaParser fp = new FormulaParser("-A1", null);
@ -340,7 +340,7 @@ public final class TestFormulaParser extends TestCase {
assertTrue("first ptg is reference",ptg[0] instanceof ReferencePtg); assertTrue("first ptg is reference",ptg[0] instanceof ReferencePtg);
assertTrue("second ptg is Minus",ptg[1] instanceof UnaryMinusPtg); assertTrue("second ptg is Minus",ptg[1] instanceof UnaryMinusPtg);
} }
public void testUnaryPlus() public void testUnaryPlus()
{ {
FormulaParser fp = new FormulaParser("+A1", null); FormulaParser fp = new FormulaParser("+A1", null);
@ -350,14 +350,14 @@ public final class TestFormulaParser extends TestCase {
assertTrue("first ptg is reference",ptg[0] instanceof ReferencePtg); assertTrue("first ptg is reference",ptg[0] instanceof ReferencePtg);
assertTrue("second ptg is Plus",ptg[1] instanceof UnaryPlusPtg); assertTrue("second ptg is Plus",ptg[1] instanceof UnaryPlusPtg);
} }
public void testLeadingSpaceInString() public void testLeadingSpaceInString()
{ {
String value = " hi "; String value = " hi ";
FormulaParser fp = new FormulaParser("\"" + value + "\"", null); FormulaParser fp = new FormulaParser("\"" + value + "\"", null);
fp.parse(); fp.parse();
Ptg[] ptg = fp.getRPNPtg(); Ptg[] ptg = fp.getRPNPtg();
assertTrue("got 1 ptg", ptg.length == 1); assertTrue("got 1 ptg", ptg.length == 1);
assertTrue("ptg0 is a StringPtg", ptg[0] instanceof StringPtg); assertTrue("ptg0 is a StringPtg", ptg[0] instanceof StringPtg);
assertTrue("ptg0 contains exact value", ((StringPtg)ptg[0]).getValue().equals(value)); assertTrue("ptg0 contains exact value", ((StringPtg)ptg[0]).getValue().equals(value));
@ -368,14 +368,14 @@ public final class TestFormulaParser extends TestCase {
FormulaParser fp = new FormulaParser("lookup(A1, A3:A52, B3:B52)", null); FormulaParser fp = new FormulaParser("lookup(A1, A3:A52, B3:B52)", null);
fp.parse(); fp.parse();
Ptg[] ptg = fp.getRPNPtg(); Ptg[] ptg = fp.getRPNPtg();
assertTrue("got 4 ptg", ptg.length == 4); assertTrue("got 4 ptg", ptg.length == 4);
assertTrue("ptg0 has Value class", ptg[0].getPtgClass() == Ptg.CLASS_VALUE); assertTrue("ptg0 has Value class", ptg[0].getPtgClass() == Ptg.CLASS_VALUE);
fp = new FormulaParser("match(A1, A3:A52)", null); fp = new FormulaParser("match(A1, A3:A52)", null);
fp.parse(); fp.parse();
ptg = fp.getRPNPtg(); ptg = fp.getRPNPtg();
assertTrue("got 3 ptg", ptg.length == 3); assertTrue("got 3 ptg", ptg.length == 3);
assertTrue("ptg0 has Value class", ptg[0].getPtgClass() == Ptg.CLASS_VALUE); assertTrue("ptg0 has Value class", ptg[0].getPtgClass() == Ptg.CLASS_VALUE);
} }
@ -521,77 +521,77 @@ public final class TestFormulaParser extends TestCase {
System.out.println("Testing org.apache.poi.hssf.record.formula.FormulaParser"); System.out.println("Testing org.apache.poi.hssf.record.formula.FormulaParser");
junit.textui.TestRunner.run(TestFormulaParser.class); junit.textui.TestRunner.run(TestFormulaParser.class);
} }
public void testNumbers() { public void testNumbers() {
HSSFWorkbook wb = new HSSFWorkbook(); HSSFWorkbook wb = new HSSFWorkbook();
wb.createSheet("Cash_Flow"); wb.createSheet("Cash_Flow");
HSSFSheet sheet = wb.createSheet("Test"); HSSFSheet sheet = wb.createSheet("Test");
HSSFRow row = sheet.createRow(0); HSSFRow row = sheet.createRow(0);
HSSFCell cell = row.createCell((short)0); HSSFCell cell = row.createCell((short)0);
String formula = null; String formula = null;
// starts from decimal point // starts from decimal point
cell.setCellFormula(".1"); cell.setCellFormula(".1");
formula = cell.getCellFormula(); formula = cell.getCellFormula();
assertEquals("0.1", formula); assertEquals("0.1", formula);
cell.setCellFormula("+.1"); cell.setCellFormula("+.1");
formula = cell.getCellFormula(); formula = cell.getCellFormula();
assertEquals("+0.1", formula); assertEquals("+0.1", formula);
cell.setCellFormula("-.1"); cell.setCellFormula("-.1");
formula = cell.getCellFormula(); formula = cell.getCellFormula();
assertEquals("-0.1", formula); assertEquals("-0.1", formula);
// has exponent // has exponent
cell.setCellFormula("10E1"); cell.setCellFormula("10E1");
formula = cell.getCellFormula(); formula = cell.getCellFormula();
assertEquals("100.0", formula); assertEquals("100.0", formula);
cell.setCellFormula("10E+1"); cell.setCellFormula("10E+1");
formula = cell.getCellFormula(); formula = cell.getCellFormula();
assertEquals("100.0", formula); assertEquals("100.0", formula);
cell.setCellFormula("10E-1"); cell.setCellFormula("10E-1");
formula = cell.getCellFormula(); formula = cell.getCellFormula();
assertEquals("1.0", formula); assertEquals("1.0", formula);
} }
public void testRanges() { public void testRanges() {
HSSFWorkbook wb = new HSSFWorkbook(); HSSFWorkbook wb = new HSSFWorkbook();
wb.createSheet("Cash_Flow"); wb.createSheet("Cash_Flow");
HSSFSheet sheet = wb.createSheet("Test"); HSSFSheet sheet = wb.createSheet("Test");
HSSFRow row = sheet.createRow(0); HSSFRow row = sheet.createRow(0);
HSSFCell cell = row.createCell((short)0); HSSFCell cell = row.createCell((short)0);
String formula = null; String formula = null;
cell.setCellFormula("A1.A2"); cell.setCellFormula("A1.A2");
formula = cell.getCellFormula(); formula = cell.getCellFormula();
assertEquals("A1:A2", formula); assertEquals("A1:A2", formula);
cell.setCellFormula("A1..A2"); cell.setCellFormula("A1..A2");
formula = cell.getCellFormula(); formula = cell.getCellFormula();
assertEquals("A1:A2", formula); assertEquals("A1:A2", formula);
cell.setCellFormula("A1...A2"); cell.setCellFormula("A1...A2");
formula = cell.getCellFormula(); formula = cell.getCellFormula();
assertEquals("A1:A2", formula); assertEquals("A1:A2", formula);
} }
/** /**
* Test for bug observable at svn revision 618865 (5-Feb-2008)<br/> * Test for bug observable at svn revision 618865 (5-Feb-2008)<br/>
* a formula consisting of a single no-arg function got rendered without the function braces * a formula consisting of a single no-arg function got rendered without the function braces
*/ */
public void testToFormulaStringZeroArgFunction() { public void testToFormulaStringZeroArgFunction() {
Workbook book = Workbook.createWorkbook(); // not really used in this test Workbook book = Workbook.createWorkbook(); // not really used in this test
Ptg[] ptgs = { Ptg[] ptgs = {
new FuncPtg(10, 0), new FuncPtg(10, 0),
}; };
@ -610,21 +610,21 @@ public final class TestFormulaParser extends TestCase {
assertEquals(2, ptgs.length); assertEquals(2, ptgs.length);
assertEquals(ptgs[0].getClass(), IntPtg.class); assertEquals(ptgs[0].getClass(), IntPtg.class);
assertEquals(ptgs[1].getClass(), PercentPtg.class); assertEquals(ptgs[1].getClass(), PercentPtg.class);
// double percent OK // double percent OK
ptgs = parseFormula("12345.678%%"); ptgs = parseFormula("12345.678%%");
assertEquals(3, ptgs.length); assertEquals(3, ptgs.length);
assertEquals(ptgs[0].getClass(), NumberPtg.class); assertEquals(ptgs[0].getClass(), NumberPtg.class);
assertEquals(ptgs[1].getClass(), PercentPtg.class); assertEquals(ptgs[1].getClass(), PercentPtg.class);
assertEquals(ptgs[2].getClass(), PercentPtg.class); assertEquals(ptgs[2].getClass(), PercentPtg.class);
// percent of a bracketed expression // percent of a bracketed expression
ptgs = parseFormula("(A1+35)%*B1%"); ptgs = parseFormula("(A1+35)%*B1%");
assertEquals(8, ptgs.length); assertEquals(8, ptgs.length);
assertEquals(ptgs[4].getClass(), PercentPtg.class); assertEquals(ptgs[4].getClass(), PercentPtg.class);
assertEquals(ptgs[6].getClass(), PercentPtg.class); assertEquals(ptgs[6].getClass(), PercentPtg.class);
// percent of a text quantity // percent of a text quantity
ptgs = parseFormula("\"8.75\"%"); ptgs = parseFormula("\"8.75\"%");
assertEquals(2, ptgs.length); assertEquals(2, ptgs.length);
@ -641,64 +641,64 @@ public final class TestFormulaParser extends TestCase {
// //
// things that parse OK but would *evaluate* to an error // things that parse OK but would *evaluate* to an error
ptgs = parseFormula("\"abc\"%"); ptgs = parseFormula("\"abc\"%");
assertEquals(2, ptgs.length); assertEquals(2, ptgs.length);
assertEquals(ptgs[0].getClass(), StringPtg.class); assertEquals(ptgs[0].getClass(), StringPtg.class);
assertEquals(ptgs[1].getClass(), PercentPtg.class); assertEquals(ptgs[1].getClass(), PercentPtg.class);
ptgs = parseFormula("#N/A%"); ptgs = parseFormula("#N/A%");
assertEquals(2, ptgs.length); assertEquals(2, ptgs.length);
assertEquals(ptgs[0].getClass(), ErrPtg.class); assertEquals(ptgs[0].getClass(), ErrPtg.class);
assertEquals(ptgs[1].getClass(), PercentPtg.class); assertEquals(ptgs[1].getClass(), PercentPtg.class);
} }
/** /**
* Tests combinations of various operators in the absence of brackets * Tests combinations of various operators in the absence of brackets
*/ */
public void testPrecedenceAndAssociativity() { public void testPrecedenceAndAssociativity() {
Class[] expClss; Class[] expClss;
// TRUE=TRUE=2=2 evaluates to FALSE // TRUE=TRUE=2=2 evaluates to FALSE
expClss = new Class[] { BoolPtg.class, BoolPtg.class, EqualPtg.class, expClss = new Class[] { BoolPtg.class, BoolPtg.class, EqualPtg.class,
IntPtg.class, EqualPtg.class, IntPtg.class, EqualPtg.class, }; IntPtg.class, EqualPtg.class, IntPtg.class, EqualPtg.class, };
confirmTokenClasses("TRUE=TRUE=2=2", expClss); confirmTokenClasses("TRUE=TRUE=2=2", expClss);
// 2^3^2 evaluates to 64 not 512 // 2^3^2 evaluates to 64 not 512
expClss = new Class[] { IntPtg.class, IntPtg.class, PowerPtg.class, expClss = new Class[] { IntPtg.class, IntPtg.class, PowerPtg.class,
IntPtg.class, PowerPtg.class, }; IntPtg.class, PowerPtg.class, };
confirmTokenClasses("2^3^2", expClss); confirmTokenClasses("2^3^2", expClss);
// "abc" & 2 + 3 & "def" evaluates to "abc5def" // "abc" & 2 + 3 & "def" evaluates to "abc5def"
expClss = new Class[] { StringPtg.class, IntPtg.class, IntPtg.class, expClss = new Class[] { StringPtg.class, IntPtg.class, IntPtg.class,
AddPtg.class, ConcatPtg.class, StringPtg.class, ConcatPtg.class, }; AddPtg.class, ConcatPtg.class, StringPtg.class, ConcatPtg.class, };
confirmTokenClasses("\"abc\"&2+3&\"def\"", expClss); confirmTokenClasses("\"abc\"&2+3&\"def\"", expClss);
// (1 / 2) - (3 * 4) // (1 / 2) - (3 * 4)
expClss = new Class[] { IntPtg.class, IntPtg.class, DividePtg.class, expClss = new Class[] { IntPtg.class, IntPtg.class, DividePtg.class,
IntPtg.class, IntPtg.class, MultiplyPtg.class, SubtractPtg.class, }; IntPtg.class, IntPtg.class, MultiplyPtg.class, SubtractPtg.class, };
confirmTokenClasses("1/2-3*4", expClss); confirmTokenClasses("1/2-3*4", expClss);
// 2 * (2^2) // 2 * (2^2)
expClss = new Class[] { IntPtg.class, IntPtg.class, IntPtg.class, PowerPtg.class, MultiplyPtg.class, }; expClss = new Class[] { IntPtg.class, IntPtg.class, IntPtg.class, PowerPtg.class, MultiplyPtg.class, };
// NOT: (2 *2) ^ 2 -> int int multiply int power // NOT: (2 *2) ^ 2 -> int int multiply int power
confirmTokenClasses("2*2^2", expClss); confirmTokenClasses("2*2^2", expClss);
// 2^200% -> 2 not 1.6E58 // 2^200% -> 2 not 1.6E58
expClss = new Class[] { IntPtg.class, IntPtg.class, PercentPtg.class, PowerPtg.class, }; expClss = new Class[] { IntPtg.class, IntPtg.class, PercentPtg.class, PowerPtg.class, };
confirmTokenClasses("2^200%", expClss); confirmTokenClasses("2^200%", expClss);
} }
private static void confirmTokenClasses(String formula, Class[] expectedClasses) { private static void confirmTokenClasses(String formula, Class[] expectedClasses) {
Ptg[] ptgs = parseFormula(formula); Ptg[] ptgs = parseFormula(formula);
assertEquals(expectedClasses.length, ptgs.length); assertEquals(expectedClasses.length, ptgs.length);
for (int i = 0; i < expectedClasses.length; i++) { for (int i = 0; i < expectedClasses.length; i++) {
if(expectedClasses[i] != ptgs[i].getClass()) { if(expectedClasses[i] != ptgs[i].getClass()) {
fail("difference at token[" + i + "]: expected (" fail("difference at token[" + i + "]: expected ("
+ expectedClasses[i].getName() + ") but got (" + expectedClasses[i].getName() + ") but got ("
+ ptgs[i].getClass().getName() + ")"); + ptgs[i].getClass().getName() + ")");
} }
} }
@ -718,38 +718,38 @@ public final class TestFormulaParser extends TestCase {
public void testParseNumber() { public void testParseNumber() {
IntPtg ip; IntPtg ip;
// bug 33160 // bug 33160
ip = (IntPtg) parseSingleToken("40", IntPtg.class); ip = (IntPtg) parseSingleToken("40", IntPtg.class);
assertEquals(40, ip.getValue()); assertEquals(40, ip.getValue());
ip = (IntPtg) parseSingleToken("40000", IntPtg.class); ip = (IntPtg) parseSingleToken("40000", IntPtg.class);
assertEquals(40000, ip.getValue()); assertEquals(40000, ip.getValue());
// check the upper edge of the IntPtg range: // check the upper edge of the IntPtg range:
ip = (IntPtg) parseSingleToken("65535", IntPtg.class); ip = (IntPtg) parseSingleToken("65535", IntPtg.class);
assertEquals(65535, ip.getValue()); assertEquals(65535, ip.getValue());
NumberPtg np = (NumberPtg) parseSingleToken("65536", NumberPtg.class); NumberPtg np = (NumberPtg) parseSingleToken("65536", NumberPtg.class);
assertEquals(65536, np.getValue(), 0); assertEquals(65536, np.getValue(), 0);
np = (NumberPtg) parseSingleToken("65534.6", NumberPtg.class); np = (NumberPtg) parseSingleToken("65534.6", NumberPtg.class);
assertEquals(65534.6, np.getValue(), 0); assertEquals(65534.6, np.getValue(), 0);
} }
public void testMissingArgs() { public void testMissingArgs() {
Class[] expClss; Class[] expClss;
expClss = new Class[] { ReferencePtg.class, MissingArgPtg.class, ReferencePtg.class, expClss = new Class[] { ReferencePtg.class, MissingArgPtg.class, ReferencePtg.class,
FuncVarPtg.class, }; FuncVarPtg.class, };
confirmTokenClasses("if(A1, ,C1)", expClss); confirmTokenClasses("if(A1, ,C1)", expClss);
expClss = new Class[] { MissingArgPtg.class, AreaPtg.class, MissingArgPtg.class, expClss = new Class[] { MissingArgPtg.class, AreaPtg.class, MissingArgPtg.class,
FuncVarPtg.class, }; FuncVarPtg.class, };
confirmTokenClasses("counta( , A1:B2, )", expClss); confirmTokenClasses("counta( , A1:B2, )", expClss);
} }
public void testParseErrorLiterals() { public void testParseErrorLiterals() {
confirmParseErrorLiteral(ErrPtg.NULL_INTERSECTION, "#NULL!"); confirmParseErrorLiteral(ErrPtg.NULL_INTERSECTION, "#NULL!");
confirmParseErrorLiteral(ErrPtg.DIV_ZERO, "#DIV/0!"); confirmParseErrorLiteral(ErrPtg.DIV_ZERO, "#DIV/0!");
confirmParseErrorLiteral(ErrPtg.VALUE_INVALID, "#VALUE!"); confirmParseErrorLiteral(ErrPtg.VALUE_INVALID, "#VALUE!");
@ -762,7 +762,7 @@ public final class TestFormulaParser extends TestCase {
private static void confirmParseErrorLiteral(ErrPtg expectedToken, String formula) { private static void confirmParseErrorLiteral(ErrPtg expectedToken, String formula) {
assertEquals(expectedToken, parseSingleToken(formula, ErrPtg.class)); assertEquals(expectedToken, parseSingleToken(formula, ErrPtg.class));
} }
/** /**
* To aid readability the parameters have been encoded with single quotes instead of double * To aid readability the parameters have been encoded with single quotes instead of double
* quotes. This method converts single quotes to double quotes before performing the parse * quotes. This method converts single quotes to double quotes before performing the parse
@ -772,23 +772,23 @@ public final class TestFormulaParser extends TestCase {
// formula: internal quotes become double double, surround with double quotes // formula: internal quotes become double double, surround with double quotes
String formula = '"' + singleQuotedValue.replaceAll("'", "\"\"") + '"'; String formula = '"' + singleQuotedValue.replaceAll("'", "\"\"") + '"';
String expectedValue = singleQuotedValue.replace('\'', '"'); String expectedValue = singleQuotedValue.replace('\'', '"');
StringPtg sp = (StringPtg) parseSingleToken(formula, StringPtg.class); StringPtg sp = (StringPtg) parseSingleToken(formula, StringPtg.class);
assertEquals(expectedValue, sp.getValue()); assertEquals(expectedValue, sp.getValue());
} }
public void testPaseStringLiterals() { public void testPaseStringLiterals() {
confirmStringParse("goto considered harmful"); confirmStringParse("goto considered harmful");
confirmStringParse("goto 'considered' harmful"); confirmStringParse("goto 'considered' harmful");
confirmStringParse(""); confirmStringParse("");
confirmStringParse("'"); confirmStringParse("'");
confirmStringParse("''"); confirmStringParse("''");
confirmStringParse("' '"); confirmStringParse("' '");
confirmStringParse(" ' "); confirmStringParse(" ' ");
} }
public void testParseSumIfSum() { public void testParseSumIfSum() {
String formulaString; String formulaString;
Ptg[] ptgs; Ptg[] ptgs;
@ -809,14 +809,14 @@ public final class TestFormulaParser extends TestCase {
parseExpectedException("1 + #N / A * 2"); parseExpectedException("1 + #N / A * 2");
parseExpectedException("#value?"); parseExpectedException("#value?");
parseExpectedException("#DIV/ 0+2"); parseExpectedException("#DIV/ 0+2");
if (false) { // TODO - add functionality to detect func arg count mismatch if (false) { // TODO - add functionality to detect func arg count mismatch
parseExpectedException("IF(TRUE)"); parseExpectedException("IF(TRUE)");
parseExpectedException("countif(A1:B5, C1, D1)"); parseExpectedException("countif(A1:B5, C1, D1)");
} }
} }
private static void parseExpectedException(String formula) { private static void parseExpectedException(String formula) {
try { try {
parseFormula(formula); parseFormula(formula);
@ -831,11 +831,11 @@ public final class TestFormulaParser extends TestCase {
} }
public void testSetFormulaWithRowBeyond32768_Bug44539() { public void testSetFormulaWithRowBeyond32768_Bug44539() {
HSSFWorkbook wb = new HSSFWorkbook(); HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet(); HSSFSheet sheet = wb.createSheet();
wb.setSheetName(0, "Sheet1"); wb.setSheetName(0, "Sheet1");
HSSFRow row = sheet.createRow(0); HSSFRow row = sheet.createRow(0);
HSSFCell cell = row.createCell((short)0); HSSFCell cell = row.createCell((short)0);
cell.setCellFormula("SUM(A32769:A32770)"); cell.setCellFormula("SUM(A32769:A32770)");
@ -862,11 +862,11 @@ public final class TestFormulaParser extends TestCase {
throw e; throw e;
} }
// FormulaParser strips spaces anyway // FormulaParser strips spaces anyway
assertEquals("4", formulaString); assertEquals("4", formulaString);
ptgs = new Ptg[] { new IntPtg(3), spacePtg, new IntPtg(4), spacePtg, new AddPtg()}; ptgs = new Ptg[] { new IntPtg(3), spacePtg, new IntPtg(4), spacePtg, new AddPtg()};
formulaString = FormulaParser.toFormulaString(null, ptgs); formulaString = FormulaParser.toFormulaString(null, ptgs);
assertEquals("3+4", formulaString); assertEquals("3+4", formulaString);
} }
/** /**
@ -875,7 +875,7 @@ public final class TestFormulaParser extends TestCase {
public void testTooFewOperandArgs() { public void testTooFewOperandArgs() {
// Simulating badly encoded cell formula of "=/1" // Simulating badly encoded cell formula of "=/1"
// Not sure if Excel could ever produce this // Not sure if Excel could ever produce this
Ptg[] ptgs = { Ptg[] ptgs = {
// Excel would probably have put tMissArg here // Excel would probably have put tMissArg here
new IntPtg(1), new IntPtg(1),
new DividePtg(), new DividePtg(),

View File

@ -19,6 +19,7 @@ package org.apache.poi.hssf.record;
import org.apache.poi.hssf.record.aggregates.AllRecordAggregateTests; import org.apache.poi.hssf.record.aggregates.AllRecordAggregateTests;
import org.apache.poi.hssf.record.formula.AllFormulaTests; import org.apache.poi.hssf.record.formula.AllFormulaTests;
import org.apache.poi.hssf.record.formula.functions.AllIndividualFunctionEvaluationTests;
import junit.framework.Test; import junit.framework.Test;
import junit.framework.TestSuite; import junit.framework.TestSuite;
@ -35,7 +36,7 @@ public final class AllRecordTests {
result.addTest(AllFormulaTests.suite()); result.addTest(AllFormulaTests.suite());
result.addTest(AllRecordAggregateTests.suite()); result.addTest(AllRecordAggregateTests.suite());
result.addTestSuite(TestAreaFormatRecord.class); result.addTestSuite(TestAreaFormatRecord.class);
result.addTestSuite(TestAreaRecord.class); result.addTestSuite(TestAreaRecord.class);
result.addTestSuite(TestAxisLineFormatRecord.class); result.addTestSuite(TestAxisLineFormatRecord.class);

View File

@ -44,7 +44,7 @@ public final class TestExternalNameRecord extends TestCase {
} }
} }
} }
public void testBasicSize() { public void testBasicSize() {
ExternalNameRecord enr = createSimpleENR(); ExternalNameRecord enr = createSimpleENR();
if(enr.getRecordSize() == 13) { if(enr.getRecordSize() == 13) {

View File

@ -17,13 +17,15 @@
package org.apache.poi.hssf.record.formula; package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.formula.eval.AllFormulaEvalTests;
import org.apache.poi.hssf.record.formula.function.AllFormulaFunctionTests; import org.apache.poi.hssf.record.formula.function.AllFormulaFunctionTests;
import org.apache.poi.hssf.record.formula.functions.AllIndividualFunctionEvaluationTests;
import junit.framework.Test; import junit.framework.Test;
import junit.framework.TestSuite; import junit.framework.TestSuite;
/** /**
* Collects all tests for this package. * Collects all tests for <tt>org.apache.poi.hssf.record.formula</tt>.
* *
* @author Josh Micich * @author Josh Micich
*/ */
@ -31,6 +33,10 @@ public class AllFormulaTests {
public static Test suite() { public static Test suite() {
TestSuite result = new TestSuite(AllFormulaTests.class.getName()); TestSuite result = new TestSuite(AllFormulaTests.class.getName());
result.addTest(AllFormulaEvalTests.suite());
result.addTest(AllFormulaFunctionTests.suite());
result.addTest(AllIndividualFunctionEvaluationTests.suite());
result.addTestSuite(TestArea3DPtg.class); result.addTestSuite(TestArea3DPtg.class);
result.addTestSuite(TestAreaErrPtg.class); result.addTestSuite(TestAreaErrPtg.class);
result.addTestSuite(TestAreaPtg.class); result.addTestSuite(TestAreaPtg.class);

View File

@ -21,7 +21,7 @@ import junit.framework.Test;
import junit.framework.TestSuite; import junit.framework.TestSuite;
/** /**
* Collects all tests for this package. * Collects all tests for this <tt>org.apache.poi.hssf.record.formula.function</tt>.
* *
* @author Josh Micich * @author Josh Micich
*/ */

View File

@ -49,14 +49,14 @@ import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory; import org.xml.sax.helpers.XMLReaderFactory;
/** /**
* This class is not used during normal POI run-time but is used at development time to generate * This class is not used during normal POI run-time but is used at development time to generate
* the file 'functionMetadata.txt'. There are more than 300 built-in functions in Excel and the * the file 'functionMetadata.txt'. There are more than 300 built-in functions in Excel and the
* intention of this class is to make it easier to maintain the metadata, by extracting it from * intention of this class is to make it easier to maintain the metadata, by extracting it from
* a reliable source. * a reliable source.
* *
* @author Josh Micich * @author Josh Micich
*/ */
public class ExcelFileFormatDocFunctionExtractor { public final class ExcelFileFormatDocFunctionExtractor {
private static final String SOURCE_DOC_FILE_NAME = "excelfileformat.odt"; private static final String SOURCE_DOC_FILE_NAME = "excelfileformat.odt";
@ -195,19 +195,19 @@ public class ExcelFileFormatDocFunctionExtractor {
"table:table-row", "table:table-cell", "text:p", "text:span", "text:note-ref", "table:table-row", "table:table-cell", "text:p", "text:span", "text:note-ref",
}; };
private final Stack _elemNameStack; private final Stack _elemNameStack;
/** <code>true</code> only when parsing the target tables */ /** <code>true</code> only when parsing the target tables */
private boolean _isInsideTable; private boolean _isInsideTable;
private final List _rowData; private final List _rowData;
private final StringBuffer _textNodeBuffer; private final StringBuffer _textNodeBuffer;
private final List _rowNoteFlags; private final List _rowNoteFlags;
private boolean _cellHasNote; private boolean _cellHasNote;
private final FunctionDataCollector _fdc; private final FunctionDataCollector _fdc;
private String _lastHeadingText; private String _lastHeadingText;
public EFFDocHandler(FunctionDataCollector fdc) { public EFFDocHandler(FunctionDataCollector fdc) {
_fdc = fdc; _fdc = fdc;
_elemNameStack = new Stack(); _elemNameStack = new Stack();
@ -216,7 +216,7 @@ public class ExcelFileFormatDocFunctionExtractor {
_textNodeBuffer = new StringBuffer(); _textNodeBuffer = new StringBuffer();
_rowNoteFlags = new ArrayList(); _rowNoteFlags = new ArrayList();
} }
private boolean matchesTargetPath() { private boolean matchesTargetPath() {
return matchesPath(0, TABLE_BASE_PATH_NAMES); return matchesPath(0, TABLE_BASE_PATH_NAMES);
} }
@ -365,7 +365,7 @@ public class ExcelFileFormatDocFunctionExtractor {
xr.setContentHandler(new EFFDocHandler(fdc)); xr.setContentHandler(new EFFDocHandler(fdc));
InputSource inSrc = new InputSource(is); InputSource inSrc = new InputSource(is);
try { try {
xr.parse(inSrc); xr.parse(inSrc);
is.close(); is.close();
@ -407,30 +407,30 @@ public class ExcelFileFormatDocFunctionExtractor {
} }
private static void outputLicenseHeader(PrintStream ps) { private static void outputLicenseHeader(PrintStream ps) {
String[] lines= { String[] lines= {
"Licensed to the Apache Software Foundation (ASF) under one or more", "Licensed to the Apache Software Foundation (ASF) under one or more",
"contributor license agreements. See the NOTICE file distributed with", "contributor license agreements. See the NOTICE file distributed with",
"this work for additional information regarding copyright ownership.", "this work for additional information regarding copyright ownership.",
"The ASF licenses this file to You under the Apache License, Version 2.0", "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 not use this file except in compliance with",
"the License. You may obtain a copy of the License at", "the License. You may obtain a copy of the License at",
"", "",
" http://www.apache.org/licenses/LICENSE-2.0", " http://www.apache.org/licenses/LICENSE-2.0",
"", "",
"Unless required by applicable law or agreed to in writing, software", "Unless required by applicable law or agreed to in writing, software",
"distributed under the License is distributed on an \"AS IS\" BASIS,", "distributed under the License is distributed on an \"AS IS\" BASIS,",
"WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.",
"See the License for the specific language governing permissions and", "See the License for the specific language governing permissions and",
"limitations under the License.", "limitations under the License.",
}; };
for (int i = 0; i < lines.length; i++) { for (int i = 0; i < lines.length; i++) {
ps.print("# "); ps.print("# ");
ps.println(lines[i]); ps.println(lines[i]);
} }
ps.println(); ps.println();
} }
/** /**
* Helps identify the source file * Helps identify the source file
*/ */
private static String getFileCRC(File f) { private static String getFileCRC(File f) {
@ -451,10 +451,10 @@ public class ExcelFileFormatDocFunctionExtractor {
} }
return "0x" + Long.toHexString(crc.getValue()).toUpperCase(); return "0x" + Long.toHexString(crc.getValue()).toUpperCase();
} }
private static File getSourceFile() { private static File getSourceFile() {
if (true) { if (false) {
File dir = new File("c:/josh/ref-docs"); File dir = new File("c:/temp");
File effDocFile = new File(dir, SOURCE_DOC_FILE_NAME); File effDocFile = new File(dir, SOURCE_DOC_FILE_NAME);
return effDocFile; return effDocFile;
} }
@ -464,7 +464,7 @@ public class ExcelFileFormatDocFunctionExtractor {
} catch (MalformedURLException e) { } catch (MalformedURLException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
File result; File result;
byte[]buf = new byte[2048]; byte[]buf = new byte[2048];
try { try {
@ -488,16 +488,15 @@ public class ExcelFileFormatDocFunctionExtractor {
System.out.println("file downloaded ok"); System.out.println("file downloaded ok");
return result; return result;
} }
public static void main(String[] args) { public static void main(String[] args) {
File effDocFile = getSourceFile(); File effDocFile = getSourceFile();
if(!effDocFile.exists()) { if(!effDocFile.exists()) {
throw new RuntimeException("file '" + effDocFile.getAbsolutePath() + "' does not exist"); throw new RuntimeException("file '" + effDocFile.getAbsolutePath() + "' does not exist");
} }
File outFile = new File("functionMetadata-asGenerated.txt"); File outFile = new File("functionMetadata-asGenerated.txt");
processFile(effDocFile, outFile); processFile(effDocFile, outFile);
} }
} }

View File

@ -18,26 +18,27 @@
package org.apache.poi.hssf.record.formula.function; package org.apache.poi.hssf.record.formula.function;
import junit.framework.TestCase; import junit.framework.TestCase;
/** /**
* *
* @author Josh Micich * @author Josh Micich
*/ */
public final class TestFunctionMetadataRegistry extends TestCase { public final class TestFunctionMetadataRegistry extends TestCase {
public void testWellKnownFunctions() { public void testWellKnownFunctions() {
confirmFunction(0, "COUNT"); confirmFunction(0, "COUNT");
confirmFunction(1, "IF"); confirmFunction(1, "IF");
}
private static void confirmFunction(int index, String funcName) { }
FunctionMetadata fm;
fm = FunctionMetadataRegistry.getFunctionByIndex(index); private static void confirmFunction(int index, String funcName) {
assertNotNull(fm); FunctionMetadata fm;
assertEquals(funcName, fm.getName()); fm = FunctionMetadataRegistry.getFunctionByIndex(index);
assertNotNull(fm);
fm = FunctionMetadataRegistry.getFunctionByName(funcName); assertEquals(funcName, fm.getName());
assertNotNull(fm);
assertEquals(index, fm.getIndex()); fm = FunctionMetadataRegistry.getFunctionByName(funcName);
} assertNotNull(fm);
assertEquals(index, fm.getIndex());
}
} }

View File

@ -44,8 +44,8 @@ public final class TestParseMissingBuiltInFuncs extends TestCase {
} }
AbstractFunctionPtg func = (AbstractFunctionPtg) ptgF; AbstractFunctionPtg func = (AbstractFunctionPtg) ptgF;
if(func.getFunctionIndex() == 255) { if(func.getFunctionIndex() == 255) {
throw new AssertionFailedError("Failed to recognise built-in function in formula '" throw new AssertionFailedError("Failed to recognise built-in function in formula '"
+ formula + "'"); + formula + "'");
} }
assertEquals(expPtgArraySize, ptgs.length); assertEquals(expPtgArraySize, ptgs.length);

View File

@ -48,7 +48,7 @@ public final class TestReadMissingBuiltInFuncs extends TestCase {
} }
sht = wb.getSheetAt(0); sht = wb.getSheetAt(0);
} }
public void testDatedif() { public void testDatedif() {
String formula; String formula;
@ -56,9 +56,9 @@ public final class TestReadMissingBuiltInFuncs extends TestCase {
formula = getCellFormula(0); formula = getCellFormula(0);
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
if(e.getMessage().startsWith("Too few arguments")) { if(e.getMessage().startsWith("Too few arguments")) {
if(e.getMessage().indexOf("AttrPtg") > 0) { if(e.getMessage().indexOf("AttrPtg") > 0) {
throw afe("tAttrVolatile not supported in FormulaParser.toFormulaString"); throw afe("tAttrVolatile not supported in FormulaParser.toFormulaString");
} }
throw afe("NOW() registered with 1 arg instead of 0"); throw afe("NOW() registered with 1 arg instead of 0");
} }
if(e.getMessage().startsWith("too much stuff")) { if(e.getMessage().startsWith("too much stuff")) {
@ -70,7 +70,7 @@ public final class TestReadMissingBuiltInFuncs extends TestCase {
assertEquals("DATEDIF(NOW(),NOW(),\"d\")", formula); assertEquals("DATEDIF(NOW(),NOW(),\"d\")", formula);
} }
public void testDdb() { public void testDdb() {
String formula = getCellFormula(1); String formula = getCellFormula(1);
if("externalflag(1,1,1,1,1)".equals(formula)) { if("externalflag(1,1,1,1,1)".equals(formula)) {
throw afe("DDB() not registered"); throw afe("DDB() not registered");
@ -78,14 +78,14 @@ public final class TestReadMissingBuiltInFuncs extends TestCase {
assertEquals("DDB(1,1,1,1,1)", formula); assertEquals("DDB(1,1,1,1,1)", formula);
} }
public void testAtan() { public void testAtan() {
String formula = getCellFormula(2); String formula = getCellFormula(2);
if(formula.equals("ARCTAN(1)")) { if(formula.equals("ARCTAN(1)")) {
throw afe("func ix 18 registered as ARCTAN() instead of ATAN()"); throw afe("func ix 18 registered as ARCTAN() instead of ATAN()");
} }
assertEquals("ATAN(1)", formula); assertEquals("ATAN(1)", formula);
} }
public void testUsdollar() { public void testUsdollar() {
String formula = getCellFormula(3); String formula = getCellFormula(3);
@ -128,7 +128,7 @@ public final class TestReadMissingBuiltInFuncs extends TestCase {
} }
assertEquals("ISNONTEXT(\"abc\")", formula); assertEquals("ISNONTEXT(\"abc\")", formula);
} }
private String getCellFormula(int rowIx) { private String getCellFormula(int rowIx) {
String result = sht.getRow(rowIx).getCell((short)0).getCellFormula(); String result = sht.getRow(rowIx).getCell((short)0).getCellFormula();
if (false) { if (false) {

View File

@ -27,9 +27,8 @@ import junit.framework.TestSuite;
*/ */
public final class AllIndividualFunctionEvaluationTests { public final class AllIndividualFunctionEvaluationTests {
// TODO - have this suite incorporated into a higher level one
public static Test suite() { public static Test suite() {
TestSuite result = new TestSuite("Tests for org.apache.poi.hssf.record.formula.functions"); TestSuite result = new TestSuite(AllIndividualFunctionEvaluationTests.class.getName());
result.addTestSuite(TestAverage.class); result.addTestSuite(TestAverage.class);
result.addTestSuite(TestCountFuncs.class); result.addTestSuite(TestCountFuncs.class);
result.addTestSuite(TestDate.class); result.addTestSuite(TestDate.class);

View File

@ -30,7 +30,7 @@ import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
* @author Josh Micich * @author Josh Micich
*/ */
public final class TestPmt extends TestCase { public final class TestPmt extends TestCase {
private static void confirm(double expected, NumberEval ne) { private static void confirm(double expected, NumberEval ne) {
// only asserting accuracy to 4 fractional digits // only asserting accuracy to 4 fractional digits
assertEquals(expected, ne.getNumberValue(), 0.00005); assertEquals(expected, ne.getNumberValue(), 0.00005);
@ -61,12 +61,12 @@ public final class TestPmt extends TestCase {
confirm(expected, invokeNormal(args)); confirm(expected, invokeNormal(args));
} }
public void testBasic() { public void testBasic() {
confirm(-1037.0321, (0.08/12), 10, 10000, 0, false); confirm(-1037.0321, (0.08/12), 10, 10000, 0, false);
confirm(-1030.1643, (0.08/12), 10, 10000, 0, true); confirm(-1030.1643, (0.08/12), 10, 10000, 0, true);
} }
public void test3args() { public void test3args() {
Eval[] args = { Eval[] args = {