Removing calls to AreaEval.getValues()

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@690094 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Josh Micich 2008-08-29 03:52:51 +00:00
parent adff0508bb
commit adc8469ebc
13 changed files with 440 additions and 553 deletions

View File

@ -30,46 +30,46 @@ public interface AreaEval extends ValueEval {
* returns the 0-based index of the first row in
* this area.
*/
public int getFirstRow();
int getFirstRow();
/**
* returns the 0-based index of the last row in
* this area.
*/
public int getLastRow();
int getLastRow();
/**
* returns the 0-based index of the first col in
* this area.
*/
public int getFirstColumn();
int getFirstColumn();
/**
* returns the 0-based index of the last col in
* this area.
*/
public int getLastColumn();
int getLastColumn();
/**
* returns true if the Area's start and end row indexes
* are same. This result of this method should agree
* with getFirstRow() == getLastRow().
*/
public boolean isRow();
boolean isRow();
/**
* returns true if the Area's start and end col indexes
* are same. This result of this method should agree
* with getFirstColumn() == getLastColumn().
*/
public boolean isColumn();
boolean isColumn();
/**
* The array of values in this area. Although the area
* maybe 1D (ie. isRow() or isColumn() returns true) or 2D
* the returned array is 1D.
*/
public ValueEval[] getValues();
ValueEval[] getValues();
/**
* returns the ValueEval from the values array at the specified
@ -80,7 +80,7 @@ public interface AreaEval extends ValueEval {
* @param row
* @param col
*/
public ValueEval getValueAt(int row, int col);
ValueEval getValueAt(int row, int col);
/**
* returns true if the cell at row and col specified
@ -89,17 +89,21 @@ public interface AreaEval extends ValueEval {
* @param row
* @param col
*/
public boolean contains(int row, int col);
boolean contains(int row, int col);
/**
* returns true if the specified col is in range
* @param col
*/
public boolean containsColumn(short col);
boolean containsColumn(short col);
/**
* returns true if the specified row is in range
* @param row
*/
public boolean containsRow(int row);
boolean containsRow(int row);
int getWidth();
int getHeight();
ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex);
}

View File

@ -94,9 +94,7 @@ abstract class AreaEvalBase implements AreaEval {
throw new IllegalArgumentException("Specified column index (" + col
+ ") is outside the allowed range (" + _firstColumn + ".." + col + ")");
}
int index = rowOffsetIx * _nColumns + colOffsetIx;
return _values[index];
return getRelativeValue(rowOffsetIx, colOffsetIx);
}
public final boolean contains(int row, int col) {
@ -119,4 +117,16 @@ abstract class AreaEvalBase implements AreaEval {
public final boolean isRow() {
return _firstRow == _lastRow;
}
public int getHeight() {
return _lastRow-_firstRow+1;
}
public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) {
int index = relativeRowIndex * _nColumns + relativeColumnIndex;
return _values[index];
}
public int getWidth() {
return _lastColumn-_firstColumn+1;
}
}

View File

