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:
parent
861e2bae01
commit
00e64461a9
@ -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];
|
||||||
}
|
}
|
||||||
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user