Made AbstractFunctionPtg immutable, other minor improvements

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@836101 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Josh Micich 2009-11-14 02:41:24 +00:00
parent 89223f9500
commit b31ca01a5c
10 changed files with 106 additions and 134 deletions

View File

@ -37,45 +37,55 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
/** 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; private final byte returnClass;
protected byte[] paramClass; private final byte[] paramClass;
protected byte field_1_num_args; private final byte _numberOfArgs;
protected short field_2_fnc_index; private final short _functionIndex;
public final boolean isBaseToken() { protected AbstractFunctionPtg(int functionIndex, int pReturnClass, byte[] paramTypes, int nParams) {
return false; _numberOfArgs = (byte) nParams;
_functionIndex = (short) functionIndex;
returnClass = (byte) pReturnClass;
paramClass = paramTypes;
} }
public final boolean isBaseToken() {
public String toString() { return false;
StringBuffer sb = new StringBuffer(64); }
public final String toString() {
StringBuilder sb = new StringBuilder(64);
sb.append(getClass().getName()).append(" ["); sb.append(getClass().getName()).append(" [");
sb.append(field_2_fnc_index).append(" ").append(field_1_num_args); sb.append(lookupName(_functionIndex));
sb.append(" nArgs=").append(_numberOfArgs);
sb.append("]"); sb.append("]");
return sb.toString(); return sb.toString();
} }
public short getFunctionIndex() { public final short getFunctionIndex() {
return field_2_fnc_index; return _functionIndex;
}
public final int getNumberOfOperands() {
return _numberOfArgs;
} }
public String getName() { public final String getName() {
return lookupName(field_2_fnc_index); return lookupName(_functionIndex);
} }
/** /**
* external functions get some special processing * external functions get some special processing
* @return <code>true</code> if this is an external function * @return <code>true</code> if this is an external function
*/ */
public boolean isExternalFunction() { public final boolean isExternalFunction() {
return field_2_fnc_index == FUNCTION_INDEX_EXTERNAL; return _functionIndex == FUNCTION_INDEX_EXTERNAL;
} }
public String toFormulaString() { public final String toFormulaString() {
return getName(); return getName();
} }
public String toFormulaString(String[] operands) { public String toFormulaString(String[] operands) {
StringBuffer buf = new StringBuffer(); StringBuilder buf = new StringBuilder();
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
@ -87,7 +97,7 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
return buf.toString(); return buf.toString();
} }
private static void appendArgs(StringBuffer buf, int firstArgIx, String[] operands) { private static void appendArgs(StringBuilder buf, int firstArgIx, String[] operands) {
buf.append('('); buf.append('(');
for (int i=firstArgIx;i<operands.length;i++) { for (int i=firstArgIx;i<operands.length;i++) {
if (i>firstArgIx) { if (i>firstArgIx) {
@ -113,7 +123,7 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
return ix >= 0; return ix >= 0;
} }
protected String lookupName(short index) { protected final String lookupName(short index) {
if(index == FunctionMetadataRegistry.FUNCTION_INDEX_EXTERNAL) { if(index == FunctionMetadataRegistry.FUNCTION_INDEX_EXTERNAL) {
return "#external#"; return "#external#";
} }
@ -142,10 +152,10 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
return returnClass; return returnClass;
} }
public byte getParameterClass(int index) { public final byte getParameterClass(int index) {
if (index >= paramClass.length) { if (index >= paramClass.length) {
// For var-arg (and other?) functions, the metadata does not list all the parameter // For var-arg (and other?) functions, the metadata does not list all the parameter
// operand classes. In these cases, all extra parameters are assumed to have the // operand classes. In these cases, all extra parameters are assumed to have the
// same operand class as the last one specified. // same operand class as the last one specified.
return paramClass[paramClass.length - 1]; return paramClass[paramClass.length - 1];
} }

View File

@ -16,6 +16,7 @@
==================================================================== */ ==================================================================== */
package org.apache.poi.hssf.record.formula; package 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;
import org.apache.poi.util.LittleEndianInput; import org.apache.poi.util.LittleEndianInput;
@ -30,50 +31,30 @@ 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;
/**Creates new function pointer from a byte array public static FuncPtg create(LittleEndianInput in) {
* usually called while reading an excel file. return create(in.readUShort());
*/
public FuncPtg(LittleEndianInput 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 + ")");
}
numParams = fm.getMinParams();
returnClass = fm.getReturnClassCode();
paramClass = fm.getParameterClassCodes();
} }
public FuncPtg(int functionIndex) {
field_2_fnc_index = (short) functionIndex; private FuncPtg(int funcIndex, FunctionMetadata fm) {
super(funcIndex, fm.getReturnClassCode(), fm.getParameterClassCodes(), fm.getMinParams()); // minParams same as max since these are not var-arg funcs
}
public static FuncPtg create(int functionIndex) {
FunctionMetadata fm = FunctionMetadataRegistry.getFunctionByIndex(functionIndex); FunctionMetadata fm = FunctionMetadataRegistry.getFunctionByIndex(functionIndex);
numParams = fm.getMinParams(); // same as max since these are not var-arg funcs if(fm == null) {
returnClass = fm.getReturnClassCode(); throw new RuntimeException("Invalid built-in function index (" + functionIndex + ")");
paramClass = fm.getParameterClassCodes(); }
return new FuncPtg(functionIndex, fm);
} }
public void write(LittleEndianOutput out) { public void write(LittleEndianOutput out) {
out.writeByte(sid + getPtgClass()); out.writeByte(sid + getPtgClass());
out.writeShort(field_2_fnc_index); out.writeShort(getFunctionIndex());
}
public int getNumberOfOperands() {
return numParams;
} }
public int getSize() { public int getSize() {
return SIZE; return SIZE;
} }
}
public String toString() {
StringBuffer sb = new StringBuffer(64);
sb.append(getClass().getName()).append(" [");
sb.append(lookupName(field_2_fnc_index));
sb.append(" nArgs=").append(numParams);
sb.append("]");
return sb.toString();
}
}