@ -149,7 +149,7 @@ public final class OperandResolver {
if (ae.isColumn()) {
if(ae.isRow()) {
return ae.getValues()[0];
return ae.getRelativeValue(0, 0);
}
if(!ae.containsRow(srcCellRow)) {
throw EvaluationException.invalidValue();
@ -274,4 +274,51 @@ public final class OperandResolver {
}
throw new IllegalArgumentException("Unexpected eval class (" + ve.getClass().getName() + ")");
}
/**
* @return <code>null</code> to represent blank values
* @throws EvaluationException if ve is an ErrorEval, or if a string value cannot be converted
*/
public static Boolean coerceValueToBoolean(ValueEval ve, boolean stringsAreBlanks) throws EvaluationException {
if (ve == null || ve instanceof BlankEval) {
// TODO - remove 've == null' condition once AreaEval is fixed
return null;
}
if (ve instanceof BoolEval) {
return Boolean.valueOf(((BoolEval) ve).getBooleanValue());
}
if (ve instanceof BlankEval) {
return null;
}
if (ve instanceof StringEval) {
if (stringsAreBlanks) {
return null;
}
String str = ((StringEval) ve).getStringValue();
if (str.equalsIgnoreCase("true")) {
return Boolean.TRUE;
}
if (str.equalsIgnoreCase("false")) {
return Boolean.FALSE;
}
// else - string cannot be converted to boolean
throw new EvaluationException(ErrorEval.VALUE_INVALID);
}
if (ve instanceof NumericValueEval) {
NumericValueEval ne = (NumericValueEval) ve;
double d = ne.getNumberValue();
if (Double.isNaN(d)) {
throw new EvaluationException(ErrorEval.VALUE_INVALID);
}
return Boolean.valueOf(d != 0);
}
if (ve instanceof ErrorEval) {
throw new EvaluationException((ErrorEval) ve);
}
throw new RuntimeException("Unexpected eval (" + ve.getClass().getName() + ")");
}
}

View File

@ -1,85 +1,32 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Created on May 9, 2005
*
*/
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.BoolEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.StringEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
public class And extends BooleanFunction {
public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
ValueEval retval = null;
boolean b = true;
boolean atleastOneNonBlank = false;
/*
* Note: do not abort the loop if b is false, since we could be
* dealing with errorevals later.
/**
*
*/
outer:
for (int i=0, iSize=operands.length; i<iSize; i++) {
if (operands[i] instanceof AreaEval) {
AreaEval ae = (AreaEval) operands[i];
ValueEval[] values = ae.getValues();
for (int j=0, jSize=values.length; j<jSize; j++) {
ValueEval tempVe = singleOperandEvaluate(values[j], srcRow, srcCol, true);
if (tempVe instanceof BoolEval) {
b = b && ((BoolEval) tempVe).getBooleanValue();
atleastOneNonBlank = true;
}
else if (tempVe instanceof ErrorEval) {
retval = tempVe;
break outer;
}
}
}
else {
ValueEval tempVe = singleOperandEvaluate(operands[i], srcRow, srcCol, false);
if (tempVe instanceof BoolEval) {
b = b && ((BoolEval) tempVe).getBooleanValue();
atleastOneNonBlank = true;
}
else if (tempVe instanceof StringEval) {
retval = ErrorEval.VALUE_INVALID;
}
else if (tempVe instanceof ErrorEval) {
retval = tempVe;
break outer;
}
}
public final class And extends BooleanFunction {
protected boolean getInitialResultValue() {
return true;
}
if (!atleastOneNonBlank) {
retval = ErrorEval.VALUE_INVALID;
protected boolean partialEvaluate(boolean cumulativeResult, boolean currentValue) {
return cumulativeResult && currentValue;
}
if (retval == null) { // if no error
retval = b ? BoolEval.TRUE : BoolEval.FALSE;
}
return retval;
}
}

View File

@ -1,100 +1,108 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Created on May 15, 2005
*
*/
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.eval.BlankEval;
import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.BoolEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
import org.apache.poi.hssf.record.formula.eval.RefEval;
import org.apache.poi.hssf.record.formula.eval.StringEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
/**
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
* Here are the general rules concerning Boolean functions:
* <ol>
* <li> Blanks are not either true or false
* <li> Strings are not either true or false (even strings "true"
* or "TRUE" or "0" etc.)
* <li> Numbers: 0 is false. Any other number is TRUE.
* <li> References are evaluated and above rules apply.
* <li> Areas: Individual cells in area are evaluated and checked to
* see if they are blanks, strings etc.
* <li> Blanks are ignored (not either true or false) </li>
* <li> Strings are ignored if part of an area ref or cell ref, otherwise they must be 'true' or 'false'</li>
* <li> Numbers: 0 is false. Any other number is TRUE </li>
* <li> Areas: *all* cells in area are evaluated according to the above rules</li>
* </ol>
*
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*/
public abstract class BooleanFunction implements Function {
protected ValueEval singleOperandEvaluate(Eval eval, int srcRow, short srcCol, boolean stringsAreBlanks) {
ValueEval retval;
if (eval instanceof RefEval) {
RefEval re = (RefEval) eval;
ValueEval ve = re.getInnerValueEval();
retval = internalResolve(ve, true);
public final Eval evaluate(Eval[] args, int srcRow, short srcCol) {
if (args.length < 1) {
return ErrorEval.VALUE_INVALID;
}
else {
retval = internalResolve(eval, stringsAreBlanks);
boolean boolResult;
try {
boolResult = calculate(args);
} catch (EvaluationException e) {
return e.getErrorEval();
}
return BoolEval.valueOf(boolResult);
}
return retval;
private boolean calculate(Eval[] args) throws EvaluationException {
boolean result = getInitialResultValue();
boolean atleastOneNonBlank = false;
/*
* Note: no short-circuit boolean loop exit because any ErrorEvals will override the result
*/
for (int i=0, iSize=args.length; i<iSize; i++) {
Eval arg = args[i];
if (arg instanceof AreaEval) {
AreaEval ae = (AreaEval) arg;
int height = ae.getHeight();
int width = ae.getWidth();
for (int rrIx=0; rrIx<height; rrIx++) {
for (int rcIx=0; rcIx<width; rcIx++) {
ValueEval ve = ae.getRelativeValue(rrIx, rcIx);
Boolean tempVe = OperandResolver.coerceValueToBoolean(ve, true);
if (tempVe != null) {
result = partialEvaluate(result, tempVe.booleanValue());
atleastOneNonBlank = true;
}
}
}
continue;
}
Boolean tempVe;
if (arg instanceof RefEval) {
ValueEval ve = ((RefEval) arg).getInnerValueEval();
tempVe = OperandResolver.coerceValueToBoolean(ve, true);
} else if (arg instanceof ValueEval) {
ValueEval ve = (ValueEval) arg;
tempVe = OperandResolver.coerceValueToBoolean(ve, false);
} else {
throw new RuntimeException("Unexpected eval (" + arg.getClass().getName() + ")");
}
private ValueEval internalResolve(Eval ve, boolean stringsAreBlanks) {
ValueEval retval = null;
// blankeval is returned as is
if (ve instanceof BlankEval) {
retval = BlankEval.INSTANCE;
if (tempVe != null) {
result = partialEvaluate(result, tempVe.booleanValue());
atleastOneNonBlank = true;
}
}
// stringeval
else if (ve instanceof StringEval) {
retval = stringsAreBlanks ? (ValueEval) BlankEval.INSTANCE : (StringEval) ve;
if (!atleastOneNonBlank) {
throw new EvaluationException(ErrorEval.VALUE_INVALID);
}
return result;
}
// bools are bools :)
else if (ve instanceof BoolEval) {
retval = (BoolEval) ve;
}
// convert numbers to bool
else if (ve instanceof NumericValueEval) {
NumericValueEval ne = (NumericValueEval) ve;
double d = ne.getNumberValue();
retval = Double.isNaN(d)
? (ValueEval) ErrorEval.VALUE_INVALID
: (d != 0)
? BoolEval.TRUE
: BoolEval.FALSE;
}
// since refevals
else {
retval = ErrorEval.VALUE_INVALID;
}
return retval;
}
protected abstract boolean getInitialResultValue();
protected abstract boolean partialEvaluate(boolean cumulativeResult, boolean currentValue);
}

View File

@ -1,33 +1,27 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Created on May 9, 2005
*
*/
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.BlankEval;
import org.apache.poi.hssf.record.formula.eval.BoolEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.RefEval;
import org.apache.poi.hssf.record.formula.eval.StringEval;
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
@ -37,87 +31,21 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
* (treated as a boolean). If the specified arg is a number,
* then it is true <=> 'number is non-zero'
*/
public class Not extends BooleanFunction {
public final class Not implements Function {
public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
ValueEval retval = null;
boolean b = true;
ValueEval tempVe = null;
switch (operands.length) {
default:
retval = ErrorEval.VALUE_INVALID;
break;
case 1:
if (operands[0] instanceof AreaEval) {
AreaEval ae = (AreaEval) operands[0];
if (ae.isRow() && ae.containsColumn(srcCol)) {
ValueEval ve = ae.getValueAt(ae.getFirstRow(), srcCol);
tempVe = singleOperandEvaluate(ve);
} else if (ae.isColumn() && ae.containsRow(srcRow)) {
ValueEval ve = ae.getValueAt(srcRow, ae.getFirstColumn());
tempVe = singleOperandEvaluate(ve);
} else {
retval = ErrorEval.VALUE_INVALID;
}
}
else {
tempVe = singleOperandEvaluate(operands[0]);
if (tempVe instanceof StringEval) {
retval = ErrorEval.VALUE_INVALID;
}
else if (tempVe instanceof ErrorEval) {
retval = tempVe;
}
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
if (args.length != 1) {
return ErrorEval.VALUE_INVALID;
}
boolean boolArgVal;
try {
ValueEval ve = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
Boolean b = OperandResolver.coerceValueToBoolean(ve, false);
boolArgVal = b == null ? false : b.booleanValue();
} catch (EvaluationException e) {
return e.getErrorEval();
}
if (retval == null) { // if no error
if (tempVe instanceof BoolEval) {
b = b && ((BoolEval) tempVe).getBooleanValue();
}
else if (tempVe instanceof StringEval) {
retval = ErrorEval.VALUE_INVALID;
}
else if (tempVe instanceof ErrorEval) {
retval = tempVe;
}
retval = b ? BoolEval.FALSE : BoolEval.TRUE;
}
return retval;
}
protected ValueEval singleOperandEvaluate(Eval ve) {
ValueEval retval = ErrorEval.VALUE_INVALID;
if (ve instanceof RefEval) {
RefEval re = (RefEval) ve;
retval = singleOperandEvaluate(re.getInnerValueEval());
}
else if (ve instanceof BoolEval) {
retval = (BoolEval) ve;
}
else if (ve instanceof NumberEval) {
NumberEval ne = (NumberEval) ve;
retval = ne.getNumberValue() != 0 ? BoolEval.TRUE : BoolEval.FALSE;
}
else if (ve instanceof StringEval) {
StringEval se = (StringEval) ve;
String str = se.getStringValue();
retval = str.equalsIgnoreCase("true")
? BoolEval.TRUE
: str.equalsIgnoreCase("false")
? BoolEval.FALSE
: (ValueEval) ErrorEval.VALUE_INVALID;
}
else if (ve instanceof BlankEval) {
retval = BoolEval.FALSE;
}
else {
retval = ErrorEval.VALUE_INVALID;
}
return retval;
return BoolEval.valueOf(!boolArgVal);
}
}

View File

@ -1,81 +1,32 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Created on May 9, 2005
*
*/
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.BoolEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
public class Or extends BooleanFunction {
public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
ValueEval retval = null;
boolean b = false;
boolean atleastOneNonBlank = false;
/*
* Note: do not abort the loop if b is true, since we could be
* dealing with errorevals later.
/**
*
*/
outer:
for (int i=0, iSize=operands.length; i<iSize; i++) {
if (operands[i] instanceof AreaEval) {
AreaEval ae = (AreaEval) operands[i];
ValueEval[] values = ae.getValues();
for (int j=0, jSize=values.length; j<jSize; j++) {
ValueEval tempVe = singleOperandEvaluate(values[j], srcRow, srcCol, true);
if (tempVe instanceof BoolEval) {
b = b || ((BoolEval) tempVe).getBooleanValue();
atleastOneNonBlank = true;
}
else if (tempVe instanceof ErrorEval) {
retval = tempVe;
break outer;
}
}
}
else {
ValueEval tempVe = singleOperandEvaluate(operands[i], srcRow, srcCol, false);
if (tempVe instanceof BoolEval) {
b = b || ((BoolEval) tempVe).getBooleanValue();
atleastOneNonBlank = true;
}
else if (tempVe instanceof ErrorEval) {
retval = tempVe;
break outer;
}
}
public final class Or extends BooleanFunction {
protected boolean getInitialResultValue() {
return false;
}
if (!atleastOneNonBlank) {
retval = ErrorEval.VALUE_INVALID;
protected boolean partialEvaluate(boolean cumulativeResult, boolean currentValue) {
return cumulativeResult || currentValue;
}
if (retval == null) { // if no error
retval = b ? BoolEval.TRUE : BoolEval.FALSE;
}
return retval;
}
}

View File

@ -22,6 +22,7 @@ import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.BlankEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
import org.apache.poi.hssf.record.formula.eval.RefEval;
@ -53,16 +54,6 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
*/
public final class Sumproduct implements Function {
private static final class EvalEx extends Exception {
private final ErrorEval _error;
public EvalEx(ErrorEval error) {
_error = error;
}
public ErrorEval getError() {
return _error;
}
}
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
@ -86,14 +77,14 @@ public final class Sumproduct implements Function {
}
return evaluateAreaSumProduct(args);
}
} catch (EvalEx e) {
return e.getError();
} catch (EvaluationException e) {
return e.getErrorEval();
}
throw new RuntimeException("Invalid arg type for SUMPRODUCT: ("
+ firstArg.getClass().getName() + ")");
}
private Eval evaluateSingleProduct(Eval[] evalArgs) throws EvalEx {
private static Eval evaluateSingleProduct(Eval[] evalArgs) throws EvaluationException {
int maxN = evalArgs.length;
double term = 1D;
@ -104,7 +95,7 @@ public final class Sumproduct implements Function {
return new NumberEval(term);
}
private double getScalarValue(Eval arg) throws EvalEx {
private static double getScalarValue(Eval arg) throws EvaluationException {
Eval eval;
if (arg instanceof RefEval) {
@ -121,9 +112,9 @@ public final class Sumproduct implements Function {
AreaEval ae = (AreaEval) eval;
// an area ref can work as a scalar value if it is 1x1
if(!ae.isColumn() || !ae.isRow()) {
throw new EvalEx(ErrorEval.VALUE_INVALID);
throw new EvaluationException(ErrorEval.VALUE_INVALID);
}
eval = ae.getValues()[0];
eval = ae.getRelativeValue(0, 0);
}
if (!(eval instanceof ValueEval)) {
@ -134,7 +125,7 @@ public final class Sumproduct implements Function {
return getProductTerm((ValueEval) eval, true);
}
private Eval evaluateAreaSumProduct(Eval[] evalArgs) throws EvalEx {
private static Eval evaluateAreaSumProduct(Eval[] evalArgs) throws EvaluationException {
int maxN = evalArgs.length;
AreaEval[] args = new AreaEval[maxN];
try {
@ -147,23 +138,27 @@ public final class Sumproduct implements Function {
AreaEval firstArg = args[0];
int height = firstArg.getLastRow() - firstArg.getFirstRow() + 1;
int width = firstArg.getLastColumn() - firstArg.getFirstColumn() + 1; // TODO - junit
int height = firstArg.getHeight();
int width = firstArg.getWidth(); // TODO - junit
double[][][] elements = new double[maxN][][];
for (int n = 0; n < maxN; n++) {
elements[n] = evaluateArea(args[n], height, width);
// first check dimensions
if (!areasAllSameSize(args, height, width)) {
// normally this results in #VALUE!,
// but errors in individual cells take precedence
for (int i = 1; i < args.length; i++) {
throwFirstError(args[i]);
}
return ErrorEval.VALUE_INVALID;
}
double acc = 0;
for(int r=0; r<height; r++) {
for(int c=0; c<width; c++) {
for (int rrIx=0; rrIx<height; rrIx++) {
for (int rcIx=0; rcIx<width; rcIx++) {
double term = 1D;
for(int n=0; n<maxN; n++) {
term *= elements[n][r][c];
double val = getProductTerm(args[n].getRelativeValue(rrIx, rcIx), false);
term *= val;
}
acc += term;
}
@ -172,60 +167,61 @@ public final class Sumproduct implements Function {
return new NumberEval(acc);
}
/**
* @return a 2-D array of the specified height and width corresponding to the evaluated cell
* values of the specified areaEval
* @throws EvalEx if any ErrorEval value was encountered while evaluating the area
*/
private static double[][] evaluateArea(AreaEval areaEval, int height, int width) throws EvalEx {
int fr =areaEval.getFirstRow();
int fc =areaEval.getFirstColumn();
private static void throwFirstError(AreaEval areaEval) throws EvaluationException {
int height = areaEval.getHeight();
int width = areaEval.getWidth();
for (int rrIx=0; rrIx<height; rrIx++) {
for (int rcIx=0; rcIx<width; rcIx++) {
ValueEval ve = areaEval.getRelativeValue(rrIx, rcIx);
if (ve instanceof ErrorEval) {
throw new EvaluationException((ErrorEval) ve);
}
}
}
}
private static boolean areasAllSameSize(AreaEval[] args, int height, int width) {
for (int i = 0; i < args.length; i++) {
AreaEval areaEval = args[i];
// check that height and width match
if(areaEval.getLastRow() - fr + 1 != height) {
throw new EvalEx(ErrorEval.VALUE_INVALID);
if(areaEval.getHeight() != height) {
return false;
}
if(areaEval.getLastColumn() - fc + 1 != width) {
throw new EvalEx(ErrorEval.VALUE_INVALID);
}
ValueEval[] values = areaEval.getValues();
double[][] result = new double[height][width];
for(int r=0; r<height; r++) {
for(int c=0; c<width; c++) {
ValueEval ve = values[r*width + c];
result[r][c] = getProductTerm(ve, false);
if(areaEval.getWidth() != width) {
return false;
}
}
return result;
return true;
}
/**
* Determines a <code>double</code> value for the specified <code>ValueEval</code>.
* @param isScalarProduct <code>false</code> for SUMPRODUCTs over area refs.
* @throws EvalEx if <code>ve</code> represents an error value.
* @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 EvalEx {
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 EvalEx(ErrorEval.VALUE_INVALID);
throw new EvaluationException(ErrorEval.VALUE_INVALID);
}
return 0;
}
if(ve instanceof ErrorEval) {
throw new EvalEx((ErrorEval)ve);
throw new EvaluationException((ErrorEval)ve);
}
if(ve instanceof StringEval) {
if(isScalarProduct) {
throw new EvalEx(ErrorEval.VALUE_INVALID);
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

View File

@ -102,7 +102,7 @@ public class HSSFFormulaEvaluator {
/**
* Does nothing
* @deprecated - not needed, since the current row can be derived from the cell
* @deprecated (Aug 2008) - not needed, since the current row can be derived from the cell
*/
public void setCurrentRow(HSSFRow row) {
// do nothing
@ -451,7 +451,7 @@ public class HSSFFormulaEvaluator {
AreaEval ae = (AreaEval) evaluationResult;
if (ae.isRow()) {
if(ae.isColumn()) {
return ae.getValues()[0];
return ae.getRelativeValue(0, 0);
}
return ae.getValueAt(ae.getFirstRow(), srcColNum);
}

View File

@ -33,31 +33,35 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
* @author Josh Micich
*/
final class EvalFactory {
private static final NumberEval ZERO = new NumberEval(0);
private EvalFactory() {
// no instances of this class
}
/**
* Creates a dummy AreaEval (filled with zeros)
* <p/>
* nCols and nRows could have been derived
* Creates a dummy AreaEval
* @param values empty (<code>null</code>) entries in this array will be converted to NumberEval.ZERO
*/
public static AreaEval createAreaEval(String areaRefStr, int nCols, int nRows) {
int nValues = nCols * nRows;
ValueEval[] values = new ValueEval[nValues];
for (int i = 0; i < nValues; i++) {
values[i] = ZERO;
public static AreaEval createAreaEval(String areaRefStr, ValueEval[] values) {
AreaPtg areaPtg = new AreaPtg(areaRefStr);
int nCols = areaPtg.getLastColumn() - areaPtg.getFirstColumn() + 1;
int nRows = areaPtg.getLastRow() - areaPtg.getFirstRow() + 1;
int nExpected = nRows * nCols;
if (values.length != nExpected) {
throw new RuntimeException("Expected " + nExpected + " values but got " + values.length);
}
return new Area2DEval(new AreaPtg(areaRefStr), values);
for (int i = 0; i < nExpected; i++) {
if (values[i] == null) {
values[i] = NumberEval.ZERO;
}
}
return new Area2DEval(areaPtg, values);
}
/**
* Creates a single RefEval (with value zero)
*/
public static RefEval createRefEval(String refStr) {
return new Ref2DEval(new RefPtg(refStr), ZERO);
return new Ref2DEval(new RefPtg(refStr), NumberEval.ZERO);
}
}

View File

@ -15,7 +15,6 @@
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record.formula.functions;
import junit.framework.AssertionFailedError;
@ -48,10 +47,6 @@ import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue;
*/
public final class TestCountFuncs extends TestCase {
public TestCountFuncs(String testName) {
super(testName);
}
public void testCountA() {
Eval[] args;
@ -69,14 +64,14 @@ public final class TestCountFuncs extends TestCase {
confirmCountA(3, args);
args = new Eval[] {
EvalFactory.createAreaEval("D2:F5", 3, 4),
EvalFactory.createAreaEval("D2:F5", new ValueEval[12]),
};
confirmCountA(12, args);
args = new Eval[] {
EvalFactory.createAreaEval("D1:F5", 3, 5), // 15
EvalFactory.createAreaEval("D1:F5", new ValueEval[15]),
EvalFactory.createRefEval("A1"),
EvalFactory.createAreaEval("A1:G6", 7, 6), // 42
EvalFactory.createAreaEval("A1:G6", new ValueEval[42]),
new NumberEval(0),
};
confirmCountA(59, args);

View File

@ -15,12 +15,12 @@
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record.formula.functions;
import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
/**
* Tests for ROW(), ROWS(), COLUMN(), COLUMNS()
@ -29,10 +29,6 @@ import org.apache.poi.hssf.record.formula.eval.Eval;
*/
public final class TestRowCol extends TestCase {
public TestRowCol(String testName) {
super(testName);
}
public void testCol() {
Function target = new Column();
{
@ -41,7 +37,7 @@ public final class TestRowCol extends TestCase {
assertEquals(3, actual, 0D);
}
{
Eval[] args = { EvalFactory.createAreaEval("E2:H12", 4, 11), };
Eval[] args = { EvalFactory.createAreaEval("E2:H12", new ValueEval[44]), };
double actual = NumericFunctionInvoker.invoke(target, args);
assertEquals(5, actual, 0D);
}
@ -55,7 +51,7 @@ public final class TestRowCol extends TestCase {
assertEquals(5, actual, 0D);
}
{
Eval[] args = { EvalFactory.createAreaEval("E2:H12", 4, 11), };
Eval[] args = { EvalFactory.createAreaEval("E2:H12", new ValueEval[44]), };
double actual = NumericFunctionInvoker.invoke(target, args);
assertEquals(2, actual, 0D);
}
@ -86,7 +82,7 @@ public final class TestRowCol extends TestCase {
}
private static void confirmRowsFunc(String areaRefStr, int nCols, int nRows) {
Eval[] args = { EvalFactory.createAreaEval(areaRefStr, nCols, nRows), };
Eval[] args = { EvalFactory.createAreaEval(areaRefStr, new ValueEval[nCols * nRows]), };
double actual = NumericFunctionInvoker.invoke(new Rows(), args);
assertEquals(nRows, actual, 0D);
@ -94,7 +90,7 @@ public final class TestRowCol extends TestCase {
private static void confirmColumnsFunc(String areaRefStr, int nCols, int nRows) {
Eval[] args = { EvalFactory.createAreaEval(areaRefStr, nCols, nRows), };
Eval[] args = { EvalFactory.createAreaEval(areaRefStr, new ValueEval[nCols * nRows]), };
double actual = NumericFunctionInvoker.invoke(new Columns(), args);
assertEquals(nCols, actual, 0D);

View File

@ -59,19 +59,19 @@ public final class TestSumproduct extends TestCase {
confirmDouble(6D, result);
}
public void testAreaSimple() {
AreaEval aeA = EvalFactory.createAreaEval("A1:A3", 1, 3);
AreaEval aeB = EvalFactory.createAreaEval("B1:B3", 1, 3);
ValueEval[] aValues = aeA.getValues();
ValueEval[] bValues = aeB.getValues();
aValues[0] = new NumberEval(2);
aValues[1] = new NumberEval(4);
aValues[2] = new NumberEval(5);
bValues[0] = new NumberEval(3);
bValues[1] = new NumberEval(6);
bValues[2] = new NumberEval(7);
ValueEval[] aValues = {
new NumberEval(2),
new NumberEval(4),
new NumberEval(5),
};
ValueEval[] bValues = {
new NumberEval(3),
new NumberEval(6),
new NumberEval(7),
};
AreaEval aeA = EvalFactory.createAreaEval("A1:A3", aValues);
AreaEval aeB = EvalFactory.createAreaEval("B1:B3", bValues);
Eval[] args = { aeA, aeB, };
Eval result = invokeSumproduct(args);
@ -83,8 +83,7 @@ public final class TestSumproduct extends TestCase {
*/
public void testOneByOneArea() {
AreaEval ae = EvalFactory.createAreaEval("A1:A1", 1, 1);
ae.getValues()[0] = new NumberEval(7);
AreaEval ae = EvalFactory.createAreaEval("A1:A1", new ValueEval[] { new NumberEval(7), });
Eval[] args = {
ae,
@ -94,11 +93,10 @@ public final class TestSumproduct extends TestCase {
confirmDouble(14D, result);
}
public void testMismatchAreaDimensions() {
AreaEval aeA = EvalFactory.createAreaEval("A1:A3", 1, 3);
AreaEval aeB = EvalFactory.createAreaEval("B1:D1", 3, 1);
AreaEval aeA = EvalFactory.createAreaEval("A1:A3", new ValueEval[3]);
AreaEval aeB = EvalFactory.createAreaEval("B1:D1", new ValueEval[3]);
Eval[] args;
args = new Eval[] { aeA, aeB, };
@ -109,12 +107,15 @@ public final class TestSumproduct extends TestCase {
}
public void testAreaWithErrorCell() {
AreaEval aeA = EvalFactory.createAreaEval("A1:A2", 1, 2);
AreaEval aeB = EvalFactory.createAreaEval("B1:B2", 1, 2);
ValueEval[] aValues = {
ErrorEval.REF_INVALID,
null,
};
AreaEval aeA = EvalFactory.createAreaEval("A1:A2", aValues);
AreaEval aeB = EvalFactory.createAreaEval("B1:B2", new ValueEval[2]);
aeB.getValues()[1] = ErrorEval.REF_INVALID;
Eval[] args = { aeA, aeB, };
assertEquals(ErrorEval.REF_INVALID, invokeSumproduct(args));
}
}