Removing calls to AreaEval.getValues()

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

View File

@ -30,46 +30,46 @@ public interface AreaEval extends ValueEval {
* returns the 0-based index of the first row in * 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
@ -80,7 +80,7 @@ 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
@ -89,17 +89,21 @@ public interface AreaEval extends ValueEval {
* @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

@ -149,7 +149,7 @@ public final class OperandResolver {
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();
@ -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;
import org.apache.poi.hssf.record.formula.eval.StringEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
public class And extends BooleanFunction {
public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
ValueEval retval = null;
boolean b = true;
boolean atleastOneNonBlank = false;
/*
* Note: do not abort the loop if b is false, since we could be
* dealing with errorevals later.
*/ */
outer: public final class And extends BooleanFunction {
for (int i=0, iSize=operands.length; i<iSize; i++) {
if (operands[i] instanceof AreaEval) { protected boolean getInitialResultValue() {
AreaEval ae = (AreaEval) operands[i]; return true;
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) { protected boolean partialEvaluate(boolean cumulativeResult, boolean currentValue) {
retval = ErrorEval.VALUE_INVALID; return cumulativeResult && currentValue;
} }
if (retval == null) { // if no error
retval = b ? BoolEval.TRUE : BoolEval.FALSE;
}
return retval;
}
} }

View File

@ -1,100 +1,108 @@
/* /* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one or more 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;
ValueEval ve = re.getInnerValueEval();
retval = internalResolve(ve, true);
} }
else { boolean boolResult;
retval = internalResolve(eval, stringsAreBlanks); try {
boolResult = calculate(args);
} catch (EvaluationException e) {
return e.getErrorEval();
}
return BoolEval.valueOf(boolResult);
} }
return retval; private boolean calculate(Eval[] args) throws EvaluationException {
boolean result = getInitialResultValue();
boolean atleastOneNonBlank = false;
/*
* Note: no short-circuit boolean loop exit because any ErrorEvals will override the result
*/
for (int i=0, iSize=args.length; i<iSize; i++) {
Eval arg = args[i];
if (arg instanceof AreaEval) {
AreaEval ae = (AreaEval) arg;
int height = ae.getHeight();
int width = ae.getWidth();
for (int rrIx=0; rrIx<height; rrIx++) {
for (int rcIx=0; rcIx<width; rcIx++) {
ValueEval ve = ae.getRelativeValue(rrIx, rcIx);
Boolean tempVe = OperandResolver.coerceValueToBoolean(ve, true);
if (tempVe != null) {
result = partialEvaluate(result, tempVe.booleanValue());
atleastOneNonBlank = true;
}
}
}
continue;
}
Boolean tempVe;
if (arg instanceof RefEval) {
ValueEval ve = ((RefEval) arg).getInnerValueEval();
tempVe = OperandResolver.coerceValueToBoolean(ve, true);
} else if (arg instanceof ValueEval) {
ValueEval ve = (ValueEval) arg;
tempVe = OperandResolver.coerceValueToBoolean(ve, false);
} else {
throw new RuntimeException("Unexpected eval (" + arg.getClass().getName() + ")");
} }
private ValueEval internalResolve(Eval ve, boolean stringsAreBlanks) {
ValueEval retval = null;
// blankeval is returned as is if (tempVe != null) {
if (ve instanceof BlankEval) { result = partialEvaluate(result, tempVe.booleanValue());
retval = BlankEval.INSTANCE; atleastOneNonBlank = true;
}
} }
// stringeval if (!atleastOneNonBlank) {
else if (ve instanceof StringEval) { throw new EvaluationException(ErrorEval.VALUE_INVALID);
retval = stringsAreBlanks ? (ValueEval) BlankEval.INSTANCE : (StringEval) ve; }
return result;
} }
// bools are bools :)
else if (ve instanceof BoolEval) {
retval = (BoolEval) ve;
}
// convert numbers to bool protected abstract boolean getInitialResultValue();
else if (ve instanceof NumericValueEval) { protected abstract boolean partialEvaluate(boolean cumulativeResult, boolean currentValue);
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;
}
} }

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;
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;
}
} }
boolean boolArgVal;
try {
ValueEval ve = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
Boolean b = OperandResolver.coerceValueToBoolean(ve, false);
boolArgVal = b == null ? false : b.booleanValue();
} catch (EvaluationException e) {
return e.getErrorEval();
} }
if (retval == null) { // if no error return BoolEval.valueOf(!boolArgVal);
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;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
public class Or extends BooleanFunction {
public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
ValueEval retval = null;
boolean b = false;
boolean atleastOneNonBlank = false;
/*
* Note: do not abort the loop if b is true, since we could be
* dealing with errorevals later.
*/ */
outer: public final class Or extends BooleanFunction {
for (int i=0, iSize=operands.length; i<iSize; i++) {
if (operands[i] instanceof AreaEval) { protected boolean getInitialResultValue() {
AreaEval ae = (AreaEval) operands[i]; return false;
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) { protected boolean partialEvaluate(boolean cumulativeResult, boolean currentValue) {
retval = ErrorEval.VALUE_INVALID; return cumulativeResult || currentValue;
} }
if (retval == null) { // if no error
retval = b ? BoolEval.TRUE : BoolEval.FALSE;
}
return retval;
}
} }

View File

@ -22,6 +22,7 @@ import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.BlankEval; import org.apache.poi.hssf.record.formula.eval.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);
}
}
}
}
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 // check that height and width match
if(areaEval.getLastRow() - fr + 1 != height) { if(areaEval.getHeight() != height) {
throw new EvalEx(ErrorEval.VALUE_INVALID); return false;
} }
if(areaEval.getLastColumn() - fc + 1 != width) { if(areaEval.getWidth() != width) {
throw new EvalEx(ErrorEval.VALUE_INVALID); return false;
}
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; 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

@ -15,7 +15,6 @@
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.AssertionFailedError; import junit.framework.AssertionFailedError;
@ -48,10 +47,6 @@ import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue;
*/ */
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;
@ -69,14 +64,14 @@ public final class TestCountFuncs extends TestCase {
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);

View File

@ -15,12 +15,12 @@
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

@ -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);
@ -83,8 +83,7 @@ public final class TestSumproduct extends TestCase {
*/ */
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,
@ -94,11 +93,10 @@ 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, };
@ -109,12 +107,15 @@ public final class TestSumproduct extends TestCase {
} }
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));
} }
} }