/** returns the OperationEval concrete impl instance corresponding to the supplied operationPtg */ public static ValueEval evaluate( OperationPtg ptg, ValueEval[] args, OperationEvaluationContext ec) { if (ptg == null) { throw new IllegalArgumentException("ptg must not be null"); } Function result = _instancesByPtgClass.get(ptg); if (result != null) { return result.evaluate(args, ec.getRowIndex(), (short) ec.getColumnIndex()); } if (ptg instanceof AbstractFunctionPtg) { AbstractFunctionPtg fptg = (AbstractFunctionPtg) ptg; int functionIndex = fptg.getFunctionIndex(); switch (functionIndex) { case FunctionMetadataRegistry.FUNCTION_INDEX_INDIRECT: return Indirect.instance.evaluate(args, ec); case FunctionMetadataRegistry.FUNCTION_INDEX_EXTERNAL: return UserDefinedFunction.instance.evaluate(args, ec); } return FunctionEval.getBasicFunction(functionIndex) .evaluate(args, ec.getRowIndex(), (short) ec.getColumnIndex()); } throw new RuntimeException("Unexpected operation ptg class (" + ptg.getClass().getName() + ")"); }
/** * returns an appropriate Eval impl instance for the Ptg. The Ptg must be one of: Area3DPtg, * AreaPtg, ReferencePtg, Ref3DPtg, IntPtg, NumberPtg, StringPtg, BoolPtg <br> * special Note: OperationPtg subtypes cannot be passed here! */ private ValueEval getEvalForPtg(Ptg ptg, OperationEvaluationContext ec) { // consider converting all these (ptg instanceof XxxPtg) expressions to (ptg.getClass() == // XxxPtg.class) if (ptg instanceof NamePtg) { // named ranges, macro functions NamePtg namePtg = (NamePtg) ptg; EvaluationName nameRecord = _workbook.getName(namePtg); if (nameRecord.isFunctionName()) { return new NameEval(nameRecord.getNameText()); } if (nameRecord.hasFormula()) { return evaluateNameFormula(nameRecord.getNameDefinition(), ec); } throw new RuntimeException( "Don't now how to evalate name '" + nameRecord.getNameText() + "'"); } if (ptg instanceof NameXPtg) { return new NameXEval(((NameXPtg) ptg)); } if (ptg instanceof IntPtg) { return new NumberEval(((IntPtg) ptg).getValue()); } if (ptg instanceof NumberPtg) { return new NumberEval(((NumberPtg) ptg).getValue()); } if (ptg instanceof StringPtg) { return new StringEval(((StringPtg) ptg).getValue()); } if (ptg instanceof BoolPtg) { return BoolEval.valueOf(((BoolPtg) ptg).getValue()); } if (ptg instanceof ErrPtg) { return ErrorEval.valueOf(((ErrPtg) ptg).getErrorCode()); } if (ptg instanceof MissingArgPtg) { return MissingArgEval.instance; } if (ptg instanceof AreaErrPtg || ptg instanceof RefErrorPtg || ptg instanceof DeletedArea3DPtg || ptg instanceof DeletedRef3DPtg) { return ErrorEval.REF_INVALID; } if (ptg instanceof Ref3DPtg) { Ref3DPtg refPtg = (Ref3DPtg) ptg; SheetRefEvaluator sre = ec.createExternSheetRefEvaluator(refPtg); return new LazyRefEval(refPtg, sre); } if (ptg instanceof Area3DPtg) { Area3DPtg aptg = (Area3DPtg) ptg; SheetRefEvaluator sre = ec.createExternSheetRefEvaluator(aptg); return new LazyAreaEval(aptg, sre); } SheetRefEvaluator sre = ec.getRefEvaluatorForCurrentSheet(); if (ptg instanceof RefPtg) { return new LazyRefEval(((RefPtg) ptg), sre); } if (ptg instanceof AreaPtg) { return new LazyAreaEval(((AreaPtg) ptg), sre); } if (ptg instanceof UnknownPtg) { // POI uses UnknownPtg when the encoded Ptg array seems to be corrupted. // This seems to occur in very rare cases (e.g. unused name formulas in bug 44774, attachment // 21790) // In any case, formulas are re-parsed before execution, so UnknownPtg should not get here throw new RuntimeException("UnknownPtg not allowed"); } if (ptg instanceof ExpPtg) { // ExpPtg is used for array formulas and shared formulas. // it is currently unsupported, and may not even get implemented here throw new RuntimeException("ExpPtg currently not supported"); } throw new RuntimeException("Unexpected ptg class (" + ptg.getClass().getName() + ")"); }
// visibility raised for testing /* package */ ValueEval evaluateFormula(OperationEvaluationContext ec, Ptg[] ptgs) { Stack<ValueEval> stack = new Stack<ValueEval>(); for (int i = 0, iSize = ptgs.length; i < iSize; i++) { // since we don't know how to handle these yet :( Ptg ptg = ptgs[i]; if (ptg instanceof AttrPtg) { AttrPtg attrPtg = (AttrPtg) ptg; if (attrPtg.isSum()) { // Excel prefers to encode 'SUM()' as a tAttr token, but this evaluator // expects the equivalent function token byte nArgs = 1; // tAttrSum always has 1 parameter ptg = new FuncVarPtg("SUM", nArgs); } } if (ptg instanceof ControlPtg) { // skip Parentheses, Attr, etc continue; } if (ptg instanceof MemFuncPtg) { // can ignore, rest of tokens for this expression are in OK RPN order continue; } if (ptg instanceof MemErrPtg) { continue; } ValueEval opResult; if (ptg instanceof OperationPtg) { OperationPtg optg = (OperationPtg) ptg; if (optg instanceof UnionPtg) { continue; } OperationEval operation = OperationEvaluatorFactory.create(optg); int numops = operation.getNumberOfOperands(); ValueEval[] ops = new ValueEval[numops]; // storing the ops in reverse order since they are popping for (int j = numops - 1; j >= 0; j--) { ValueEval p = stack.pop(); ops[j] = p; } // logDebug("invoke " + operation + " (nAgs=" + numops + ")"); opResult = operation.evaluate(ops, ec); if (opResult == MissingArgEval.instance) { opResult = BlankEval.INSTANCE; } } else { opResult = getEvalForPtg(ptg, ec); } if (opResult == null) { throw new RuntimeException("Evaluation result must not be null"); } // logDebug("push " + opResult); stack.push(opResult); } ValueEval value = stack.pop(); if (!stack.isEmpty()) { throw new IllegalStateException("evaluation stack not empty"); } value = dereferenceValue(value, ec.getRowIndex(), ec.getColumnIndex()); if (value == BlankEval.INSTANCE) { // Note Excel behaviour here. A blank final final value is converted to zero. return NumberEval.ZERO; // Formulas _never_ evaluate to blank. If a formula appears to have evaluated to // blank, the actual value is empty string. This can be verified with ISBLANK(). } return value; }