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

@ -22,7 +22,7 @@ package org.apache.poi.hssf.record.formula.eval;
/** /**
* @author Amol S. Deshmukh < amolweb at ya hoo dot com > * @author Amol S. Deshmukh < amolweb at ya hoo dot com >
* *
*/ */
public interface AreaEval extends ValueEval { public interface AreaEval extends ValueEval {
@ -30,49 +30,49 @@ public interface AreaEval extends ValueEval {
* returns the 0-based index of the first row in * returns the 0-based index of the first row in
* this area. * this area.
*/ */
public int getFirstRow(); int getFirstRow();
/** /**
* returns the 0-based index of the last row in * returns the 0-based index of the last row in
* this area. * this area.
*/ */
public int getLastRow(); int getLastRow();
/** /**
* returns the 0-based index of the first col in * returns the 0-based index of the first col in
* this area. * this area.
*/ */
public int getFirstColumn(); int getFirstColumn();
/** /**
* returns the 0-based index of the last col in * returns the 0-based index of the last col in
* this area. * this area.
*/ */
public int getLastColumn(); int getLastColumn();
/** /**
* returns true if the Area's start and end row indexes * returns true if the Area's start and end row indexes
* are same. This result of this method should agree * are same. This result of this method should agree
* with getFirstRow() == getLastRow(). * with getFirstRow() == getLastRow().
*/ */
public boolean isRow(); boolean isRow();
/** /**
* returns true if the Area's start and end col indexes * returns true if the Area's start and end col indexes
* are same. This result of this method should agree * are same. This result of this method should agree
* with getFirstColumn() == getLastColumn(). * with getFirstColumn() == getLastColumn().
*/ */
public boolean isColumn(); boolean isColumn();
/** /**
* The array of values in this area. Although the area * The array of values in this area. Although the area
* maybe 1D (ie. isRow() or isColumn() returns true) or 2D * maybe 1D (ie. isRow() or isColumn() returns true) or 2D
* the returned array is 1D. * the returned array is 1D.
*/ */
public ValueEval[] getValues(); ValueEval[] getValues();
/** /**
* returns the ValueEval from the values array at the specified * returns the ValueEval from the values array at the specified
* row and col index. The specified indexes should be absolute indexes * row and col index. The specified indexes should be absolute indexes
* in the sheet and not relative indexes within the area. Also, * in the sheet and not relative indexes within the area. Also,
* if contains(row, col) evaluates to true, a null value will * if contains(row, col) evaluates to true, a null value will
@ -80,26 +80,30 @@ public interface AreaEval extends ValueEval {
* @param row * @param row
* @param col * @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 * returns true if the cell at row and col specified
* as absolute indexes in the sheet is contained in * as absolute indexes in the sheet is contained in
* this area. * this area.
* @param row * @param row
* @param col * @param col
*/ */
public boolean contains(int row, int col); boolean contains(int row, int col);
/** /**
* returns true if the specified col is in range * returns true if the specified col is in range
* @param col * @param col
*/ */
public boolean containsColumn(short col); boolean containsColumn(short col);
/** /**
* returns true if the specified row is in range * returns true if the specified row is in range
* @param row * @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 throw new IllegalArgumentException("Specified column index (" + col
+ ") is outside the allowed range (" + _firstColumn + ".." + col + ")"); + ") is outside the allowed range (" + _firstColumn + ".." + col + ")");
} }
return getRelativeValue(rowOffsetIx, colOffsetIx);
int index = rowOffsetIx * _nColumns + colOffsetIx;
return _values[index];
} }
public final boolean contains(int row, int col) { public final boolean contains(int row, int col) {
@ -119,4 +117,16 @@ abstract class AreaEvalBase implements AreaEval {
public final boolean isRow() { public final boolean isRow() {
return _firstRow == _lastRow; 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

@ -27,7 +27,7 @@ public final class OperandResolver {
private OperandResolver() { private OperandResolver() {
// no instances of this class // no instances of this class
} }
/** /**
* Retrieves a single value from a variety of different argument types according to standard * Retrieves a single value from a variety of different argument types according to standard
* Excel rules. Does not perform any type conversion. * Excel rules. Does not perform any type conversion.
@ -113,17 +113,17 @@ public final class OperandResolver {
} }
if (result instanceof ErrorEval) { if (result instanceof ErrorEval) {
throw new EvaluationException((ErrorEval) result); throw new EvaluationException((ErrorEval) result);
} }
return result; return result;
} }
/** /**
* @return possibly <tt>ErrorEval</tt>, and <code>null</code> * @return possibly <tt>ErrorEval</tt>, and <code>null</code>
*/ */
private static ValueEval chooseSingleElementFromAreaInternal(AreaEval ae, private static ValueEval chooseSingleElementFromAreaInternal(AreaEval ae,
int srcCellRow, short srcCellCol) throws EvaluationException { int srcCellRow, short srcCellCol) throws EvaluationException {
if(false) { if(false) {
// this is too simplistic // this is too simplistic
if(ae.containsRow(srcCellRow) && ae.containsColumn(srcCellCol)) { if(ae.containsRow(srcCellRow) && ae.containsColumn(srcCellCol)) {
@ -131,25 +131,25 @@ public final class OperandResolver {
} }
/* /*
Circular references are not dealt with directly here, but it is worth noting some issues. Circular references are not dealt with directly here, but it is worth noting some issues.
ANY one of the return statements in this method could return a cell that is identical ANY one of the return statements in this method could return a cell that is identical
to the one immediately being evaluated. The evaluating cell is identified by srcCellRow, to the one immediately being evaluated. The evaluating cell is identified by srcCellRow,
srcCellRow AND sheet. The sheet is not available in any nearby calling method, so that's srcCellRow AND sheet. The sheet is not available in any nearby calling method, so that's
one reason why circular references are not easy to detect here. (The sheet of the returned one reason why circular references are not easy to detect here. (The sheet of the returned
cell can be obtained from ae if it is an Area3DEval.) cell can be obtained from ae if it is an Area3DEval.)
Another reason there's little value in attempting to detect circular references here is Another reason there's little value in attempting to detect circular references here is
that only direct circular references could be detected. If the cycle involved two or more that only direct circular references could be detected. If the cycle involved two or more
cells this method could not detect it. cells this method could not detect it.
Logic to detect evaluation cycles of all kinds has been coded in EvaluationCycleDetector Logic to detect evaluation cycles of all kinds has been coded in EvaluationCycleDetector
(and HSSFFormulaEvaluator). (and HSSFFormulaEvaluator).
*/ */
} }
if (ae.isColumn()) { if (ae.isColumn()) {
if(ae.isRow()) { if(ae.isRow()) {
return ae.getValues()[0]; return ae.getRelativeValue(0, 0);
} }
if(!ae.containsRow(srcCellRow)) { if(!ae.containsRow(srcCellRow)) {
throw EvaluationException.invalidValue(); throw EvaluationException.invalidValue();
@ -199,20 +199,20 @@ public final class OperandResolver {
*/ */
public static double coerceValueToDouble(ValueEval ev) throws EvaluationException { public static double coerceValueToDouble(ValueEval ev) throws EvaluationException {
if (ev instanceof NumericValueEval) { if (ev instanceof NumericValueEval) {
// this also handles booleans // this also handles booleans
return ((NumericValueEval)ev).getNumberValue(); return ((NumericValueEval)ev).getNumberValue();
} }
if (ev instanceof StringEval) { if (ev instanceof StringEval) {
Double dd = parseDouble(((StringEval) ev).getStringValue()); Double dd = parseDouble(((StringEval) ev).getStringValue());
if (dd == null) { if (dd == null) {
throw EvaluationException.invalidValue(); throw EvaluationException.invalidValue();
} }
return dd.doubleValue(); return dd.doubleValue();
} }
throw new RuntimeException("Unexpected arg eval type (" + ev.getClass().getName() + ")"); throw new RuntimeException("Unexpected arg eval type (" + ev.getClass().getName() + ")");
} }
/** /**
* Converts a string to a double using standard rules that Excel would use.<br/> * Converts a string to a double using standard rules that Excel would use.<br/>
* Tolerates currency prefixes, commas, leading and trailing spaces.<p/> * Tolerates currency prefixes, commas, leading and trailing spaces.<p/>
@ -245,7 +245,7 @@ public final class OperandResolver {
return null; return null;
} }
// TODO - support notation like '1E3' (==1000) // TODO - support notation like '1E3' (==1000)
double val; double val;
try { try {
val = Double.parseDouble(text); val = Double.parseDouble(text);
@ -254,7 +254,7 @@ public final class OperandResolver {
} }
return new Double(isPositive ? +val : -val); return new Double(isPositive ? +val : -val);
} }
/** /**
* @param ve must be a <tt>NumberEval</tt>, <tt>StringEval</tt>, <tt>BoolEval</tt>, or <tt>BlankEval</tt> * @param ve must be a <tt>NumberEval</tt>, <tt>StringEval</tt>, <tt>BoolEval</tt>, or <tt>BlankEval</tt>
* @return the converted string value. never <code>null</code> * @return the converted string value. never <code>null</code>
@ -274,4 +274,51 @@ public final class OperandResolver {
} }
throw new IllegalArgumentException("Unexpected eval class (" + ve.getClass().getName() + ")"); 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 Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0 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 not use this file except in compliance with
* the License. You may obtain a copy of the License at the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and See the License for the specific language governing permissions and
* limitations under the License. limitations under the License.
*/ ==================================================================== */
/*
* Created on May 9, 2005
*
*/
package org.apache.poi.hssf.record.formula.functions; 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; public final class And extends BooleanFunction {
import org.apache.poi.hssf.record.formula.eval.StringEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
public class And extends BooleanFunction { protected boolean getInitialResultValue() {
return true;
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;
}
}
}
if (!atleastOneNonBlank) {
retval = ErrorEval.VALUE_INVALID;
}
if (retval == null) { // if no error
retval = b ? BoolEval.TRUE : BoolEval.FALSE;
}
return retval;
}
protected boolean partialEvaluate(boolean cumulativeResult, boolean currentValue) {
return cumulativeResult && currentValue;
}
} }

View File

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

View File

@ -1,81 +1,32 @@
/* /* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one or more Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0 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 not use this file except in compliance with
* the License. You may obtain a copy of the License at the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and See the License for the specific language governing permissions and
* limitations under the License. limitations under the License.
*/ ==================================================================== */
/*
* Created on May 9, 2005
*
*/
package org.apache.poi.hssf.record.formula.functions; 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; public final class Or extends BooleanFunction {
import org.apache.poi.hssf.record.formula.eval.ValueEval;
public class Or extends BooleanFunction { protected boolean getInitialResultValue() {
return false;
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;
}
}
}
if (!atleastOneNonBlank) {
retval = ErrorEval.VALUE_INVALID;
}
if (retval == null) { // if no error
retval = b ? BoolEval.TRUE : BoolEval.FALSE;
}
return retval;
}
protected boolean partialEvaluate(boolean cumulativeResult, boolean currentValue) {
return cumulativeResult || currentValue;
}
} }

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.BlankEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval; 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.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.NumberEval;
import org.apache.poi.hssf.record.formula.eval.NumericValueEval; import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
import org.apache.poi.hssf.record.formula.eval.RefEval; 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 { 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) { public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
@ -86,14 +77,14 @@ public final class Sumproduct implements Function {
} }
return evaluateAreaSumProduct(args); return evaluateAreaSumProduct(args);
} }
} catch (EvalEx e) { } catch (EvaluationException e) {
return e.getError(); return e.getErrorEval();
} }
throw new RuntimeException("Invalid arg type for SUMPRODUCT: (" throw new RuntimeException("Invalid arg type for SUMPRODUCT: ("
+ firstArg.getClass().getName() + ")"); + firstArg.getClass().getName() + ")");
} }
private Eval evaluateSingleProduct(Eval[] evalArgs) throws EvalEx { private static Eval evaluateSingleProduct(Eval[] evalArgs) throws EvaluationException {
int maxN = evalArgs.length; int maxN = evalArgs.length;
double term = 1D; double term = 1D;
@ -104,7 +95,7 @@ public final class Sumproduct implements Function {
return new NumberEval(term); return new NumberEval(term);
} }
private double getScalarValue(Eval arg) throws EvalEx { private static double getScalarValue(Eval arg) throws EvaluationException {
Eval eval; Eval eval;
if (arg instanceof RefEval) { if (arg instanceof RefEval) {
@ -121,9 +112,9 @@ public final class Sumproduct implements Function {
AreaEval ae = (AreaEval) eval; AreaEval ae = (AreaEval) eval;
// an area ref can work as a scalar value if it is 1x1 // an area ref can work as a scalar value if it is 1x1
if(!ae.isColumn() || !ae.isRow()) { 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)) { if (!(eval instanceof ValueEval)) {
@ -134,7 +125,7 @@ public final class Sumproduct implements Function {
return getProductTerm((ValueEval) eval, true); return getProductTerm((ValueEval) eval, true);
} }
private Eval evaluateAreaSumProduct(Eval[] evalArgs) throws EvalEx { private static Eval evaluateAreaSumProduct(Eval[] evalArgs) throws EvaluationException {
int maxN = evalArgs.length; int maxN = evalArgs.length;
AreaEval[] args = new AreaEval[maxN]; AreaEval[] args = new AreaEval[maxN];
try { try {
@ -147,23 +138,27 @@ public final class Sumproduct implements Function {
AreaEval firstArg = args[0]; AreaEval firstArg = args[0];
int height = firstArg.getLastRow() - firstArg.getFirstRow() + 1; int height = firstArg.getHeight();
int width = firstArg.getLastColumn() - firstArg.getFirstColumn() + 1; // TODO - junit int width = firstArg.getWidth(); // TODO - junit
// first check dimensions
if (!areasAllSameSize(args, height, width)) {
double[][][] elements = new double[maxN][][]; // normally this results in #VALUE!,
// but errors in individual cells take precedence
for (int n = 0; n < maxN; n++) { for (int i = 1; i < args.length; i++) {
elements[n] = evaluateArea(args[n], height, width); throwFirstError(args[i]);
}
return ErrorEval.VALUE_INVALID;
} }
double acc = 0; double acc = 0;
for(int r=0; r<height; r++) { for (int rrIx=0; rrIx<height; rrIx++) {
for(int c=0; c<width; c++) { for (int rcIx=0; rcIx<width; rcIx++) {
double term = 1D; double term = 1D;
for(int n=0; n<maxN; n++) { 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; acc += term;
} }
@ -172,60 +167,61 @@ public final class Sumproduct implements Function {
return new NumberEval(acc); return new NumberEval(acc);
} }
/** private static void throwFirstError(AreaEval areaEval) throws EvaluationException {
* @return a 2-D array of the specified height and width corresponding to the evaluated cell int height = areaEval.getHeight();
* values of the specified areaEval int width = areaEval.getWidth();
* @throws EvalEx if any ErrorEval value was encountered while evaluating the area for (int rrIx=0; rrIx<height; rrIx++) {
*/ for (int rcIx=0; rcIx<width; rcIx++) {
private static double[][] evaluateArea(AreaEval areaEval, int height, int width) throws EvalEx { ValueEval ve = areaEval.getRelativeValue(rrIx, rcIx);
int fr =areaEval.getFirstRow(); if (ve instanceof ErrorEval) {
int fc =areaEval.getFirstColumn(); throw new EvaluationException((ErrorEval) ve);
}
// check that height and width match
if(areaEval.getLastRow() - fr + 1 != height) {
throw new EvalEx(ErrorEval.VALUE_INVALID);
}
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);
} }
} }
return result;
} }
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.getHeight() != height) {
return false;
}
if(areaEval.getWidth() != width) {
return false;
}
}
return true;
}
/** /**
* Determines a <code>double</code> value for the specified <code>ValueEval</code>. * Determines a <code>double</code> value for the specified <code>ValueEval</code>.
* @param isScalarProduct <code>false</code> for SUMPRODUCTs over area refs. * @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/> * <p/>
* Note - string values and empty cells are interpreted differently depending on * 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 * <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 * error (#VALUE!) is raised. For area (sum)products, if any term is blank or a string, the
* result is zero. * 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) { if(ve instanceof BlankEval || ve == null) {
// TODO - shouldn't BlankEval.INSTANCE be used always instead of 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) // null seems to occur when the blank cell is part of an area ref (but not reliably)
if(isScalarProduct) { if(isScalarProduct) {
throw new EvalEx(ErrorEval.VALUE_INVALID); throw new EvaluationException(ErrorEval.VALUE_INVALID);
} }
return 0; return 0;
} }
if(ve instanceof ErrorEval) { if(ve instanceof ErrorEval) {
throw new EvalEx((ErrorEval)ve); throw new EvaluationException((ErrorEval)ve);
} }
if(ve instanceof StringEval) { if(ve instanceof StringEval) {
if(isScalarProduct) { if(isScalarProduct) {
throw new EvalEx(ErrorEval.VALUE_INVALID); throw new EvaluationException(ErrorEval.VALUE_INVALID);
} }
// Note for area SUMPRODUCTs, string values are interpreted as zero // Note for area SUMPRODUCTs, string values are interpreted as zero
// even if they would parse as valid numeric values // even if they would parse as valid numeric values

View File

@ -102,7 +102,7 @@ public class HSSFFormulaEvaluator {
/** /**
* Does nothing * 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) { public void setCurrentRow(HSSFRow row) {
// do nothing // do nothing
@ -451,7 +451,7 @@ public class HSSFFormulaEvaluator {
AreaEval ae = (AreaEval) evaluationResult; AreaEval ae = (AreaEval) evaluationResult;
if (ae.isRow()) { if (ae.isRow()) {
if(ae.isColumn()) { if(ae.isColumn()) {
return ae.getValues()[0]; return ae.getRelativeValue(0, 0);
} }
return ae.getValueAt(ae.getFirstRow(), srcColNum); return ae.getValueAt(ae.getFirstRow(), srcColNum);
} }

View File

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

View File

@ -14,7 +14,6 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.hssf.record.formula.functions; package org.apache.poi.hssf.record.formula.functions;
@ -43,53 +42,49 @@ import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue;
/** /**
* Test cases for COUNT(), COUNTA() COUNTIF(), COUNTBLANK() * Test cases for COUNT(), COUNTA() COUNTIF(), COUNTBLANK()
* *
* @author Josh Micich * @author Josh Micich
*/ */
public final class TestCountFuncs extends TestCase { public final class TestCountFuncs extends TestCase {
public TestCountFuncs(String testName) {
super(testName);
}
public void testCountA() { public void testCountA() {
Eval[] args; Eval[] args;
args = new Eval[] { args = new Eval[] {
new NumberEval(0), new NumberEval(0),
}; };
confirmCountA(1, args); confirmCountA(1, args);
args = new Eval[] { args = new Eval[] {
new NumberEval(0), new NumberEval(0),
new NumberEval(0), new NumberEval(0),
new StringEval(""), new StringEval(""),
}; };
confirmCountA(3, args); confirmCountA(3, args);
args = new Eval[] { args = new Eval[] {
EvalFactory.createAreaEval("D2:F5", 3, 4), EvalFactory.createAreaEval("D2:F5", new ValueEval[12]),
}; };
confirmCountA(12, args); confirmCountA(12, args);
args = new Eval[] { args = new Eval[] {
EvalFactory.createAreaEval("D1:F5", 3, 5), // 15 EvalFactory.createAreaEval("D1:F5", new ValueEval[15]),
EvalFactory.createRefEval("A1"), EvalFactory.createRefEval("A1"),
EvalFactory.createAreaEval("A1:G6", 7, 6), // 42 EvalFactory.createAreaEval("A1:G6", new ValueEval[42]),
new NumberEval(0), new NumberEval(0),
}; };
confirmCountA(59, args); confirmCountA(59, args);
} }
public void testCountIf() { public void testCountIf() {
AreaEval range; AreaEval range;
ValueEval[] values; ValueEval[] values;
// when criteria is a boolean value // when criteria is a boolean value
values = new ValueEval[] { values = new ValueEval[] {
new NumberEval(0), new NumberEval(0),
new StringEval("TRUE"), // note - does not match boolean TRUE new StringEval("TRUE"), // note - does not match boolean TRUE
BoolEval.TRUE, BoolEval.TRUE,
BoolEval.FALSE, BoolEval.FALSE,
@ -98,22 +93,22 @@ public final class TestCountFuncs extends TestCase {
}; };
range = createAreaEval("A1:B3", values); range = createAreaEval("A1:B3", values);
confirmCountIf(2, range, BoolEval.TRUE); confirmCountIf(2, range, BoolEval.TRUE);
// when criteria is numeric // when criteria is numeric
values = new ValueEval[] { values = new ValueEval[] {
new NumberEval(0), new NumberEval(0),
new StringEval("2"), new StringEval("2"),
new StringEval("2.001"), new StringEval("2.001"),
new NumberEval(2), new NumberEval(2),
new NumberEval(2), new NumberEval(2),
BoolEval.TRUE, BoolEval.TRUE,
}; };
range = createAreaEval("A1:B3", values); range = createAreaEval("A1:B3", values);
confirmCountIf(3, range, new NumberEval(2)); confirmCountIf(3, range, new NumberEval(2));
// note - same results when criteria is a string that parses as the number with the same value // note - same results when criteria is a string that parses as the number with the same value
confirmCountIf(3, range, new StringEval("2.00")); confirmCountIf(3, range, new StringEval("2.00"));
if (false) { // not supported yet: if (false) { // not supported yet:
// when criteria is an expression (starting with a comparison operator) // when criteria is an expression (starting with a comparison operator)
confirmCountIf(4, range, new StringEval(">1")); confirmCountIf(4, range, new StringEval(">1"));
} }
@ -123,7 +118,7 @@ public final class TestCountFuncs extends TestCase {
*/ */
public void testCountIfWithCriteriaReference() { public void testCountIfWithCriteriaReference() {
ValueEval[] values = { ValueEval[] values = {
new NumberEval(22), new NumberEval(22),
new NumberEval(25), new NumberEval(25),
new NumberEval(21), new NumberEval(21),
@ -132,14 +127,14 @@ public final class TestCountFuncs extends TestCase {
new NumberEval(25), new NumberEval(25),
}; };
Area2DEval arg0 = new Area2DEval(new AreaPtg("C1:C6"), values); Area2DEval arg0 = new Area2DEval(new AreaPtg("C1:C6"), values);
Ref2DEval criteriaArg = new Ref2DEval(new RefPtg("A1"), new NumberEval(25)); Ref2DEval criteriaArg = new Ref2DEval(new RefPtg("A1"), new NumberEval(25));
Eval[] args= { arg0, criteriaArg, }; Eval[] args= { arg0, criteriaArg, };
double actual = NumericFunctionInvoker.invoke(new Countif(), args); double actual = NumericFunctionInvoker.invoke(new Countif(), args);
assertEquals(4, actual, 0D); assertEquals(4, actual, 0D);
} }
private static AreaEval createAreaEval(String areaRefStr, ValueEval[] values) { private static AreaEval createAreaEval(String areaRefStr, ValueEval[] values) {
return new Area2DEval(new AreaPtg(areaRefStr), values); return new Area2DEval(new AreaPtg(areaRefStr), values);
@ -150,15 +145,15 @@ public final class TestCountFuncs extends TestCase {
assertEquals(expected, result, 0); assertEquals(expected, result, 0);
} }
private static void confirmCountIf(int expected, AreaEval range, Eval criteria) { private static void confirmCountIf(int expected, AreaEval range, Eval criteria) {
Eval[] args = { range, criteria, }; Eval[] args = { range, criteria, };
double result = NumericFunctionInvoker.invoke(new Countif(), args); double result = NumericFunctionInvoker.invoke(new Countif(), args);
assertEquals(expected, result, 0); assertEquals(expected, result, 0);
} }
public void testCountIfEmptyStringCriteria() { public void testCountIfEmptyStringCriteria() {
I_MatchPredicate mp; I_MatchPredicate mp;
// pred '=' matches blank cell but not empty string // pred '=' matches blank cell but not empty string
mp = Countif.createCriteriaPredicate(new StringEval("=")); mp = Countif.createCriteriaPredicate(new StringEval("="));
confirmPredicate(false, mp, ""); confirmPredicate(false, mp, "");
@ -168,21 +163,21 @@ public final class TestCountFuncs extends TestCase {
mp = Countif.createCriteriaPredicate(new StringEval("")); mp = Countif.createCriteriaPredicate(new StringEval(""));
confirmPredicate(true, mp, ""); confirmPredicate(true, mp, "");
confirmPredicate(true, mp, null); confirmPredicate(true, mp, null);
// pred '<>' matches empty string but not blank cell // pred '<>' matches empty string but not blank cell
mp = Countif.createCriteriaPredicate(new StringEval("<>")); mp = Countif.createCriteriaPredicate(new StringEval("<>"));
confirmPredicate(false, mp, null); confirmPredicate(false, mp, null);
confirmPredicate(true, mp, ""); confirmPredicate(true, mp, "");
} }
public void testCountifComparisons() { public void testCountifComparisons() {
I_MatchPredicate mp; I_MatchPredicate mp;
mp = Countif.createCriteriaPredicate(new StringEval(">5")); mp = Countif.createCriteriaPredicate(new StringEval(">5"));
confirmPredicate(false, mp, 4); confirmPredicate(false, mp, 4);
confirmPredicate(false, mp, 5); confirmPredicate(false, mp, 5);
confirmPredicate(true, mp, 6); confirmPredicate(true, mp, 6);
mp = Countif.createCriteriaPredicate(new StringEval("<=5")); mp = Countif.createCriteriaPredicate(new StringEval("<=5"));
confirmPredicate(true, mp, 4); confirmPredicate(true, mp, 4);
confirmPredicate(true, mp, 5); confirmPredicate(true, mp, 5);
@ -194,7 +189,7 @@ public final class TestCountFuncs extends TestCase {
mp = Countif.createCriteriaPredicate(new StringEval("=abc")); mp = Countif.createCriteriaPredicate(new StringEval("=abc"));
confirmPredicate(true, mp, "abc"); confirmPredicate(true, mp, "abc");
mp = Countif.createCriteriaPredicate(new StringEval("=42")); mp = Countif.createCriteriaPredicate(new StringEval("=42"));
confirmPredicate(false, mp, 41); confirmPredicate(false, mp, 41);
confirmPredicate(true, mp, 42); confirmPredicate(true, mp, 42);
@ -211,28 +206,28 @@ public final class TestCountFuncs extends TestCase {
confirmPredicate(true, mp, "500"); confirmPredicate(true, mp, "500");
confirmPredicate(true, mp, "4t4"); confirmPredicate(true, mp, "4t4");
} }
public void testWildCards() { public void testWildCards() {
I_MatchPredicate mp; I_MatchPredicate mp;
mp = Countif.createCriteriaPredicate(new StringEval("a*b")); mp = Countif.createCriteriaPredicate(new StringEval("a*b"));
confirmPredicate(false, mp, "abc"); confirmPredicate(false, mp, "abc");
confirmPredicate(true, mp, "ab"); confirmPredicate(true, mp, "ab");
confirmPredicate(true, mp, "axxb"); confirmPredicate(true, mp, "axxb");
confirmPredicate(false, mp, "xab"); confirmPredicate(false, mp, "xab");
mp = Countif.createCriteriaPredicate(new StringEval("a?b")); mp = Countif.createCriteriaPredicate(new StringEval("a?b"));
confirmPredicate(false, mp, "abc"); confirmPredicate(false, mp, "abc");
confirmPredicate(false, mp, "ab"); confirmPredicate(false, mp, "ab");
confirmPredicate(false, mp, "axxb"); confirmPredicate(false, mp, "axxb");
confirmPredicate(false, mp, "xab"); confirmPredicate(false, mp, "xab");
confirmPredicate(true, mp, "axb"); confirmPredicate(true, mp, "axb");
mp = Countif.createCriteriaPredicate(new StringEval("a~?")); mp = Countif.createCriteriaPredicate(new StringEval("a~?"));
confirmPredicate(false, mp, "a~a"); confirmPredicate(false, mp, "a~a");
confirmPredicate(false, mp, "a~?"); confirmPredicate(false, mp, "a~?");
confirmPredicate(true, mp, "a?"); confirmPredicate(true, mp, "a?");
mp = Countif.createCriteriaPredicate(new StringEval("~*a")); mp = Countif.createCriteriaPredicate(new StringEval("~*a"));
confirmPredicate(false, mp, "~aa"); confirmPredicate(false, mp, "~aa");
confirmPredicate(false, mp, "~*a"); confirmPredicate(false, mp, "~*a");
@ -245,40 +240,40 @@ public final class TestCountFuncs extends TestCase {
} }
public void testNotQuiteWildCards() { public void testNotQuiteWildCards() {
I_MatchPredicate mp; I_MatchPredicate mp;
// make sure special reg-ex chars are treated like normal chars // make sure special reg-ex chars are treated like normal chars
mp = Countif.createCriteriaPredicate(new StringEval("a.b")); mp = Countif.createCriteriaPredicate(new StringEval("a.b"));
confirmPredicate(false, mp, "aab"); confirmPredicate(false, mp, "aab");
confirmPredicate(true, mp, "a.b"); confirmPredicate(true, mp, "a.b");
mp = Countif.createCriteriaPredicate(new StringEval("a~b")); mp = Countif.createCriteriaPredicate(new StringEval("a~b"));
confirmPredicate(false, mp, "ab"); confirmPredicate(false, mp, "ab");
confirmPredicate(false, mp, "axb"); confirmPredicate(false, mp, "axb");
confirmPredicate(false, mp, "a~~b"); confirmPredicate(false, mp, "a~~b");
confirmPredicate(true, mp, "a~b"); confirmPredicate(true, mp, "a~b");
mp = Countif.createCriteriaPredicate(new StringEval(">a*b")); mp = Countif.createCriteriaPredicate(new StringEval(">a*b"));
confirmPredicate(false, mp, "a(b"); confirmPredicate(false, mp, "a(b");
confirmPredicate(true, mp, "aab"); confirmPredicate(true, mp, "aab");
confirmPredicate(false, mp, "a*a"); confirmPredicate(false, mp, "a*a");
confirmPredicate(true, mp, "a*c"); confirmPredicate(true, mp, "a*c");
} }
private static void confirmPredicate(boolean expectedResult, I_MatchPredicate matchPredicate, int value) { private static void confirmPredicate(boolean expectedResult, I_MatchPredicate matchPredicate, int value) {
assertEquals(expectedResult, matchPredicate.matches(new NumberEval(value))); assertEquals(expectedResult, matchPredicate.matches(new NumberEval(value)));
} }
private static void confirmPredicate(boolean expectedResult, I_MatchPredicate matchPredicate, String value) { private static void confirmPredicate(boolean expectedResult, I_MatchPredicate matchPredicate, String value) {
Eval ev = value == null ? (Eval)BlankEval.INSTANCE : new StringEval(value); Eval ev = value == null ? (Eval)BlankEval.INSTANCE : new StringEval(value);
assertEquals(expectedResult, matchPredicate.matches(ev)); assertEquals(expectedResult, matchPredicate.matches(ev));
} }
public void testCountifFromSpreadsheet() { public void testCountifFromSpreadsheet() {
final String FILE_NAME = "countifExamples.xls"; final String FILE_NAME = "countifExamples.xls";
final int START_ROW_IX = 1; final int START_ROW_IX = 1;
final int COL_IX_ACTUAL = 2; final int COL_IX_ACTUAL = 2;
final int COL_IX_EXPECTED = 3; final int COL_IX_EXPECTED = 3;
int failureCount = 0; int failureCount = 0;
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook(FILE_NAME); HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook(FILE_NAME);
HSSFSheet sheet = wb.getSheetAt(0); HSSFSheet sheet = wb.getSheetAt(0);
@ -299,7 +294,7 @@ public final class TestCountFuncs extends TestCase {
failureCount++; failureCount++;
} }
} }
if (failureCount > 0) { if (failureCount > 0) {
throw new AssertionFailedError(failureCount + " countif evaluations failed. See stderr for more details"); throw new AssertionFailedError(failureCount + " countif evaluations failed. See stderr for more details");
} }

View File

@ -14,13 +14,13 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.hssf.record.formula.functions; package org.apache.poi.hssf.record.formula.functions;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.eval.Eval; import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
/** /**
* Tests for ROW(), ROWS(), COLUMN(), COLUMNS() * 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 final class TestRowCol extends TestCase {
public TestRowCol(String testName) {
super(testName);
}
public void testCol() { public void testCol() {
Function target = new Column(); Function target = new Column();
{ {
@ -41,7 +37,7 @@ public final class TestRowCol extends TestCase {
assertEquals(3, actual, 0D); 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); double actual = NumericFunctionInvoker.invoke(target, args);
assertEquals(5, actual, 0D); assertEquals(5, actual, 0D);
} }
@ -55,7 +51,7 @@ public final class TestRowCol extends TestCase {
assertEquals(5, actual, 0D); 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); double actual = NumericFunctionInvoker.invoke(target, args);
assertEquals(2, actual, 0D); assertEquals(2, actual, 0D);
} }
@ -86,7 +82,7 @@ public final class TestRowCol extends TestCase {
} }
private static void confirmRowsFunc(String areaRefStr, int nCols, int nRows) { 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); double actual = NumericFunctionInvoker.invoke(new Rows(), args);
assertEquals(nRows, actual, 0D); assertEquals(nRows, actual, 0D);
@ -94,7 +90,7 @@ public final class TestRowCol extends TestCase {
private static void confirmColumnsFunc(String areaRefStr, int nCols, int nRows) { 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); double actual = NumericFunctionInvoker.invoke(new Columns(), args);
assertEquals(nCols, actual, 0D); assertEquals(nCols, actual, 0D);

View File

@ -49,7 +49,7 @@ public final class TestSumproduct extends TestCase {
} }
public void testScalarSimple() { public void testScalarSimple() {
RefEval refEval = new Ref2DEval(new RefPtg("A1"), new NumberEval(3)); RefEval refEval = new Ref2DEval(new RefPtg("A1"), new NumberEval(3));
Eval[] args = { Eval[] args = {
refEval, refEval,
@ -59,19 +59,19 @@ public final class TestSumproduct extends TestCase {
confirmDouble(6D, result); confirmDouble(6D, result);
} }
public void testAreaSimple() { public void testAreaSimple() {
ValueEval[] aValues = {
AreaEval aeA = EvalFactory.createAreaEval("A1:A3", 1, 3); new NumberEval(2),
AreaEval aeB = EvalFactory.createAreaEval("B1:B3", 1, 3); new NumberEval(4),
ValueEval[] aValues = aeA.getValues(); new NumberEval(5),
ValueEval[] bValues = aeB.getValues(); };
aValues[0] = new NumberEval(2); ValueEval[] bValues = {
aValues[1] = new NumberEval(4); new NumberEval(3),
aValues[2] = new NumberEval(5); new NumberEval(6),
bValues[0] = new NumberEval(3); new NumberEval(7),
bValues[1] = new NumberEval(6); };
bValues[2] = new NumberEval(7); AreaEval aeA = EvalFactory.createAreaEval("A1:A3", aValues);
AreaEval aeB = EvalFactory.createAreaEval("B1:B3", bValues);
Eval[] args = { aeA, aeB, }; Eval[] args = { aeA, aeB, };
Eval result = invokeSumproduct(args); Eval result = invokeSumproduct(args);
@ -82,10 +82,9 @@ public final class TestSumproduct extends TestCase {
* For scalar products, the terms may be 1x1 area refs * For scalar products, the terms may be 1x1 area refs
*/ */
public void testOneByOneArea() { public void testOneByOneArea() {
AreaEval ae = EvalFactory.createAreaEval("A1:A1", 1, 1); AreaEval ae = EvalFactory.createAreaEval("A1:A1", new ValueEval[] { new NumberEval(7), });
ae.getValues()[0] = new NumberEval(7);
Eval[] args = { Eval[] args = {
ae, ae,
new NumberEval(2), new NumberEval(2),
@ -94,27 +93,29 @@ public final class TestSumproduct extends TestCase {
confirmDouble(14D, result); confirmDouble(14D, result);
} }
public void testMismatchAreaDimensions() { public void testMismatchAreaDimensions() {
AreaEval aeA = EvalFactory.createAreaEval("A1:A3", 1, 3); AreaEval aeA = EvalFactory.createAreaEval("A1:A3", new ValueEval[3]);
AreaEval aeB = EvalFactory.createAreaEval("B1:D1", 3, 1); AreaEval aeB = EvalFactory.createAreaEval("B1:D1", new ValueEval[3]);
Eval[] args; Eval[] args;
args = new Eval[] { aeA, aeB, }; args = new Eval[] { aeA, aeB, };
assertEquals(ErrorEval.VALUE_INVALID, invokeSumproduct(args)); assertEquals(ErrorEval.VALUE_INVALID, invokeSumproduct(args));
args = new Eval[] { aeA, new NumberEval(5), }; args = new Eval[] { aeA, new NumberEval(5), };
assertEquals(ErrorEval.VALUE_INVALID, invokeSumproduct(args)); assertEquals(ErrorEval.VALUE_INVALID, invokeSumproduct(args));
} }
public void testAreaWithErrorCell() { public void testAreaWithErrorCell() {
AreaEval aeA = EvalFactory.createAreaEval("A1:A2", 1, 2); ValueEval[] aValues = {
AreaEval aeB = EvalFactory.createAreaEval("B1:B2", 1, 2); 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; aeB.getValues()[1] = ErrorEval.REF_INVALID;
Eval[] args = { aeA, aeB, }; Eval[] args = { aeA, aeB, };
assertEquals(ErrorEval.REF_INVALID, invokeSumproduct(args)); assertEquals(ErrorEval.REF_INVALID, invokeSumproduct(args));
} }
} }