public ValueEval evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) { int maxN = args.length; if (maxN < 1) { return ErrorEval.VALUE_INVALID; } ValueEval firstArg = args[0]; try { if (firstArg instanceof NumericValueEval) { return evaluateSingleProduct(args); } if (firstArg instanceof RefEval) { return evaluateSingleProduct(args); } if (firstArg instanceof TwoDEval) { TwoDEval ae = (TwoDEval) firstArg; if (ae.isRow() && ae.isColumn()) { return evaluateSingleProduct(args); } return evaluateAreaSumProduct(args); } } catch (EvaluationException e) { return e.getErrorEval(); } throw new RuntimeException( "Invalid arg type for SUMPRODUCT: (" + firstArg.getClass().getName() + ")"); }
/** * Determines a <code>double</code> value for the specified <code>ValueEval</code>. * * @param isScalarProduct <code>false</code> for SUMPRODUCTs over area refs. * @throws EvaluationException if <code>ve</code> represents an error value. * <p>Note - string values and empty cells are interpreted differently depending on <code> * isScalarProduct</code>. For scalar products, if any term is blank or a string, the error * (#VALUE!) is raised. For area (sum)products, if any term is blank or a string, the result * is zero. */ private static double getProductTerm(ValueEval ve, boolean isScalarProduct) throws EvaluationException { if (ve instanceof BlankEval || ve == null) { // TODO - shouldn't BlankEval.INSTANCE be used always instead of null? // null seems to occur when the blank cell is part of an area ref (but not reliably) if (isScalarProduct) { throw new EvaluationException(ErrorEval.VALUE_INVALID); } return 0; } if (ve instanceof ErrorEval) { throw new EvaluationException((ErrorEval) ve); } if (ve instanceof StringEval) { if (isScalarProduct) { throw new EvaluationException(ErrorEval.VALUE_INVALID); } // Note for area SUMPRODUCTs, string values are interpreted as zero // even if they would parse as valid numeric values return 0; } if (ve instanceof NumericValueEval) { NumericValueEval nve = (NumericValueEval) ve; return nve.getNumberValue(); } throw new RuntimeException("Unexpected value eval class (" + ve.getClass().getName() + ")"); }
/** @return never <code>null</code>, never {@link BlankEval} */ private ValueEval evaluateAny( EvaluationCell srcCell, int sheetIndex, int rowIndex, int columnIndex, EvaluationTracker tracker) { // avoid tracking dependencies for cells that have constant definition boolean shouldCellDependencyBeRecorded = _stabilityClassifier == null ? true : !_stabilityClassifier.isCellFinal(sheetIndex, rowIndex, columnIndex); if (srcCell == null || srcCell.getCellType() != Cell.CELL_TYPE_FORMULA) { ValueEval result = getValueFromNonFormulaCell(srcCell); if (shouldCellDependencyBeRecorded) { tracker.acceptPlainValueDependency(_workbookIx, sheetIndex, rowIndex, columnIndex, result); } return result; } FormulaCellCacheEntry cce = _cache.getOrCreateFormulaCellEntry(srcCell); if (shouldCellDependencyBeRecorded || cce.isInputSensitive()) { tracker.acceptFormulaDependency(cce); } IEvaluationListener evalListener = _evaluationListener; ValueEval result; if (cce.getValue() == null) { if (!tracker.startEvaluate(cce)) { return ErrorEval.CIRCULAR_REF_ERROR; } OperationEvaluationContext ec = new OperationEvaluationContext( this, _workbook, sheetIndex, rowIndex, columnIndex, tracker); try { Ptg[] ptgs = _workbook.getFormulaTokens(srcCell); if (evalListener == null) { result = evaluateFormula(ec, ptgs); } else { evalListener.onStartEvaluate(srcCell, cce, ptgs); result = evaluateFormula(ec, ptgs); evalListener.onEndEvaluate(cce, result); } tracker.updateCacheResult(result); } catch (NotImplementedException e) { throw addExceptionInfo(e, sheetIndex, rowIndex, columnIndex); } finally { tracker.endEvaluate(cce); } } else { if (evalListener != null) { evalListener.onCacheHit(sheetIndex, rowIndex, columnIndex, cce.getValue()); } return cce.getValue(); } if (isDebugLogEnabled()) { String sheetName = getSheetName(sheetIndex); CellReference cr = new CellReference(rowIndex, columnIndex); logDebug("Evaluated " + sheetName + "!" + cr.formatAsString() + " to " + result.toString()); } // Usually (result === cce.getValue()) // But sometimes: (result==ErrorEval.CIRCULAR_REF_ERROR, cce.getValue()==null) // When circular references are detected, the cache entry is only updated for // the top evaluation frame return result; }