diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index 9be4485ae..f5a8acfbd 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -37,6 +37,7 @@ + Various fixes: Recognising var-arg built-in functions #44675, ExternalNameRecord serialisation bug #44695, PMT() bug #44691 30311 - More work on Conditional Formatting Move the Formula Evaluator code out of scratchpad Move the missing record aware eventusermodel code out of scratchpad diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index f8712cc30..2d44e6e64 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,7 @@ + Various fixes: Recognising var-arg built-in functions #44675, ExternalNameRecord serialisation bug #44695, PMT() bug #44691 30311 - More work on Conditional Formatting Move the Formula Evaluator code out of scratchpad Move the missing record aware eventusermodel code out of scratchpad diff --git a/src/java/org/apache/poi/hssf/model/FormulaParser.java b/src/java/org/apache/poi/hssf/model/FormulaParser.java index e6eb29670..721928474 100644 --- a/src/java/org/apache/poi/hssf/model/FormulaParser.java +++ b/src/java/org/apache/poi/hssf/model/FormulaParser.java @@ -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.
* Primarily used by test cases when testing for specific parsing exceptions.

- * + * */ static final class FormulaParseException extends RuntimeException { // This class was given package scope until it would become clear that it is useful to diff --git a/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java b/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java index 99890f805..47be0497d 100755 --- a/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java +++ b/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java @@ -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; diff --git a/src/java/org/apache/poi/hssf/record/formula/AbstractFunctionPtg.java b/src/java/org/apache/poi/hssf/record/formula/AbstractFunctionPtg.java index 149ff2359..f0fc1fccf 100644 --- a/src/java/org/apache/poi/hssf/record/formula/AbstractFunctionPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/AbstractFunctionPtg.java @@ -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 *

* The name matching is case insensitive. - * @return true if the name specifies a standard worksheet function, + * @return true if the name specifies a standard worksheet function, * false if the name should be assumed to be an external function. */ public static final boolean isInternalFunctionName(String name) { 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. *

@@ -145,7 +145,7 @@ public abstract class AbstractFunctionPtg extends OperationPtg { public byte getDefaultOperandClass() { return returnClass; } - + public byte getParameterClass(int index) { try { return paramClass[index]; diff --git a/src/java/org/apache/poi/hssf/record/formula/FuncPtg.java b/src/java/org/apache/poi/hssf/record/formula/FuncPtg.java index a94355a0f..69edf88aa 100644 --- a/src/java/org/apache/poi/hssf/record/formula/FuncPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/FuncPtg.java @@ -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(" ["); diff --git a/src/java/org/apache/poi/hssf/record/formula/FuncVarPtg.java b/src/java/org/apache/poi/hssf/record/formula/FuncVarPtg.java index b4732c728..fb6527139 100644 --- a/src/java/org/apache/poi/hssf/record/formula/FuncVarPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/FuncVarPtg.java @@ -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(" ["); diff --git a/src/java/org/apache/poi/hssf/record/formula/function/FunctionDataBuilder.java b/src/java/org/apache/poi/hssf/record/formula/function/FunctionDataBuilder.java index b304ec3d4..2009ebae1 100644 --- a/src/java/org/apache/poi/hssf/record/formula/function/FunctionDataBuilder.java +++ b/src/java/org/apache/poi/hssf/record/formula/function/FunctionDataBuilder.java @@ -23,7 +23,7 @@ import java.util.Map; import java.util.Set; /** - * Temporarily collects FunctionMetadata instances for creation of a + * Temporarily collects FunctionMetadata instances for creation of a * FunctionMetadataRegistry. * * @author Josh Micich diff --git a/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadata.java b/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadata.java index 9df2db93c..94f1659b5 100644 --- a/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadata.java +++ b/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadata.java @@ -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); diff --git a/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataReader.java b/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataReader.java index bd50bf04d..bd72a9223 100644 --- a/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataReader.java +++ b/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataReader.java @@ -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"); } diff --git a/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataRegistry.java b/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataRegistry.java index 0cc8de37d..fe243bf01 100644 --- a/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataRegistry.java +++ b/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataRegistry.java @@ -19,7 +19,11 @@ package org.apache.poi.hssf.record.formula.function; import java.util.Map; import java.util.Set; - +/** + * Allows clients to get FunctionMetadata 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; } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Pmt.java b/src/java/org/apache/poi/hssf/record/formula/functions/Pmt.java index 58628053c..68a8d43dc 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Pmt.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Pmt.java @@ -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) { diff --git a/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java b/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java index 3b98aed0a..2589aa90d 100644 --- a/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java +++ b/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java @@ -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 #17582 @@ -247,7 +247,7 @@ public final class TestFormulaParser extends TestCase { } - + public void testSimpleLogical() { FormulaParser fp=new FormulaParser("IF(A1 * 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(), diff --git a/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java b/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java index 78954160f..32b16cf65 100755 --- a/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java +++ b/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java @@ -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); diff --git a/src/testcases/org/apache/poi/hssf/record/TestExternalNameRecord.java b/src/testcases/org/apache/poi/hssf/record/TestExternalNameRecord.java index 960627a39..3c35b29a2 100644 --- a/src/testcases/org/apache/poi/hssf/record/TestExternalNameRecord.java +++ b/src/testcases/org/apache/poi/hssf/record/TestExternalNameRecord.java @@ -44,7 +44,7 @@ public final class TestExternalNameRecord extends TestCase { } } } - + public void testBasicSize() { ExternalNameRecord enr = createSimpleENR(); if(enr.getRecordSize() == 13) { diff --git a/src/testcases/org/apache/poi/hssf/record/formula/AllFormulaTests.java b/src/testcases/org/apache/poi/hssf/record/formula/AllFormulaTests.java index b50e95fa8..645709bb6 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/AllFormulaTests.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/AllFormulaTests.java @@ -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 org.apache.poi.hssf.record.formula. * * @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); diff --git a/src/testcases/org/apache/poi/hssf/record/formula/function/AllFormulaFunctionTests.java b/src/testcases/org/apache/poi/hssf/record/formula/function/AllFormulaFunctionTests.java index 8a59095e4..01f88bd06 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/function/AllFormulaFunctionTests.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/function/AllFormulaFunctionTests.java @@ -21,7 +21,7 @@ import junit.framework.Test; import junit.framework.TestSuite; /** - * Collects all tests for this package. + * Collects all tests for this org.apache.poi.hssf.record.formula.function. * * @author Josh Micich */ diff --git a/src/testcases/org/apache/poi/hssf/record/formula/function/ExcelFileFormatDocFunctionExtractor.java b/src/testcases/org/apache/poi/hssf/record/formula/function/ExcelFileFormatDocFunctionExtractor.java index c7d74b6f7..48a76e31d 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/function/ExcelFileFormatDocFunctionExtractor.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/function/ExcelFileFormatDocFunctionExtractor.java @@ -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; /** true only when parsing the target tables */ private boolean _isInsideTable; - + private final List _rowData; private final StringBuffer _textNodeBuffer; private final List _rowNoteFlags; private boolean _cellHasNote; - + private final FunctionDataCollector _fdc; private String _lastHeadingText; - + public EFFDocHandler(FunctionDataCollector fdc) { _fdc = fdc; _elemNameStack = new Stack(); @@ -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); } - } diff --git a/src/testcases/org/apache/poi/hssf/record/formula/function/TestFunctionMetadataRegistry.java b/src/testcases/org/apache/poi/hssf/record/formula/function/TestFunctionMetadataRegistry.java index edb215ec5..c175c473b 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/function/TestFunctionMetadataRegistry.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/function/TestFunctionMetadataRegistry.java @@ -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()); + } } diff --git a/src/testcases/org/apache/poi/hssf/record/formula/function/TestParseMissingBuiltInFuncs.java b/src/testcases/org/apache/poi/hssf/record/formula/function/TestParseMissingBuiltInFuncs.java index fe1b8fccc..3671d37c1 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/function/TestParseMissingBuiltInFuncs.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/function/TestParseMissingBuiltInFuncs.java @@ -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); diff --git a/src/testcases/org/apache/poi/hssf/record/formula/function/TestReadMissingBuiltInFuncs.java b/src/testcases/org/apache/poi/hssf/record/formula/function/TestReadMissingBuiltInFuncs.java index 7e1d0317e..f1e6bcfac 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/function/TestReadMissingBuiltInFuncs.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/function/TestReadMissingBuiltInFuncs.java @@ -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) { diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java index 66d2a1d27..5973d7cb2 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java @@ -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); diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestPmt.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestPmt.java index 935615aca..2fecef704 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestPmt.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestPmt.java @@ -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 = {