View File

@ -22,7 +22,6 @@ import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.LittleEndianOutput; import org.apache.poi.util.LittleEndianOutput;
/** /**
*
* @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{
@ -30,60 +29,45 @@ 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;
/**
* Single instance of this token for 'sum() taking a single argument'
*/
public static final OperationPtg SUM = FuncVarPtg.create("SUM", 1);
private FuncVarPtg(int functionIndex, int returnClass, byte[] paramClasses, int numArgs) {
super(functionIndex, returnClass, paramClasses, numArgs);
}
/**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(LittleEndianInput in) { public static FuncVarPtg create(LittleEndianInput in) {
field_1_num_args = in.readByte(); return create(in.readByte(), in.readShort());
field_2_fnc_index = in.readShort();
FunctionMetadata fm = FunctionMetadataRegistry.getFunctionByIndex(field_2_fnc_index);
if(fm == null) {
// Happens only as a result of a call to FormulaParser.parse(), with a non-built-in function name
returnClass = Ptg.CLASS_VALUE;
paramClass = new byte[] {Ptg.CLASS_VALUE};
} else {
returnClass = fm.getReturnClassCode();
paramClass = fm.getParameterClassCodes();
}
} }
/** /**
* Create a function ptg from a string tokenised by the parser * Create a function ptg from a string tokenised by the parser
*/ */
public FuncVarPtg(String pName, byte pNumOperands) { public static FuncVarPtg create(String pName, int numArgs) {
field_1_num_args = pNumOperands; return create(numArgs, lookupIndex(pName));
field_2_fnc_index = lookupIndex(pName); }
FunctionMetadata fm = FunctionMetadataRegistry.getFunctionByIndex(field_2_fnc_index);
private static FuncVarPtg create(int numArgs, int functionIndex) {
FunctionMetadata fm = FunctionMetadataRegistry.getFunctionByIndex(functionIndex);
if(fm == null) { if(fm == null) {
// Happens only as a result of a call to FormulaParser.parse(), with a non-built-in function name // Happens only as a result of a call to FormulaParser.parse(), with a non-built-in function name
returnClass = Ptg.CLASS_VALUE; return new FuncVarPtg(functionIndex, Ptg.CLASS_VALUE, new byte[] {Ptg.CLASS_VALUE}, numArgs);
paramClass = new byte[] {Ptg.CLASS_VALUE};
} else {
returnClass = fm.getReturnClassCode();
paramClass = fm.getParameterClassCodes();
} }
return new FuncVarPtg(functionIndex, fm.getReturnClassCode(), fm.getParameterClassCodes(), numArgs);
} }
public void write(LittleEndianOutput out) { public void write(LittleEndianOutput out) {
out.writeByte(sid + getPtgClass()); out.writeByte(sid + getPtgClass());
out.writeByte(field_1_num_args); out.writeByte(getNumberOfOperands());
out.writeShort(field_2_fnc_index); out.writeShort(getFunctionIndex());
}
public int getNumberOfOperands() {
return field_1_num_args;
} }
public int getSize() { public int getSize() {
return SIZE; return SIZE;
} }
public String toString() {
StringBuffer sb = new StringBuffer(64);
sb.append(getClass().getName()).append(" [");
sb.append(lookupName(field_2_fnc_index));
sb.append(" nArgs=").append(field_1_num_args);
sb.append("]");
return sb.toString();
}
} }

