Patches from Patrick Zimmermann from bugs #60130 and #60131 - DGET fix for empty cells and D* coding improvements
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1760717 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
6ed8aaada6
commit
5fc5680d15
@ -17,7 +17,10 @@
|
|||||||
|
|
||||||
package org.apache.poi.ss.formula.functions;
|
package org.apache.poi.ss.formula.functions;
|
||||||
|
|
||||||
|
import org.apache.poi.ss.formula.eval.BlankEval;
|
||||||
import org.apache.poi.ss.formula.eval.ErrorEval;
|
import org.apache.poi.ss.formula.eval.ErrorEval;
|
||||||
|
import org.apache.poi.ss.formula.eval.EvaluationException;
|
||||||
|
import org.apache.poi.ss.formula.eval.OperandResolver;
|
||||||
import org.apache.poi.ss.formula.eval.ValueEval;
|
import org.apache.poi.ss.formula.eval.ValueEval;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -46,8 +49,18 @@ public final class DGet implements IDStarAlgorithm {
|
|||||||
public ValueEval getResult() {
|
public ValueEval getResult() {
|
||||||
if(result == null) {
|
if(result == null) {
|
||||||
return ErrorEval.VALUE_INVALID;
|
return ErrorEval.VALUE_INVALID;
|
||||||
} else {
|
} else if(result instanceof BlankEval) {
|
||||||
return result;
|
return ErrorEval.VALUE_INVALID;
|
||||||
}
|
} else
|
||||||
|
try {
|
||||||
|
if(OperandResolver.coerceValueToString(OperandResolver.getSingleValue(result, 0, 0)).equals("")) {
|
||||||
|
return ErrorEval.VALUE_INVALID;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} catch (EvaluationException e) {
|
||||||
|
return e.getErrorEval();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,13 +17,13 @@
|
|||||||
|
|
||||||
package org.apache.poi.ss.formula.functions;
|
package org.apache.poi.ss.formula.functions;
|
||||||
|
|
||||||
import org.apache.poi.ss.formula.TwoDEval;
|
import org.apache.poi.ss.formula.eval.AreaEval;
|
||||||
import org.apache.poi.ss.formula.eval.BlankEval;
|
import org.apache.poi.ss.formula.eval.BlankEval;
|
||||||
import org.apache.poi.ss.formula.eval.ErrorEval;
|
import org.apache.poi.ss.formula.eval.ErrorEval;
|
||||||
import org.apache.poi.ss.formula.eval.EvaluationException;
|
import org.apache.poi.ss.formula.eval.EvaluationException;
|
||||||
import org.apache.poi.ss.formula.eval.NotImplementedException;
|
import org.apache.poi.ss.formula.eval.NotImplementedException;
|
||||||
import org.apache.poi.ss.formula.eval.NumericValueEval;
|
import org.apache.poi.ss.formula.eval.NumericValueEval;
|
||||||
import org.apache.poi.ss.formula.eval.RefEval;
|
import org.apache.poi.ss.formula.eval.OperandResolver;
|
||||||
import org.apache.poi.ss.formula.eval.StringEval;
|
import org.apache.poi.ss.formula.eval.StringEval;
|
||||||
import org.apache.poi.ss.formula.eval.StringValueEval;
|
import org.apache.poi.ss.formula.eval.StringValueEval;
|
||||||
import org.apache.poi.ss.formula.eval.ValueEval;
|
import org.apache.poi.ss.formula.eval.ValueEval;
|
||||||
@ -62,11 +62,17 @@ public final class DStarRunner implements Function3Arg {
|
|||||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex,
|
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex,
|
||||||
ValueEval database, ValueEval filterColumn, ValueEval conditionDatabase) {
|
ValueEval database, ValueEval filterColumn, ValueEval conditionDatabase) {
|
||||||
// Input processing and error checks.
|
// Input processing and error checks.
|
||||||
if(!(database instanceof TwoDEval) || !(conditionDatabase instanceof TwoDEval)) {
|
if(!(database instanceof AreaEval) || !(conditionDatabase instanceof AreaEval)) {
|
||||||
return ErrorEval.VALUE_INVALID;
|
return ErrorEval.VALUE_INVALID;
|
||||||
}
|
}
|
||||||
TwoDEval db = (TwoDEval)database;
|
AreaEval db = (AreaEval)database;
|
||||||
TwoDEval cdb = (TwoDEval)conditionDatabase;
|
AreaEval cdb = (AreaEval)conditionDatabase;
|
||||||
|
|
||||||
|
try {
|
||||||
|
filterColumn = OperandResolver.getSingleValue(filterColumn, srcRowIndex, srcColumnIndex);
|
||||||
|
} catch (EvaluationException e) {
|
||||||
|
return e.getErrorEval();
|
||||||
|
}
|
||||||
|
|
||||||
int fc;
|
int fc;
|
||||||
try {
|
try {
|
||||||
@ -100,15 +106,11 @@ public final class DStarRunner implements Function3Arg {
|
|||||||
}
|
}
|
||||||
// Filter each entry.
|
// Filter each entry.
|
||||||
if(matches) {
|
if(matches) {
|
||||||
try {
|
ValueEval currentValueEval = resolveReference(db, row, fc);
|
||||||
ValueEval currentValueEval = solveReference(db.getValue(row, fc));
|
// Pass the match to the algorithm and conditionally abort the search.
|
||||||
// Pass the match to the algorithm and conditionally abort the search.
|
boolean shouldContinue = algorithm.processMatch(currentValueEval);
|
||||||
boolean shouldContinue = algorithm.processMatch(currentValueEval);
|
if(! shouldContinue) {
|
||||||
if(! shouldContinue) {
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
} catch (EvaluationException e) {
|
|
||||||
return e.getErrorEval();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -126,56 +128,16 @@ public final class DStarRunner implements Function3Arg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve reference(-chains) until we have a normal value.
|
*
|
||||||
*
|
*
|
||||||
* @param field a ValueEval which can be a RefEval.
|
* @param nameValueEval Must not be a RefEval or AreaEval. Thus make sure resolveReference() is called on the value first!
|
||||||
* @return a ValueEval which is guaranteed not to be a RefEval
|
|
||||||
* @throws EvaluationException If a multi-sheet reference was found along the way.
|
|
||||||
*/
|
|
||||||
private static ValueEval solveReference(ValueEval field) throws EvaluationException {
|
|
||||||
if (field instanceof RefEval) {
|
|
||||||
RefEval refEval = (RefEval)field;
|
|
||||||
if (refEval.getNumberOfSheets() > 1) {
|
|
||||||
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
|
||||||
}
|
|
||||||
return solveReference(refEval.getInnerValueEval(refEval.getFirstSheetIndex()));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return field;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the first column index that matches the given name. The name can either be
|
|
||||||
* a string or an integer, when it's an integer, then the respective column
|
|
||||||
* (1 based index) is returned.
|
|
||||||
* @param nameValueEval
|
|
||||||
* @param db
|
* @param db
|
||||||
* @return the first column index that matches the given name (or int)
|
* @return
|
||||||
* @throws EvaluationException
|
* @throws EvaluationException
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
private static int getColumnForName(ValueEval nameValueEval, AreaEval db)
|
||||||
private static int getColumnForTag(ValueEval nameValueEval, TwoDEval db)
|
|
||||||
throws EvaluationException {
|
throws EvaluationException {
|
||||||
int resultColumn = -1;
|
String name = OperandResolver.coerceValueToString(nameValueEval);
|
||||||
|
|
||||||
// Numbers as column indicator are allowed, check that.
|
|
||||||
if(nameValueEval instanceof NumericValueEval) {
|
|
||||||
double doubleResultColumn = ((NumericValueEval)nameValueEval).getNumberValue();
|
|
||||||
resultColumn = (int)doubleResultColumn;
|
|
||||||
// Floating comparisions are usually not possible, but should work for 0.0.
|
|
||||||
if(doubleResultColumn - resultColumn != 0.0)
|
|
||||||
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
|
||||||
resultColumn -= 1; // Numbers are 1-based not 0-based.
|
|
||||||
} else {
|
|
||||||
resultColumn = getColumnForName(nameValueEval, db);
|
|
||||||
}
|
|
||||||
return resultColumn;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int getColumnForName(ValueEval nameValueEval, TwoDEval db)
|
|
||||||
throws EvaluationException {
|
|
||||||
String name = getStringFromValueEval(nameValueEval);
|
|
||||||
return getColumnForString(db, name);
|
return getColumnForString(db, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,16 +149,19 @@ public final class DStarRunner implements Function3Arg {
|
|||||||
* @return Corresponding column number.
|
* @return Corresponding column number.
|
||||||
* @throws EvaluationException If it's not possible to turn all headings into strings.
|
* @throws EvaluationException If it's not possible to turn all headings into strings.
|
||||||
*/
|
*/
|
||||||
private static int getColumnForString(TwoDEval db,String name)
|
private static int getColumnForString(AreaEval db,String name)
|
||||||
throws EvaluationException {
|
throws EvaluationException {
|
||||||
int resultColumn = -1;
|
int resultColumn = -1;
|
||||||
final int width = db.getWidth();
|
final int width = db.getWidth();
|
||||||
for(int column = 0; column < width; ++column) {
|
for(int column = 0; column < width; ++column) {
|
||||||
ValueEval columnNameValueEval = db.getValue(0, column);
|
ValueEval columnNameValueEval = resolveReference(db, 0, column);
|
||||||
if(solveReference(columnNameValueEval) instanceof BlankEval) {
|
if(columnNameValueEval instanceof BlankEval) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String columnName = getStringFromValueEval(columnNameValueEval);
|
if(columnNameValueEval instanceof ErrorEval) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String columnName = OperandResolver.coerceValueToString(columnNameValueEval);
|
||||||
if(name.equals(columnName)) {
|
if(name.equals(columnName)) {
|
||||||
resultColumn = column;
|
resultColumn = column;
|
||||||
break;
|
break;
|
||||||
@ -215,7 +180,7 @@ public final class DStarRunner implements Function3Arg {
|
|||||||
* @throws EvaluationException If references could not be resolved or comparison
|
* @throws EvaluationException If references could not be resolved or comparison
|
||||||
* operators and operands didn't match.
|
* operators and operands didn't match.
|
||||||
*/
|
*/
|
||||||
private static boolean fullfillsConditions(TwoDEval db, int row, TwoDEval cdb)
|
private static boolean fullfillsConditions(AreaEval db, int row, AreaEval cdb)
|
||||||
throws EvaluationException {
|
throws EvaluationException {
|
||||||
// Only one row must match to accept the input, so rows are ORed.
|
// Only one row must match to accept the input, so rows are ORed.
|
||||||
// Each row is made up of cells where each cell is a condition,
|
// Each row is made up of cells where each cell is a condition,
|
||||||
@ -229,20 +194,15 @@ public final class DStarRunner implements Function3Arg {
|
|||||||
// special column that accepts formulas.
|
// special column that accepts formulas.
|
||||||
boolean columnCondition = true;
|
boolean columnCondition = true;
|
||||||
ValueEval condition = null;
|
ValueEval condition = null;
|
||||||
try {
|
|
||||||
// The condition to apply.
|
// The condition to apply.
|
||||||
condition = solveReference(cdb.getValue(conditionRow, column));
|
condition = resolveReference(cdb, conditionRow, column);
|
||||||
} catch (java.lang.RuntimeException e) {
|
|
||||||
// It might be a special formula, then it is ok if it fails.
|
|
||||||
columnCondition = false;
|
|
||||||
}
|
|
||||||
// If the condition is empty it matches.
|
// If the condition is empty it matches.
|
||||||
if(condition instanceof BlankEval)
|
if(condition instanceof BlankEval)
|
||||||
continue;
|
continue;
|
||||||
// The column in the DB to apply the condition to.
|
// The column in the DB to apply the condition to.
|
||||||
ValueEval targetHeader = solveReference(cdb.getValue(0, column));
|
ValueEval targetHeader = resolveReference(cdb, 0, column);
|
||||||
targetHeader = solveReference(targetHeader);
|
|
||||||
|
|
||||||
|
|
||||||
if(!(targetHeader instanceof StringValueEval)) {
|
if(!(targetHeader instanceof StringValueEval)) {
|
||||||
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||||
@ -254,14 +214,14 @@ public final class DStarRunner implements Function3Arg {
|
|||||||
|
|
||||||
if(columnCondition == true) { // normal column condition
|
if(columnCondition == true) { // normal column condition
|
||||||
// Should not throw, checked above.
|
// Should not throw, checked above.
|
||||||
ValueEval value = db.getValue(
|
ValueEval value = resolveReference(db, row, getColumnForName(targetHeader, db));
|
||||||
row, getColumnForName(targetHeader, db));
|
|
||||||
if(!testNormalCondition(value, condition)) {
|
if(!testNormalCondition(value, condition)) {
|
||||||
matches = false;
|
matches = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else { // It's a special formula condition.
|
} else { // It's a special formula condition.
|
||||||
if(getStringFromValueEval(condition).isEmpty()) {
|
// TODO: Check whether the condition cell contains a formula and return #VALUE! if it doesn't.
|
||||||
|
if(OperandResolver.coerceValueToString(condition).isEmpty()) {
|
||||||
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||||
}
|
}
|
||||||
throw new NotImplementedException(
|
throw new NotImplementedException(
|
||||||
@ -328,7 +288,7 @@ public final class DStarRunner implements Function3Arg {
|
|||||||
if(itsANumber) {
|
if(itsANumber) {
|
||||||
return testNumericCondition(value, operator.equal, stringOrNumber);
|
return testNumericCondition(value, operator.equal, stringOrNumber);
|
||||||
} else { // It's a string.
|
} else { // It's a string.
|
||||||
String valueString = value instanceof BlankEval ? "" : getStringFromValueEval(value);
|
String valueString = value instanceof BlankEval ? "" : OperandResolver.coerceValueToString(value);
|
||||||
return stringOrNumber.equals(valueString);
|
return stringOrNumber.equals(valueString);
|
||||||
}
|
}
|
||||||
} else { // It's a text starts-with condition.
|
} else { // It's a text starts-with condition.
|
||||||
@ -336,7 +296,7 @@ public final class DStarRunner implements Function3Arg {
|
|||||||
return value instanceof StringEval;
|
return value instanceof StringEval;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
String valueString = value instanceof BlankEval ? "" : getStringFromValueEval(value);
|
String valueString = value instanceof BlankEval ? "" : OperandResolver.coerceValueToString(value);
|
||||||
return valueString.startsWith(conditionString);
|
return valueString.startsWith(conditionString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -424,20 +384,20 @@ public final class DStarRunner implements Function3Arg {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes a ValueEval and tries to retrieve a String value from it.
|
* Resolve a ValueEval that's in an AreaEval.
|
||||||
* It tries to resolve references if there are any.
|
|
||||||
*
|
*
|
||||||
* @param value ValueEval to retrieve the string from.
|
* @param db AreaEval from which the cell to resolve is retrieved.
|
||||||
* @return String corresponding to the given ValueEval.
|
* @param dbRow Relative row in the AreaEval.
|
||||||
* @throws EvaluationException If it's not possible to retrieve a String value.
|
* @param dbCol Relative column in the AreaEval.
|
||||||
|
* @return A ValueEval that is a NumberEval, StringEval, BoolEval, BlankEval or ErrorEval.
|
||||||
*/
|
*/
|
||||||
private static String getStringFromValueEval(ValueEval value)
|
private static ValueEval resolveReference(AreaEval db, int dbRow, int dbCol) {
|
||||||
throws EvaluationException {
|
try {
|
||||||
value = solveReference(value);
|
return OperandResolver.getSingleValue(db.getValue(dbRow, dbCol), db.getFirstRow()+dbRow, db.getFirstColumn()+dbCol);
|
||||||
if(!(value instanceof StringValueEval))
|
} catch (EvaluationException e) {
|
||||||
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
return e.getErrorEval();
|
||||||
return ((StringValueEval)value).getStringValue();
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user