Modified formula parser to encode SUM taking a single argument as tAttrSum (from comment 7 of bugzilla 46643)

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@740159 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Josh Micich 2009-02-02 23:53:22 +00:00
parent abdf3c9c46
commit 7df009dfd9
6 changed files with 63 additions and 62 deletions

View File

@ -79,8 +79,7 @@ public final class AttrPtg extends ControlPtg {
_chooseFuncOffset = -1;
}
public AttrPtg(LittleEndianInput in)
{
public AttrPtg(LittleEndianInput in) {
field_1_options = in.readByte();
field_2_data = in.readShort();
if (isOptimizedChoose()) {
@ -113,44 +112,30 @@ public final class AttrPtg extends ControlPtg {
return new AttrPtg(space.set(0), data, null, -1);
}
public void setOptions(byte options)
{
field_1_options = options;
public static AttrPtg getSumSingle() {
return new AttrPtg(sum.set(0), 0, null, -1);
}
public byte getOptions()
{
return field_1_options;
public boolean isSemiVolatile() {
return semiVolatile.isSet(field_1_options);
}
public boolean isSemiVolatile()
{
return semiVolatile.isSet(getOptions());
public boolean isOptimizedIf() {
return optiIf.isSet(field_1_options);
}
public boolean isOptimizedIf()
{
return optiIf.isSet(getOptions());
}
public boolean isOptimizedChoose()
{
return optiChoose.isSet(getOptions());
public boolean isOptimizedChoose() {
return optiChoose.isSet(field_1_options);
}
// lets hope no one uses this anymore
public boolean isGoto()
{
return optGoto.isSet(getOptions());
public boolean isGoto() {
return optGoto.isSet(field_1_options);
}
public boolean isSum()
{
return sum.isSet(getOptions());
}
public void setSum(boolean bsum) {
field_1_options=sum.setByteBoolean(field_1_options,bsum);
public boolean isSum() {
return sum.isSet(field_1_options);
}
public void setOptimizedIf(boolean bif) {
@ -166,24 +151,20 @@ public final class AttrPtg extends ControlPtg {
}
// lets hope no one uses this anymore
public boolean isBaxcel()
{
return baxcel.isSet(getOptions());
private boolean isBaxcel() {
return baxcel.isSet(field_1_options);
}
// biff3&4 only shouldn't happen anymore
public boolean isSpace()
{
return space.isSet(getOptions());
public boolean isSpace() {
return space.isSet(field_1_options);
}
public void setData(short data)
{
public void setData(short data) {
field_2_data = data;
}
public short getData()
{
public short getData() {
return field_2_data;
}
@ -227,8 +208,7 @@ public final class AttrPtg extends ControlPtg {
}
}
public int getSize()
{
public int getSize() {
if (_jumpTable != null) {
return SIZE + (_jumpTable.length + 1) * LittleEndian.SHORT_SIZE;
}
@ -239,22 +219,20 @@ public final class AttrPtg extends ControlPtg {
if(space.isSet(field_1_options)) {
return operands[ 0 ];
} else if (optiIf.isSet(field_1_options)) {
return toFormulaString() + "(" + operands[ 0 ] +")";
return toFormulaString() + "(" + operands[0] + ")";
} else if (optGoto.isSet(field_1_options)) {
return toFormulaString() + operands[0]; //goto isn't a real formula element should not show up
} else {
return toFormulaString() + "(" + operands[ 0 ] + ")";
return toFormulaString() + "(" + operands[0] + ")";
}
}
public int getNumberOfOperands()
{
public int getNumberOfOperands() {
return 1;
}
public int getType()
{
public int getType() {
return -1;
}
@ -288,7 +266,7 @@ public final class AttrPtg extends ControlPtg {
if (_jumpTable == null) {
jt = null;
} else {
jt = (int[]) _jumpTable.clone();
jt = _jumpTable.clone();
}
return new AttrPtg(field_1_options, field_2_data, jt, _chooseFuncOffset);
}

View File

@ -20,6 +20,7 @@ package org.apache.poi.hssf.record.formula.eval;
import java.util.HashMap;
import java.util.Map;
import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
import org.apache.poi.hssf.record.formula.functions.*;
/**
@ -31,12 +32,14 @@ public abstract class FunctionEval implements OperationEval {
* Some function IDs that require special treatment
*/
private static final class FunctionID {
/** 4 */
public static final int SUM = FunctionMetadataRegistry.FUNCTION_INDEX_SUM;
/** 78 */
public static final int OFFSET = 78;
/** 148 */
public static final int INDIRECT = 148;
/** 255 */
public static final int EXTERNAL_FUNC = 255;
public static final int EXTERNAL_FUNC = FunctionMetadataRegistry.FUNCTION_INDEX_EXTERNAL;
}
// convenient access to namespace
private static final FunctionID ID = null;
@ -75,7 +78,7 @@ public abstract class FunctionEval implements OperationEval {
retval[1] = new If(); // IF
retval[2] = new IsNa(); // ISNA
retval[3] = new IsError(); // ISERROR
retval[4] = AggregateFunction.SUM;
retval[ID.SUM] = AggregateFunction.SUM;
retval[5] = AggregateFunction.AVERAGE;
retval[6] = AggregateFunction.MIN;
retval[7] = AggregateFunction.MAX;

View File

@ -30,11 +30,12 @@ public final class FunctionMetadataRegistry {
*/
public static final String FUNCTION_NAME_IF = "IF";
public static final short FUNCTION_INDEX_SUM = 4;
public static final short FUNCTION_INDEX_EXTERNAL = 255;
private static FunctionMetadataRegistry _instance;
private final FunctionMetadata[] _functionDataByIndex;
private final Map _functionDataByName;
private final Map<String, FunctionMetadata> _functionDataByName;
private static FunctionMetadataRegistry getInstance() {
if (_instance == null) {
@ -43,12 +44,12 @@ public final class FunctionMetadataRegistry {
return _instance;
}
/* package */ FunctionMetadataRegistry(FunctionMetadata[] functionDataByIndex, Map functionDataByName) {
/* package */ FunctionMetadataRegistry(FunctionMetadata[] functionDataByIndex, Map<String, FunctionMetadata> functionDataByName) {
_functionDataByIndex = functionDataByIndex;
_functionDataByName = functionDataByName;
}
/* package */ Set getAllFunctionNames() {
/* package */ Set<String> getAllFunctionNames() {
return _functionDataByName.keySet();
}
@ -75,7 +76,7 @@ public final class FunctionMetadataRegistry {
}
private FunctionMetadata getFunctionByNameInternal(String name) {
return (FunctionMetadata) _functionDataByName.get(name);
return _functionDataByName.get(name);
}

View File

@ -27,6 +27,7 @@ import org.apache.poi.hssf.record.formula.AddPtg;
import org.apache.poi.hssf.record.formula.Area3DPtg;
import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.ArrayPtg;
import org.apache.poi.hssf.record.formula.AttrPtg;
import org.apache.poi.hssf.record.formula.BoolPtg;
import org.apache.poi.hssf.record.formula.ConcatPtg;
import org.apache.poi.hssf.record.formula.DividePtg;
@ -592,9 +593,11 @@ public final class FormulaParser {
}
boolean isVarArgs = !fm.hasFixedArgsLength();
int funcIx = fm.getIndex();
if (false && funcIx == 4 && args.length == 1) {
// TODO - make POI behave more like Excel when summing a single argument:
// return new ParseNode(AttrPtg.getSumSingle(), args);
if (funcIx == FunctionMetadataRegistry.FUNCTION_INDEX_SUM && args.length == 1) {
// Excel encodes the sum of a single argument as tAttrSum
// POI does the same for consistency, but this is not critical
return new ParseNode(AttrPtg.getSumSingle(), args);
// The code below would encode tFuncVar(SUM) which seems to do no harm
}
validateNumArgs(args.length, fm);

View File

@ -18,7 +18,9 @@
package org.apache.poi.ss.formula;
import org.apache.poi.hssf.record.formula.AbstractFunctionPtg;
import org.apache.poi.hssf.record.formula.AttrPtg;
import org.apache.poi.hssf.record.formula.ControlPtg;
import org.apache.poi.hssf.record.formula.FuncVarPtg;
import org.apache.poi.hssf.record.formula.MemFuncPtg;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.RangePtg;
@ -101,6 +103,13 @@ final class OperandClassTransformer {
return;
}
if (isSingleArgSum(token)) {
// Need to process the argument of SUM with transformFunctionNode below
// so make a dummy FuncVarPtg for that call.
token = new FuncVarPtg("SUM", (byte)1);
// Note - the tAttrSum token (node.getToken()) is a base
// token so does not need to have its operand class set
}
if (token instanceof ValueOperatorPtg || token instanceof ControlPtg
|| token instanceof MemFuncPtg
|| token instanceof UnionPtg) {
@ -135,6 +144,14 @@ final class OperandClassTransformer {
token.setClass(transformClass(token.getPtgClass(), desiredOperandClass, callerForceArrayFlag));
}
private static boolean isSingleArgSum(Ptg token) {
if (token instanceof AttrPtg) {
AttrPtg attrPtg = (AttrPtg) token;
return attrPtg.isSum();
}
return false;
}
private static boolean isSimpleValueFunction(Ptg token) {
if (token instanceof AbstractFunctionPtg) {
AbstractFunctionPtg aptg = (AbstractFunctionPtg) token;

View File

@ -20,7 +20,7 @@ package org.apache.poi.hssf.model;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.FuncVarPtg;
import org.apache.poi.hssf.record.formula.AttrPtg;
import org.apache.poi.hssf.record.formula.NamePtg;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.usermodel.HSSFCell;
@ -58,9 +58,8 @@ public final class TestFormulaParserEval extends TestCase {
confirmParseFormula(workbook);
// And make it non-contiguous
if (false) { // TODO (Nov 2008) - make the formula parser support area unions
name.setReference("A1:A2,C3");
}
// using area unions
name.setReference("A1:A2,C3");
confirmParseFormula(workbook);
}
@ -72,7 +71,7 @@ public final class TestFormulaParserEval extends TestCase {
Ptg[] ptgs = HSSFFormulaParser.parse("SUM(testName)", workbook);
assertTrue("two tokens expected, got "+ptgs.length,ptgs.length == 2);
assertEquals(NamePtg.class, ptgs[0].getClass());
assertEquals(FuncVarPtg.class, ptgs[1].getClass());
assertEquals(AttrPtg.class, ptgs[1].getClass());
}
public void testEvaluateFormulaWithRowBeyond32768_Bug44539() {