View File

@ -102,8 +102,8 @@ public abstract class Ptg implements Cloneable {
switch (baseId) { switch (baseId) {
case ArrayPtg.sid: return new ArrayPtg(in); // 0x20, 0x40, 0x60 case ArrayPtg.sid: return new ArrayPtg(in); // 0x20, 0x40, 0x60
case FuncPtg.sid: return new FuncPtg(in); // 0x21, 0x41, 0x61 case FuncPtg.sid: return FuncPtg.create(in); // 0x21, 0x41, 0x61
case FuncVarPtg.sid: return new FuncVarPtg(in); // 0x22, 0x42, 0x62 case FuncVarPtg.sid: return FuncVarPtg.create(in);//0x22, 0x42, 0x62
case NamePtg.sid: return new NamePtg(in); // 0x23, 0x43, 0x63 case NamePtg.sid: return new NamePtg(in); // 0x23, 0x43, 0x63
case RefPtg.sid: return new RefPtg(in); // 0x24, 0x44, 0x64 case RefPtg.sid: return new RefPtg(in); // 0x24, 0x44, 0x64
case AreaPtg.sid: return new AreaPtg(in); // 0x25, 0x45, 0x65 case AreaPtg.sid: return new AreaPtg(in); // 0x25, 0x45, 0x65

View File

@ -970,7 +970,7 @@ public final class FormulaParser {
ParseNode[] allArgs = new ParseNode[numArgs+1]; ParseNode[] allArgs = new ParseNode[numArgs+1];
allArgs[0] = new ParseNode(namePtg); allArgs[0] = new ParseNode(namePtg);
System.arraycopy(args, 0, allArgs, 1, numArgs); System.arraycopy(args, 0, allArgs, 1, numArgs);
return new ParseNode(new FuncVarPtg(name, (byte)(numArgs+1)), allArgs); return new ParseNode(FuncVarPtg.create(name, numArgs+1), allArgs);
} }
if (namePtg != null) { if (namePtg != null) {
@ -988,9 +988,9 @@ public final class FormulaParser {
AbstractFunctionPtg retval; AbstractFunctionPtg retval;
if(isVarArgs) { if(isVarArgs) {
retval = new FuncVarPtg(name, (byte)numArgs); retval = FuncVarPtg.create(name, numArgs);
} else { } else {
retval = new FuncPtg(funcIx); retval = FuncPtg.create(funcIx);
} }
return new ParseNode(retval, args); return new ParseNode(retval, args);
} }
@ -1013,7 +1013,7 @@ public final class FormulaParser {
maxArgs = _book.getSpreadsheetVersion().getMaxFunctionArgs(); maxArgs = _book.getSpreadsheetVersion().getMaxFunctionArgs();
} else { } else {
//_book can be omitted by test cases //_book can be omitted by test cases
maxArgs = fm.getMaxParams(); // just use BIFF8 maxArgs = fm.getMaxParams(); // just use BIFF8
} }
} else { } else {
maxArgs = fm.getMaxParams(); maxArgs = fm.getMaxParams();

View File

@ -29,29 +29,29 @@ import org.apache.poi.hssf.record.formula.UnionPtg;
import org.apache.poi.hssf.record.formula.ValueOperatorPtg; import org.apache.poi.hssf.record.formula.ValueOperatorPtg;
/** /**
* This class performs 'operand class' transformation. Non-base tokens are classified into three * This class performs 'operand class' transformation. Non-base tokens are classified into three
* operand classes: * operand classes:
* <ul> * <ul>
* <li>reference</li> * <li>reference</li>
* <li>value</li> * <li>value</li>
* <li>array</li> * <li>array</li>
* </ul> * </ul>
* <p/> * <p/>
* *
* The final operand class chosen for each token depends on the formula type and the token's place * The final operand class chosen for each token depends on the formula type and the token's place
* in the formula. If POI gets the operand class wrong, Excel <em>may</em> interpret the formula * in the formula. If POI gets the operand class wrong, Excel <em>may</em> interpret the formula
* incorrectly. This condition is typically manifested as a formula cell that displays as '#VALUE!', * incorrectly. This condition is typically manifested as a formula cell that displays as '#VALUE!',
* but resolves correctly when the user presses F2, enter.<p/> * but resolves correctly when the user presses F2, enter.<p/>
* *
* The logic implemented here was partially inspired by the description in * The logic implemented here was partially inspired by the description in
* "OpenOffice.org's Documentation of the Microsoft Excel File Format". The model presented there * "OpenOffice.org's Documentation of the Microsoft Excel File Format". The model presented there
* seems to be inconsistent with observed Excel behaviour (These differences have not been fully * seems to be inconsistent with observed Excel behaviour (These differences have not been fully
* investigated). The implementation in this class has been heavily modified in order to satisfy * investigated). The implementation in this class has been heavily modified in order to satisfy
* concrete examples of how Excel performs the same logic (see TestRVA).<p/> * concrete examples of how Excel performs the same logic (see TestRVA).<p/>
* *
* Hopefully, as additional important test cases are identified and added to the test suite, * Hopefully, as additional important test cases are identified and added to the test suite,
* patterns might become more obvious in this code and allow for simplification. * patterns might become more obvious in this code and allow for simplification.
* *
* @author Josh Micich * @author Josh Micich
*/ */
final class OperandClassTransformer { final class OperandClassTransformer {
@ -77,15 +77,15 @@ final class OperandClassTransformer {
rootNodeOperandClass = Ptg.CLASS_REF; rootNodeOperandClass = Ptg.CLASS_REF;
break; break;
default: default:
throw new RuntimeException("Incomplete code - formula type (" throw new RuntimeException("Incomplete code - formula type ("
+ _formulaType + ") not supported yet"); + _formulaType + ") not supported yet");
} }
transformNode(rootNode, rootNodeOperandClass, false); transformNode(rootNode, rootNodeOperandClass, false);
} }
/** /**
* @param callerForceArrayFlag <code>true</code> if one of the current node's parents is a * @param callerForceArrayFlag <code>true</code> if one of the current node's parents is a
* function Ptg which has been changed from default 'V' to 'A' type (due to requirements on * function Ptg which has been changed from default 'V' to 'A' type (due to requirements on
* the function return value). * the function return value).
*/ */
@ -94,7 +94,7 @@ final class OperandClassTransformer {
Ptg token = node.getToken(); Ptg token = node.getToken();
ParseNode[] children = node.getChildren(); ParseNode[] children = node.getChildren();
boolean isSimpleValueFunc = isSimpleValueFunction(token); boolean isSimpleValueFunc = isSimpleValueFunction(token);
if (isSimpleValueFunc) { if (isSimpleValueFunc) {
boolean localForceArray = desiredOperandClass == Ptg.CLASS_ARRAY; boolean localForceArray = desiredOperandClass == Ptg.CLASS_ARRAY;
for (int i = 0; i < children.length; i++) { for (int i = 0; i < children.length; i++) {
@ -103,12 +103,12 @@ final class OperandClassTransformer {
setSimpleValueFuncClass((AbstractFunctionPtg) token, desiredOperandClass, callerForceArrayFlag); setSimpleValueFuncClass((AbstractFunctionPtg) token, desiredOperandClass, callerForceArrayFlag);
return; return;
} }
if (isSingleArgSum(token)) { if (isSingleArgSum(token)) {
// Need to process the argument of SUM with transformFunctionNode below // Need to process the argument of SUM with transformFunctionNode below
// so make a dummy FuncVarPtg for that call. // so make a dummy FuncVarPtg for that call.
token = new FuncVarPtg("SUM", (byte)1); token = FuncVarPtg.SUM;
// Note - the tAttrSum token (node.getToken()) is a base // Note - the tAttrSum token (node.getToken()) is a base
// token so does not need to have its operand class set // token so does not need to have its operand class set
} }
if (token instanceof ValueOperatorPtg || token instanceof ControlPtg if (token instanceof ValueOperatorPtg || token instanceof ControlPtg
@ -117,9 +117,9 @@ final class OperandClassTransformer {
|| token instanceof UnionPtg) { || token instanceof UnionPtg) {
// Value Operator Ptgs and Control are base tokens, so token will be unchanged // Value Operator Ptgs and Control are base tokens, so token will be unchanged
// but any child nodes are processed according to desiredOperandClass and callerForceArrayFlag // but any child nodes are processed according to desiredOperandClass and callerForceArrayFlag
// As per OOO documentation Sec 3.2.4 "Token Class Transformation", "Step 1" // As per OOO documentation Sec 3.2.4 "Token Class Transformation", "Step 1"
// All direct operands of value operators that are initially 'R' type will // All direct operands of value operators that are initially 'R' type will
// be converted to 'V' type. // be converted to 'V' type.
byte localDesiredOperandClass = desiredOperandClass == Ptg.CLASS_REF ? Ptg.CLASS_VALUE : desiredOperandClass; byte localDesiredOperandClass = desiredOperandClass == Ptg.CLASS_REF ? Ptg.CLASS_VALUE : desiredOperandClass;
for (int i = 0; i < children.length; i++) { for (int i = 0; i < children.length; i++) {
@ -149,7 +149,7 @@ final class OperandClassTransformer {
private static boolean isSingleArgSum(Ptg token) { private static boolean isSingleArgSum(Ptg token) {
if (token instanceof AttrPtg) { if (token instanceof AttrPtg) {
AttrPtg attrPtg = (AttrPtg) token; AttrPtg attrPtg = (AttrPtg) token;
return attrPtg.isSum(); return attrPtg.isSum();
} }
return false; return false;
} }
@ -180,12 +180,12 @@ final class OperandClassTransformer {
} }
// else fall through // else fall through
case Ptg.CLASS_ARRAY: case Ptg.CLASS_ARRAY:
return Ptg.CLASS_ARRAY; return Ptg.CLASS_ARRAY;
case Ptg.CLASS_REF: case Ptg.CLASS_REF:
if (!callerForceArrayFlag) { if (!callerForceArrayFlag) {
return currentOperandClass; return currentOperandClass;
} }
return Ptg.CLASS_REF; return Ptg.CLASS_REF;
} }
throw new IllegalStateException("Unexpected operand class (" + desiredOperandClass + ")"); throw new IllegalStateException("Unexpected operand class (" + desiredOperandClass + ")");
} }
@ -221,15 +221,15 @@ final class OperandClassTransformer {
} else { } else {
if (defaultReturnOperandClass == desiredOperandClass) { if (defaultReturnOperandClass == desiredOperandClass) {
localForceArrayFlag = false; localForceArrayFlag = false;
// an alternative would have been to for non-base Ptgs to set their operand class // an alternative would have been to for non-base Ptgs to set their operand class
// from their default, but this would require the call in many subclasses because // from their default, but this would require the call in many subclasses because
// the default OC is not known until the end of the constructor // the default OC is not known until the end of the constructor
afp.setClass(defaultReturnOperandClass); afp.setClass(defaultReturnOperandClass);
} else { } else {
switch (desiredOperandClass) { switch (desiredOperandClass) {
case Ptg.CLASS_VALUE: case Ptg.CLASS_VALUE:
// always OK to set functions to return 'value' // always OK to set functions to return 'value'
afp.setClass(Ptg.CLASS_VALUE); afp.setClass(Ptg.CLASS_VALUE);
localForceArrayFlag = false; localForceArrayFlag = false;
break; break;
case Ptg.CLASS_ARRAY: case Ptg.CLASS_ARRAY:
@ -282,7 +282,7 @@ final class OperandClassTransformer {
if (callerForceArrayFlag || desiredOperandClass == Ptg.CLASS_ARRAY) { if (callerForceArrayFlag || desiredOperandClass == Ptg.CLASS_ARRAY) {
afp.setClass(Ptg.CLASS_ARRAY); afp.setClass(Ptg.CLASS_ARRAY);
} else { } else {
afp.setClass(Ptg.CLASS_VALUE); afp.setClass(Ptg.CLASS_VALUE);
} }
} }
} }

View File

@ -344,8 +344,7 @@ public final class WorkbookEvaluator {
if (attrPtg.isSum()) { if (attrPtg.isSum()) {
// Excel prefers to encode 'SUM()' as a tAttr token, but this evaluator // Excel prefers to encode 'SUM()' as a tAttr token, but this evaluator
// expects the equivalent function token // expects the equivalent function token
byte nArgs = 1; // tAttrSum always has 1 parameter ptg = FuncVarPtg.SUM;
ptg = new FuncVarPtg("SUM", nArgs);
} }
if (attrPtg.isOptimizedChoose()) { if (attrPtg.isOptimizedChoose()) {
ValueEval arg0 = stack.pop(); ValueEval arg0 = stack.pop();

View File

@ -419,7 +419,7 @@ public final class TestFormulaParser extends TestCase {
HSSFWorkbook book = new HSSFWorkbook(); HSSFWorkbook book = new HSSFWorkbook();
Ptg[] ptgs = { Ptg[] ptgs = {
new FuncPtg(10), FuncPtg.create(10),
}; };
assertEquals("NA()", HSSFFormulaParser.toFormulaString(book, ptgs)); assertEquals("NA()", HSSFFormulaParser.toFormulaString(book, ptgs));
} }

View File

@ -34,15 +34,15 @@ public final class TestFuncPtg extends TestCase {
0, 0,
}; };
FuncPtg ptg = new FuncPtg(TestcaseRecordInputStream.createLittleEndian(fakeData) ); FuncPtg ptg = FuncPtg.create(TestcaseRecordInputStream.createLittleEndian(fakeData) );
assertEquals( "Len formula index is not 32(20H)", 0x20, ptg.getFunctionIndex() ); assertEquals( "Len formula index is not 32(20H)", 0x20, ptg.getFunctionIndex() );
assertEquals( "Number of operands in the len formula", 1, ptg.getNumberOfOperands() ); assertEquals( "Number of operands in the len formula", 1, ptg.getNumberOfOperands() );
assertEquals( "Function Name", "LEN", ptg.getName() ); assertEquals( "Function Name", "LEN", ptg.getName() );
assertEquals( "Ptg Size", 3, ptg.getSize() ); assertEquals( "Ptg Size", 3, ptg.getSize() );
} }
public void testClone() { public void testClone() {
FuncPtg funcPtg = new FuncPtg(27); // ROUND() - takes 2 args FuncPtg funcPtg = FuncPtg.create(27); // ROUND() - takes 2 args
FuncPtg clone = (FuncPtg) funcPtg.clone(); FuncPtg clone = (FuncPtg) funcPtg.clone();
if (clone.getNumberOfOperands() == 0) { if (clone.getNumberOfOperands() == 0) {
@ -52,5 +52,3 @@ public final class TestFuncPtg extends TestCase {
assertEquals("ROUND", clone.getName()); assertEquals("ROUND", clone.getName());
} }
} }

View File

@ -186,7 +186,7 @@ public final class TestRangeEval extends TestCase {
new RefPtg("C1"), new RefPtg("C1"),
new IntPtg(0), new IntPtg(0),
new RefPtg("B1"), new RefPtg("B1"),
new FuncVarPtg("OFFSET", (byte)3), FuncVarPtg.create("OFFSET", (byte)3),
RangePtg.instance, RangePtg.instance,
AttrPtg.SUM, AttrPtg.SUM,
}; };