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! -->
<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">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>

View File

@ -34,6 +34,7 @@
<!-- Don't forget to update changes.xml too! -->
<changes>
<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">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>

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.FunctionMetadataRegistry;
/**
* This class parses a formula string into a List of tokens in RPN order.
* Inspired by
@ -48,11 +46,11 @@ import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
* @author Pavel Krupets (pkrupets at palmtreebusiness dot com)
*/
public final class FormulaParser {
/**
* Specific exception thrown when a supplied formula does not parse properly.<br/>
* Primarily used by test cases when testing for specific parsing exceptions.</p>
*
*
*/
static final class FormulaParseException extends RuntimeException {
// 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_STD_DOCUMENT_NAME = 0x0008;
private static final int OPT_OLE_LINK = 0x0010;
// private static final int OPT_CLIP_FORMAT_MASK = 0x7FE0;
// private static final int OPT_CLIP_FORMAT_MASK = 0x7FE0;
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
* Therefore, this class will have ONLY two subclasses
* @author Avik Sengupta
* @author Andrew C. Oliver (acoliver at apache dot org)
*/
public abstract class AbstractFunctionPtg extends OperationPtg {
/**
* The name of the IF function (i.e. "IF"). Extracted as a constant for clarity.
*/
*/
public static final String FUNCTION_NAME_IF = "IF";
/** All external functions have function index 255 */
private static final short FUNCTION_INDEX_EXTERNAL = 255;
protected byte returnClass;
protected byte[] paramClass;
protected byte field_1_num_args;
protected short field_2_fnc_index;
public String toString() {
StringBuffer sb = new StringBuffer(64);
sb.append(getClass().getName()).append(" [");
@ -51,17 +51,17 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
sb.append("]");
return sb.toString();
}
public int getType() {
return -1;
}
}
public short getFunctionIndex() {
return field_2_fnc_index;
}
public String getName() {
return lookupName(field_2_fnc_index);
}
@ -72,14 +72,14 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
public boolean isExternalFunction() {
return field_2_fnc_index == FUNCTION_INDEX_EXTERNAL;
}
public String toFormulaString(Workbook book) {
return getName();
}
public String toFormulaString(String[] operands) {
StringBuffer buf = new StringBuffer();
StringBuffer buf = new StringBuffer();
if(isExternalFunction()) {
buf.append(operands[0]); // first operand is actually the function name
appendArgs(buf, 1, operands);
@ -100,23 +100,23 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
}
buf.append(")");
}
public abstract void writeBytes(byte[] array, int offset);
public abstract int getSize();
/**
* Used to detect whether a function name found in a formula is one of the standard excel functions
* <p>
* 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.
*/
public static final boolean isInternalFunctionName(String name) {
short ix = FunctionMetadataRegistry.lookupIndexByName(name.toUpperCase());
return ix >= 0;
}
protected String lookupName(short index) {
if(index == FunctionMetadataRegistry.FUNCTION_INDEX_EXTERNAL) {
return "#external#";
@ -127,7 +127,7 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
}
return fm.getName();
}
/**
* Resolves internal function names into function indexes.
* <p>
@ -145,7 +145,7 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
public byte getDefaultOperandClass() {
return returnClass;
}
public byte getParameterClass(int index) {
try {
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)
*/
public final class FuncPtg extends AbstractFunctionPtg {
public final static byte sid = 0x21;
public final static int SIZE = 3;
private int numParams=0;
/**
* 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.
@ -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"
*/
//protected byte[] leftOvers = null;
private FuncPtg() {
//Required for clone methods
//Required for clone methods
}
/**Creates new function pointer from a byte array
* usually called while reading an excel file.
/**Creates new function pointer from a byte array
* usually called while reading an excel file.
*/
public FuncPtg(RecordInputStream in) {
//field_1_num_args = data[ offset + 0 ];
field_2_fnc_index = in.readShort();
FunctionMetadata fm = FunctionMetadataRegistry.getFunctionByIndex(field_2_fnc_index);
if(fm == null) {
throw new RuntimeException("Invalid built-in function index (" + field_2_fnc_index + ")");
@ -62,12 +62,12 @@ public final class FuncPtg extends AbstractFunctionPtg {
numParams = numberOfParameters;
paramClass = new byte[] { Ptg.CLASS_VALUE, }; // TODO
}
public void writeBytes(byte[] array, int offset) {
array[offset+0]= (byte) (sid + ptgClass);
LittleEndian.putShort(array,offset+1,field_2_fnc_index);
}
public int getNumberOfOperands() {
return numParams;
}
@ -79,11 +79,11 @@ public final class FuncPtg extends AbstractFunctionPtg {
ptg.setClass(ptgClass);
return ptg;
}
public int getSize() {
return SIZE;
}
public String toString() {
StringBuffer sb = new StringBuffer(64);
sb.append(getClass().getName()).append(" [");

View File

@ -15,7 +15,6 @@
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record.formula;
import org.apache.poi.util.LittleEndian;
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)
*/
public final class FuncVarPtg extends AbstractFunctionPtg{
public final static byte sid = 0x22;
private final static int SIZE = 4;
private final static int SIZE = 4;
private FuncVarPtg() {
//Required for clone methods
}
/**Creates new function pointer from a byte array
* usually called while reading an excel file.
/**Creates new function pointer from a byte array
* usually called while reading an excel file.
*/
public FuncVarPtg(RecordInputStream in) {
field_1_num_args = in.readByte();
field_2_fnc_index = in.readShort();
}
/**
* 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};
}
}
public void writeBytes(byte[] array, int offset) {
array[offset+0]=(byte) (sid + ptgClass);
array[offset+1]=field_1_num_args;
LittleEndian.putShort(array,offset+2,field_2_fnc_index);
}
public int getNumberOfOperands() {
return field_1_num_args;
}
public Object clone() {
FuncVarPtg ptg = new FuncVarPtg();
ptg.field_1_num_args = field_1_num_args;
@ -77,11 +76,11 @@ public final class FuncVarPtg extends AbstractFunctionPtg{
ptg.setClass(ptgClass);
return ptg;
}
public int getSize() {
return SIZE;
}
public String toString() {
StringBuffer sb = new StringBuffer(64);
sb.append(getClass().getName()).append(" [");

View File

@ -23,7 +23,7 @@ import java.util.Map;
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>.
*
* @author Josh Micich

View File

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

View File

@ -46,7 +46,7 @@ final class FunctionMetadataReader {
public static FunctionMetadataRegistry createRegistry() {
InputStream is = FunctionMetadataReader.class.getResourceAsStream(METADATA_FILE_NAME);
if(is == null) {
if (is == null) {
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.Set;
/**
* Allows clients to get <tt>FunctionMetadata</tt> instances for any built-in function of Excel.
*
* @author Josh Micich
*/
public final class FunctionMetadataRegistry {
/**
* 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() {
if (_instance == null) {
_instance = FunctionMetadataReader.createRegistry();
// _instance = POIFunctionMetadataCreator.createInstance();
}
return _instance;
}

View File

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

View File

@ -14,7 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.model;
import junit.framework.AssertionFailedError;
@ -54,7 +54,7 @@ import org.apache.poi.hssf.usermodel.HSSFSheet;
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() .
* Some tests are also done in scratchpad, if they need
* HSSFFormulaEvaluator, which is there
@ -71,7 +71,7 @@ public final class TestFormulaParser extends TestCase {
assertNotNull("Ptg array should not be null", result);
return result;
}
public void testSimpleFormula() {
FormulaParser fp = new FormulaParser("2+2",null);
fp.parse();
@ -86,9 +86,9 @@ public final class TestFormulaParser extends TestCase {
assertTrue("",(ptgs[0] instanceof IntPtg));
assertTrue("",(ptgs[1] instanceof IntPtg));
assertTrue("",(ptgs[2] instanceof AddPtg));
}
public void testFormulaWithSpace2() {
Ptg[] ptgs;
FormulaParser fp;
@ -97,7 +97,7 @@ public final class TestFormulaParser extends TestCase {
ptgs = fp.getRPNPtg();
assertTrue("five tokens expected, got "+ptgs.length,ptgs.length == 5);
}
public void testFormulaWithSpaceNRef() {
Ptg[] ptgs;
FormulaParser fp;
@ -106,7 +106,7 @@ public final class TestFormulaParser extends TestCase {
ptgs = fp.getRPNPtg();
assertTrue("two tokens expected, got "+ptgs.length,ptgs.length == 2);
}
public void testFormulaWithString() {
Ptg[] ptgs;
FormulaParser fp;
@ -172,7 +172,7 @@ public final class TestFormulaParser extends TestCase {
}
/**
* 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);
}
/**
* 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>
@ -247,7 +247,7 @@ public final class TestFormulaParser extends TestCase {
}
public void testSimpleLogical() {
FormulaParser fp=new FormulaParser("IF(A1<A2,B1,B2)",null);
fp.parse();
@ -255,10 +255,10 @@ public final class TestFormulaParser extends TestCase {
assertTrue("Ptg array should not be null", ptgs !=null);
assertEquals("Ptg array length", 9, ptgs.length);
assertEquals("3rd Ptg is less than",LessThanPtg.class,ptgs[2].getClass());
}
public void testParenIf() {
FormulaParser fp=new FormulaParser("IF((A1+A2)<=3,\"yes\",\"no\")",null);
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());
}
public void testMacroFunction() {
Workbook w = Workbook.createWorkbook();
FormulaParser fp = new FormulaParser("FOO()", w);
@ -291,7 +291,7 @@ public final class TestFormulaParser extends TestCase {
// the name gets encoded as the first arg
NamePtg tname = (NamePtg) ptg[0];
assertEquals("FOO", tname.toFormulaString(w));
AbstractFunctionPtg tfunc = (AbstractFunctionPtg) ptg[1];
assertTrue(tfunc.isExternalFunction());
}
@ -302,9 +302,9 @@ public final class TestFormulaParser extends TestCase {
Ptg[] ptg = fp.getRPNPtg();
assertTrue("first ptg is string",ptg[0] instanceof StringPtg);
assertTrue("second ptg is string",ptg[1] instanceof StringPtg);
}
public void testConcatenate(){
FormulaParser fp = new FormulaParser("CONCATENATE(\"first\",\"second\")",null);
fp.parse();
@ -312,7 +312,7 @@ public final class TestFormulaParser extends TestCase {
assertTrue("first ptg is string",ptg[0] instanceof StringPtg);
assertTrue("second ptg is string",ptg[1] instanceof StringPtg);
}
public void testWorksheetReferences()
{
HSSFWorkbook wb = new HSSFWorkbook();
@ -330,7 +330,7 @@ public final class TestFormulaParser extends TestCase {
cell = row.createCell((short)1);
cell.setCellFormula("'Quotes Needed Here &#$@'!A1");
}
public void testUnaryMinus()
{
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("second ptg is Minus",ptg[1] instanceof UnaryMinusPtg);
}
public void testUnaryPlus()
{
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("second ptg is Plus",ptg[1] instanceof UnaryPlusPtg);
}
public void testLeadingSpaceInString()
{
String value = " hi ";
FormulaParser fp = new FormulaParser("\"" + value + "\"", null);
fp.parse();
Ptg[] ptg = fp.getRPNPtg();
assertTrue("got 1 ptg", ptg.length == 1);
assertTrue("ptg0 is a StringPtg", ptg[0] instanceof StringPtg);
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);
fp.parse();
Ptg[] ptg = fp.getRPNPtg();
assertTrue("got 4 ptg", ptg.length == 4);
assertTrue("ptg0 has Value class", ptg[0].getPtgClass() == Ptg.CLASS_VALUE);
fp = new FormulaParser("match(A1, A3:A52)", null);
fp.parse();
ptg = fp.getRPNPtg();
assertTrue("got 3 ptg", ptg.length == 3);
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");
junit.textui.TestRunner.run(TestFormulaParser.class);
}
public void testNumbers() {
HSSFWorkbook wb = new HSSFWorkbook();
wb.createSheet("Cash_Flow");
HSSFSheet sheet = wb.createSheet("Test");
HSSFRow row = sheet.createRow(0);
HSSFCell cell = row.createCell((short)0);
String formula = null;
// starts from decimal point
cell.setCellFormula(".1");
formula = cell.getCellFormula();
assertEquals("0.1", formula);
cell.setCellFormula("+.1");
formula = cell.getCellFormula();
assertEquals("+0.1", formula);
cell.setCellFormula("-.1");
formula = cell.getCellFormula();
assertEquals("-0.1", formula);
// has exponent
cell.setCellFormula("10E1");
formula = cell.getCellFormula();
assertEquals("100.0", formula);
cell.setCellFormula("10E+1");
formula = cell.getCellFormula();
assertEquals("100.0", formula);
cell.setCellFormula("10E-1");
formula = cell.getCellFormula();
assertEquals("1.0", formula);
}
public void testRanges() {
HSSFWorkbook wb = new HSSFWorkbook();
wb.createSheet("Cash_Flow");
HSSFSheet sheet = wb.createSheet("Test");
HSSFRow row = sheet.createRow(0);
HSSFCell cell = row.createCell((short)0);
String formula = null;
cell.setCellFormula("A1.A2");
formula = cell.getCellFormula();
assertEquals("A1:A2", formula);
cell.setCellFormula("A1..A2");
formula = cell.getCellFormula();
assertEquals("A1:A2", formula);
cell.setCellFormula("A1...A2");
formula = cell.getCellFormula();
assertEquals("A1:A2", formula);
}
/**
* 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
*/
public void testToFormulaStringZeroArgFunction() {
Workbook book = Workbook.createWorkbook(); // not really used in this test
Ptg[] ptgs = {
new FuncPtg(10, 0),
};
@ -610,21 +610,21 @@ public final class TestFormulaParser extends TestCase {
assertEquals(2, ptgs.length);
assertEquals(ptgs[0].getClass(), IntPtg.class);
assertEquals(ptgs[1].getClass(), PercentPtg.class);
// double percent OK
// double percent OK
ptgs = parseFormula("12345.678%%");
assertEquals(3, ptgs.length);
assertEquals(ptgs[0].getClass(), NumberPtg.class);
assertEquals(ptgs[1].getClass(), PercentPtg.class);
assertEquals(ptgs[2].getClass(), PercentPtg.class);
// percent of a bracketed expression
ptgs = parseFormula("(A1+35)%*B1%");
assertEquals(8, ptgs.length);
assertEquals(ptgs[4].getClass(), PercentPtg.class);
assertEquals(ptgs[6].getClass(), PercentPtg.class);
// percent of a text quantity
ptgs = parseFormula("\"8.75\"%");
assertEquals(2, ptgs.length);
@ -641,64 +641,64 @@ public final class TestFormulaParser extends TestCase {
//
// things that parse OK but would *evaluate* to an error
ptgs = parseFormula("\"abc\"%");
assertEquals(2, ptgs.length);
assertEquals(ptgs[0].getClass(), StringPtg.class);
assertEquals(ptgs[1].getClass(), PercentPtg.class);
ptgs = parseFormula("#N/A%");
assertEquals(2, ptgs.length);
assertEquals(ptgs[0].getClass(), ErrPtg.class);
assertEquals(ptgs[1].getClass(), PercentPtg.class);
}
/**
* Tests combinations of various operators in the absence of brackets
*/
public void testPrecedenceAndAssociativity() {
Class[] expClss;
// 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, };
confirmTokenClasses("TRUE=TRUE=2=2", expClss);
// 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, };
confirmTokenClasses("2^3^2", expClss);
// "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, };
confirmTokenClasses("\"abc\"&2+3&\"def\"", expClss);
// (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, };
confirmTokenClasses("1/2-3*4", expClss);
// 2 * (2^2)
expClss = new Class[] { IntPtg.class, IntPtg.class, IntPtg.class, PowerPtg.class, MultiplyPtg.class, };
// NOT: (2 *2) ^ 2 -> int int multiply int power
confirmTokenClasses("2*2^2", expClss);
// 2^200% -> 2 not 1.6E58
expClss = new Class[] { IntPtg.class, IntPtg.class, PercentPtg.class, PowerPtg.class, };
confirmTokenClasses("2^200%", expClss);
}
private static void confirmTokenClasses(String formula, Class[] expectedClasses) {
Ptg[] ptgs = parseFormula(formula);
assertEquals(expectedClasses.length, ptgs.length);
for (int i = 0; i < expectedClasses.length; i++) {
if(expectedClasses[i] != ptgs[i].getClass()) {
fail("difference at token[" + i + "]: expected ("
+ expectedClasses[i].getName() + ") but got ("
+ expectedClasses[i].getName() + ") but got ("
+ ptgs[i].getClass().getName() + ")");
}
}
@ -718,38 +718,38 @@ public final class TestFormulaParser extends TestCase {
public void testParseNumber() {
IntPtg ip;
// bug 33160
ip = (IntPtg) parseSingleToken("40", IntPtg.class);
assertEquals(40, ip.getValue());
ip = (IntPtg) parseSingleToken("40000", IntPtg.class);
assertEquals(40000, ip.getValue());
// check the upper edge of the IntPtg range:
ip = (IntPtg) parseSingleToken("65535", IntPtg.class);
assertEquals(65535, ip.getValue());
NumberPtg np = (NumberPtg) parseSingleToken("65536", NumberPtg.class);
assertEquals(65536, np.getValue(), 0);
np = (NumberPtg) parseSingleToken("65534.6", NumberPtg.class);
assertEquals(65534.6, np.getValue(), 0);
}
public void testMissingArgs() {
Class[] expClss;
expClss = new Class[] { ReferencePtg.class, MissingArgPtg.class, ReferencePtg.class,
expClss = new Class[] { ReferencePtg.class, MissingArgPtg.class, ReferencePtg.class,
FuncVarPtg.class, };
confirmTokenClasses("if(A1, ,C1)", expClss);
expClss = new Class[] { MissingArgPtg.class, AreaPtg.class, MissingArgPtg.class,
FuncVarPtg.class, };
confirmTokenClasses("counta( , A1:B2, )", expClss);
}
public void testParseErrorLiterals() {
confirmParseErrorLiteral(ErrPtg.NULL_INTERSECTION, "#NULL!");
confirmParseErrorLiteral(ErrPtg.DIV_ZERO, "#DIV/0!");
confirmParseErrorLiteral(ErrPtg.VALUE_INVALID, "#VALUE!");
@ -762,7 +762,7 @@ public final class TestFormulaParser extends TestCase {
private static void confirmParseErrorLiteral(ErrPtg expectedToken, String formula) {
assertEquals(expectedToken, parseSingleToken(formula, ErrPtg.class));
}
/**
* 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
@ -772,23 +772,23 @@ public final class TestFormulaParser extends TestCase {
// formula: internal quotes become double double, surround with double quotes
String formula = '"' + singleQuotedValue.replaceAll("'", "\"\"") + '"';
String expectedValue = singleQuotedValue.replace('\'', '"');
StringPtg sp = (StringPtg) parseSingleToken(formula, StringPtg.class);
assertEquals(expectedValue, sp.getValue());
}
public void testPaseStringLiterals() {
confirmStringParse("goto considered harmful");
confirmStringParse("goto 'considered' harmful");
confirmStringParse("");
confirmStringParse("'");
confirmStringParse("''");
confirmStringParse("' '");
confirmStringParse(" ' ");
}
public void testParseSumIfSum() {
String formulaString;
Ptg[] ptgs;
@ -809,14 +809,14 @@ public final class TestFormulaParser extends TestCase {
parseExpectedException("1 + #N / A * 2");
parseExpectedException("#value?");
parseExpectedException("#DIV/ 0+2");
if (false) { // TODO - add functionality to detect func arg count mismatch
parseExpectedException("IF(TRUE)");
parseExpectedException("countif(A1:B5, C1, D1)");
}
}
private static void parseExpectedException(String formula) {
try {
parseFormula(formula);
@ -831,11 +831,11 @@ public final class TestFormulaParser extends TestCase {
}
public void testSetFormulaWithRowBeyond32768_Bug44539() {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet();
wb.setSheetName(0, "Sheet1");
HSSFRow row = sheet.createRow(0);
HSSFCell cell = row.createCell((short)0);
cell.setCellFormula("SUM(A32769:A32770)");
@ -862,11 +862,11 @@ public final class TestFormulaParser extends TestCase {
throw e;
}
// FormulaParser strips spaces anyway
assertEquals("4", formulaString);
assertEquals("4", formulaString);
ptgs = new Ptg[] { new IntPtg(3), spacePtg, new IntPtg(4), spacePtg, new AddPtg()};
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() {
// Simulating badly encoded cell formula of "=/1"
// Not sure if Excel could ever produce this
Ptg[] ptgs = {
Ptg[] ptgs = {
// Excel would probably have put tMissArg here
new IntPtg(1),
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.formula.AllFormulaTests;
import org.apache.poi.hssf.record.formula.functions.AllIndividualFunctionEvaluationTests;
import junit.framework.Test;
import junit.framework.TestSuite;
@ -35,7 +36,7 @@ public final class AllRecordTests {
result.addTest(AllFormulaTests.suite());
result.addTest(AllRecordAggregateTests.suite());
result.addTestSuite(TestAreaFormatRecord.class);
result.addTestSuite(TestAreaRecord.class);
result.addTestSuite(TestAxisLineFormatRecord.class);

View File

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

View File

@ -17,13 +17,15 @@
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.functions.AllIndividualFunctionEvaluationTests;
import junit.framework.Test;
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
*/
@ -31,6 +33,10 @@ public class AllFormulaTests {
public static Test suite() {
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(TestAreaErrPtg.class);
result.addTestSuite(TestAreaPtg.class);

View File

@ -21,7 +21,7 @@ import junit.framework.Test;
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
*/

View File

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

View File

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

View File

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

View File

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

View File

@ -27,9 +27,8 @@ import junit.framework.TestSuite;
*/
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");
TestSuite result = new TestSuite(AllIndividualFunctionEvaluationTests.class.getName());
result.addTestSuite(TestAverage.class);
result.addTestSuite(TestCountFuncs.class);
result.addTestSuite(TestDate.class);

View File

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