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:
parent
adff0508bb
commit
adc8469ebc
@ -22,7 +22,7 @@ package org.apache.poi.hssf.record.formula.eval;
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*
|
||||
*
|
||||
*/
|
||||
public interface AreaEval extends ValueEval {
|
||||
|
||||
@ -30,49 +30,49 @@ 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
|
||||
* returns the ValueEval from the values array at the specified
|
||||
* row and col index. The specified indexes should be absolute indexes
|
||||
* in the sheet and not relative indexes within the area. Also,
|
||||
* if contains(row, col) evaluates to true, a null value will
|
||||
@ -80,26 +80,30 @@ 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
|
||||
* as absolute indexes in the sheet is contained in
|
||||
* returns true if the cell at row and col specified
|
||||
* as absolute indexes in the sheet is contained in
|
||||
* this area.
|
||||
* @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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ public final class OperandResolver {
|
||||
private OperandResolver() {
|
||||
// no instances of this class
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves a single value from a variety of different argument types according to standard
|
||||
* Excel rules. Does not perform any type conversion.
|
||||
@ -113,17 +113,17 @@ public final class OperandResolver {
|
||||
}
|
||||
if (result instanceof ErrorEval) {
|
||||
throw new EvaluationException((ErrorEval) result);
|
||||
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return possibly <tt>ErrorEval</tt>, and <code>null</code>
|
||||
*/
|
||||
private static ValueEval chooseSingleElementFromAreaInternal(AreaEval ae,
|
||||
int srcCellRow, short srcCellCol) throws EvaluationException {
|
||||
|
||||
|
||||
if(false) {
|
||||
// this is too simplistic
|
||||
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.
|
||||
|
||||
|
||||
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,
|
||||
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
|
||||
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
|
||||
that only direct circular references could be detected. If the cycle involved two or more
|
||||
cells this method could not detect it.
|
||||
|
||||
|
||||
Logic to detect evaluation cycles of all kinds has been coded in EvaluationCycleDetector
|
||||
(and HSSFFormulaEvaluator).
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
if (ae.isColumn()) {
|
||||
if(ae.isRow()) {
|
||||
return ae.getValues()[0];
|
||||
return ae.getRelativeValue(0, 0);
|
||||
}
|
||||
if(!ae.containsRow(srcCellRow)) {
|
||||
throw EvaluationException.invalidValue();
|
||||
@ -199,20 +199,20 @@ public final class OperandResolver {
|
||||
*/
|
||||
public static double coerceValueToDouble(ValueEval ev) throws EvaluationException {
|
||||
|
||||
if (ev instanceof NumericValueEval) {
|
||||
// this also handles booleans
|
||||
return ((NumericValueEval)ev).getNumberValue();
|
||||
}
|
||||
if (ev instanceof StringEval) {
|
||||
Double dd = parseDouble(((StringEval) ev).getStringValue());
|
||||
if (dd == null) {
|
||||
throw EvaluationException.invalidValue();
|
||||
}
|
||||
return dd.doubleValue();
|
||||
if (ev instanceof NumericValueEval) {
|
||||
// this also handles booleans
|
||||
return ((NumericValueEval)ev).getNumberValue();
|
||||
}
|
||||
if (ev instanceof StringEval) {
|
||||
Double dd = parseDouble(((StringEval) ev).getStringValue());
|
||||
if (dd == null) {
|
||||
throw EvaluationException.invalidValue();
|
||||
}
|
||||
return dd.doubleValue();
|
||||
}
|
||||
throw new RuntimeException("Unexpected arg eval type (" + ev.getClass().getName() + ")");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts a string to a double using standard rules that Excel would use.<br/>
|
||||
* Tolerates currency prefixes, commas, leading and trailing spaces.<p/>
|
||||
@ -245,7 +245,7 @@ public final class OperandResolver {
|
||||
return null;
|
||||
}
|
||||
// TODO - support notation like '1E3' (==1000)
|
||||
|
||||
|
||||
double val;
|
||||
try {
|
||||
val = Double.parseDouble(text);
|
||||
@ -254,7 +254,7 @@ public final class OperandResolver {
|
||||
}
|
||||
return new Double(isPositive ? +val : -val);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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>
|
||||
@ -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() + ")");
|
||||
}
|
||||
}
|
||||
|
@ -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 final class And extends BooleanFunction {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!atleastOneNonBlank) {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
if (retval == null) { // if no error
|
||||
retval = b ? BoolEval.TRUE : BoolEval.FALSE;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
protected boolean getInitialResultValue() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean partialEvaluate(boolean cumulativeResult, boolean currentValue) {
|
||||
return cumulativeResult && currentValue;
|
||||
}
|
||||
}
|
||||
|
@ -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 < amolweb at ya hoo dot com >
|
||||
* 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 < amolweb at ya hoo dot com >
|
||||
*/
|
||||
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);
|
||||
}
|
||||
else {
|
||||
retval = internalResolve(eval, stringsAreBlanks);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
private ValueEval internalResolve(Eval ve, boolean stringsAreBlanks) {
|
||||
ValueEval retval = null;
|
||||
|
||||
// blankeval is returned as is
|
||||
if (ve instanceof BlankEval) {
|
||||
retval = BlankEval.INSTANCE;
|
||||
}
|
||||
|
||||
// stringeval
|
||||
else if (ve instanceof StringEval) {
|
||||
retval = stringsAreBlanks ? (ValueEval) BlankEval.INSTANCE : (StringEval) ve;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
}
|
||||
public final Eval evaluate(Eval[] args, int srcRow, short srcCol) {
|
||||
if (args.length < 1) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
boolean boolResult;
|
||||
try {
|
||||
boolResult = calculate(args);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
return BoolEval.valueOf(boolResult);
|
||||
}
|
||||
|
||||
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() + ")");
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -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 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
public final class Not implements Function {
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
return BoolEval.valueOf(!boolArgVal);
|
||||
}
|
||||
}
|
||||
|
@ -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 final class Or extends BooleanFunction {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!atleastOneNonBlank) {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
if (retval == null) { // if no error
|
||||
retval = b ? BoolEval.TRUE : BoolEval.FALSE;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
protected boolean getInitialResultValue() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean partialEvaluate(boolean cumulativeResult, boolean currentValue) {
|
||||
return cumulativeResult || currentValue;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
// 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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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>.
|
||||
* @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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,6 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
|
||||
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()
|
||||
*
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class TestCountFuncs extends TestCase {
|
||||
|
||||
public TestCountFuncs(String testName) {
|
||||
super(testName);
|
||||
}
|
||||
|
||||
public void testCountA() {
|
||||
|
||||
|
||||
Eval[] args;
|
||||
|
||||
|
||||
args = new Eval[] {
|
||||
new NumberEval(0),
|
||||
new NumberEval(0),
|
||||
};
|
||||
confirmCountA(1, args);
|
||||
|
||||
|
||||
args = new Eval[] {
|
||||
new NumberEval(0),
|
||||
new NumberEval(0),
|
||||
new NumberEval(0),
|
||||
new StringEval(""),
|
||||
};
|
||||
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.createRefEval("A1"),
|
||||
EvalFactory.createAreaEval("A1:G6", 7, 6), // 42
|
||||
EvalFactory.createAreaEval("D1:F5", new ValueEval[15]),
|
||||
EvalFactory.createRefEval("A1"),
|
||||
EvalFactory.createAreaEval("A1:G6", new ValueEval[42]),
|
||||
new NumberEval(0),
|
||||
};
|
||||
confirmCountA(59, args);
|
||||
}
|
||||
|
||||
public void testCountIf() {
|
||||
|
||||
|
||||
AreaEval range;
|
||||
ValueEval[] values;
|
||||
|
||||
|
||||
// when criteria is a boolean value
|
||||
values = new ValueEval[] {
|
||||
new NumberEval(0),
|
||||
new NumberEval(0),
|
||||
new StringEval("TRUE"), // note - does not match boolean TRUE
|
||||
BoolEval.TRUE,
|
||||
BoolEval.FALSE,
|
||||
@ -98,22 +93,22 @@ public final class TestCountFuncs extends TestCase {
|
||||
};
|
||||
range = createAreaEval("A1:B3", values);
|
||||
confirmCountIf(2, range, BoolEval.TRUE);
|
||||
|
||||
|
||||
// when criteria is numeric
|
||||
values = new ValueEval[] {
|
||||
new NumberEval(0),
|
||||
new StringEval("2"),
|
||||
new StringEval("2.001"),
|
||||
new NumberEval(2),
|
||||
new NumberEval(2),
|
||||
new NumberEval(0),
|
||||
new StringEval("2"),
|
||||
new StringEval("2.001"),
|
||||
new NumberEval(2),
|
||||
new NumberEval(2),
|
||||
BoolEval.TRUE,
|
||||
};
|
||||
range = createAreaEval("A1:B3", values);
|
||||
confirmCountIf(3, range, new NumberEval(2));
|
||||
// note - same results when criteria is a string that parses as the number with the same value
|
||||
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)
|
||||
confirmCountIf(4, range, new StringEval(">1"));
|
||||
}
|
||||
@ -123,7 +118,7 @@ public final class TestCountFuncs extends TestCase {
|
||||
*/
|
||||
public void testCountIfWithCriteriaReference() {
|
||||
|
||||
ValueEval[] values = {
|
||||
ValueEval[] values = {
|
||||
new NumberEval(22),
|
||||
new NumberEval(25),
|
||||
new NumberEval(21),
|
||||
@ -132,14 +127,14 @@ public final class TestCountFuncs extends TestCase {
|
||||
new NumberEval(25),
|
||||
};
|
||||
Area2DEval arg0 = new Area2DEval(new AreaPtg("C1:C6"), values);
|
||||
|
||||
|
||||
Ref2DEval criteriaArg = new Ref2DEval(new RefPtg("A1"), new NumberEval(25));
|
||||
Eval[] args= { arg0, criteriaArg, };
|
||||
|
||||
|
||||
double actual = NumericFunctionInvoker.invoke(new Countif(), args);
|
||||
assertEquals(4, actual, 0D);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static AreaEval createAreaEval(String areaRefStr, ValueEval[] values) {
|
||||
return new Area2DEval(new AreaPtg(areaRefStr), values);
|
||||
@ -150,15 +145,15 @@ public final class TestCountFuncs extends TestCase {
|
||||
assertEquals(expected, result, 0);
|
||||
}
|
||||
private static void confirmCountIf(int expected, AreaEval range, Eval criteria) {
|
||||
|
||||
|
||||
Eval[] args = { range, criteria, };
|
||||
double result = NumericFunctionInvoker.invoke(new Countif(), args);
|
||||
assertEquals(expected, result, 0);
|
||||
}
|
||||
|
||||
|
||||
public void testCountIfEmptyStringCriteria() {
|
||||
I_MatchPredicate mp;
|
||||
|
||||
|
||||
// pred '=' matches blank cell but not empty string
|
||||
mp = Countif.createCriteriaPredicate(new StringEval("="));
|
||||
confirmPredicate(false, mp, "");
|
||||
@ -168,21 +163,21 @@ public final class TestCountFuncs extends TestCase {
|
||||
mp = Countif.createCriteriaPredicate(new StringEval(""));
|
||||
confirmPredicate(true, mp, "");
|
||||
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("<>"));
|
||||
confirmPredicate(false, mp, null);
|
||||
confirmPredicate(true, mp, "");
|
||||
}
|
||||
|
||||
|
||||
public void testCountifComparisons() {
|
||||
I_MatchPredicate mp;
|
||||
|
||||
|
||||
mp = Countif.createCriteriaPredicate(new StringEval(">5"));
|
||||
confirmPredicate(false, mp, 4);
|
||||
confirmPredicate(false, mp, 5);
|
||||
confirmPredicate(true, mp, 6);
|
||||
|
||||
|
||||
mp = Countif.createCriteriaPredicate(new StringEval("<=5"));
|
||||
confirmPredicate(true, mp, 4);
|
||||
confirmPredicate(true, mp, 5);
|
||||
@ -194,7 +189,7 @@ public final class TestCountFuncs extends TestCase {
|
||||
|
||||
mp = Countif.createCriteriaPredicate(new StringEval("=abc"));
|
||||
confirmPredicate(true, mp, "abc");
|
||||
|
||||
|
||||
mp = Countif.createCriteriaPredicate(new StringEval("=42"));
|
||||
confirmPredicate(false, mp, 41);
|
||||
confirmPredicate(true, mp, 42);
|
||||
@ -211,28 +206,28 @@ public final class TestCountFuncs extends TestCase {
|
||||
confirmPredicate(true, mp, "500");
|
||||
confirmPredicate(true, mp, "4t4");
|
||||
}
|
||||
|
||||
|
||||
public void testWildCards() {
|
||||
I_MatchPredicate mp;
|
||||
|
||||
|
||||
mp = Countif.createCriteriaPredicate(new StringEval("a*b"));
|
||||
confirmPredicate(false, mp, "abc");
|
||||
confirmPredicate(true, mp, "ab");
|
||||
confirmPredicate(true, mp, "axxb");
|
||||
confirmPredicate(false, mp, "xab");
|
||||
|
||||
|
||||
mp = Countif.createCriteriaPredicate(new StringEval("a?b"));
|
||||
confirmPredicate(false, mp, "abc");
|
||||
confirmPredicate(false, mp, "ab");
|
||||
confirmPredicate(false, mp, "axxb");
|
||||
confirmPredicate(false, mp, "xab");
|
||||
confirmPredicate(true, mp, "axb");
|
||||
|
||||
|
||||
mp = Countif.createCriteriaPredicate(new StringEval("a~?"));
|
||||
confirmPredicate(false, mp, "a~a");
|
||||
confirmPredicate(false, mp, "a~?");
|
||||
confirmPredicate(true, mp, "a?");
|
||||
|
||||
|
||||
mp = Countif.createCriteriaPredicate(new StringEval("~*a"));
|
||||
confirmPredicate(false, mp, "~aa");
|
||||
confirmPredicate(false, mp, "~*a");
|
||||
@ -245,40 +240,40 @@ public final class TestCountFuncs extends TestCase {
|
||||
}
|
||||
public void testNotQuiteWildCards() {
|
||||
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"));
|
||||
confirmPredicate(false, mp, "aab");
|
||||
confirmPredicate(true, mp, "a.b");
|
||||
|
||||
|
||||
|
||||
mp = Countif.createCriteriaPredicate(new StringEval("a~b"));
|
||||
confirmPredicate(false, mp, "ab");
|
||||
confirmPredicate(false, mp, "axb");
|
||||
confirmPredicate(false, mp, "a~~b");
|
||||
confirmPredicate(true, mp, "a~b");
|
||||
|
||||
|
||||
mp = Countif.createCriteriaPredicate(new StringEval(">a*b"));
|
||||
confirmPredicate(false, mp, "a(b");
|
||||
confirmPredicate(true, mp, "aab");
|
||||
confirmPredicate(false, mp, "a*a");
|
||||
confirmPredicate(true, mp, "a*c");
|
||||
}
|
||||
|
||||
|
||||
private static void confirmPredicate(boolean expectedResult, I_MatchPredicate matchPredicate, int value) {
|
||||
assertEquals(expectedResult, matchPredicate.matches(new NumberEval(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));
|
||||
}
|
||||
|
||||
|
||||
public void testCountifFromSpreadsheet() {
|
||||
final String FILE_NAME = "countifExamples.xls";
|
||||
final int START_ROW_IX = 1;
|
||||
final int COL_IX_ACTUAL = 2;
|
||||
final int COL_IX_EXPECTED = 3;
|
||||
|
||||
|
||||
int failureCount = 0;
|
||||
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook(FILE_NAME);
|
||||
HSSFSheet sheet = wb.getSheetAt(0);
|
||||
@ -299,7 +294,7 @@ public final class TestCountFuncs extends TestCase {
|
||||
failureCount++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (failureCount > 0) {
|
||||
throw new AssertionFailedError(failureCount + " countif evaluations failed. See stderr for more details");
|
||||
}
|
||||
|
@ -14,13 +14,13 @@
|
||||
See the License for the specific language governing permissions and
|
||||
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);
|
||||
|
@ -49,7 +49,7 @@ public final class TestSumproduct extends TestCase {
|
||||
}
|
||||
|
||||
public void testScalarSimple() {
|
||||
|
||||
|
||||
RefEval refEval = new Ref2DEval(new RefPtg("A1"), new NumberEval(3));
|
||||
Eval[] args = {
|
||||
refEval,
|
||||
@ -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);
|
||||
@ -82,10 +82,9 @@ public final class TestSumproduct extends TestCase {
|
||||
* For scalar products, the terms may be 1x1 area refs
|
||||
*/
|
||||
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,
|
||||
new NumberEval(2),
|
||||
@ -94,27 +93,29 @@ 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, };
|
||||
assertEquals(ErrorEval.VALUE_INVALID, invokeSumproduct(args));
|
||||
|
||||
|
||||
args = new Eval[] { aeA, new NumberEval(5), };
|
||||
assertEquals(ErrorEval.VALUE_INVALID, invokeSumproduct(args));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user