Lines 17-29
Link Here
|
17 |
|
17 |
|
18 |
package org.apache.poi.ss.formula.functions; |
18 |
package org.apache.poi.ss.formula.functions; |
19 |
|
19 |
|
20 |
import org.apache.poi.ss.formula.TwoDEval; |
20 |
import org.apache.poi.ss.formula.eval.AreaEval; |
21 |
import org.apache.poi.ss.formula.eval.BlankEval; |
21 |
import org.apache.poi.ss.formula.eval.BlankEval; |
22 |
import org.apache.poi.ss.formula.eval.ErrorEval; |
22 |
import org.apache.poi.ss.formula.eval.ErrorEval; |
23 |
import org.apache.poi.ss.formula.eval.EvaluationException; |
23 |
import org.apache.poi.ss.formula.eval.EvaluationException; |
24 |
import org.apache.poi.ss.formula.eval.NotImplementedException; |
24 |
import org.apache.poi.ss.formula.eval.NotImplementedException; |
25 |
import org.apache.poi.ss.formula.eval.NumericValueEval; |
25 |
import org.apache.poi.ss.formula.eval.NumericValueEval; |
26 |
import org.apache.poi.ss.formula.eval.RefEval; |
26 |
import org.apache.poi.ss.formula.eval.OperandResolver; |
27 |
import org.apache.poi.ss.formula.eval.StringEval; |
27 |
import org.apache.poi.ss.formula.eval.StringEval; |
28 |
import org.apache.poi.ss.formula.eval.StringValueEval; |
28 |
import org.apache.poi.ss.formula.eval.StringValueEval; |
29 |
import org.apache.poi.ss.formula.eval.ValueEval; |
29 |
import org.apache.poi.ss.formula.eval.ValueEval; |
Lines 62-72
public final class DStarRunner implements Function3Arg {
Link Here
|
62 |
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, |
62 |
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, |
63 |
ValueEval database, ValueEval filterColumn, ValueEval conditionDatabase) { |
63 |
ValueEval database, ValueEval filterColumn, ValueEval conditionDatabase) { |
64 |
// Input processing and error checks. |
64 |
// Input processing and error checks. |
65 |
if(!(database instanceof TwoDEval) || !(conditionDatabase instanceof TwoDEval)) { |
65 |
if(!(database instanceof AreaEval) || !(conditionDatabase instanceof AreaEval)) { |
66 |
return ErrorEval.VALUE_INVALID; |
66 |
return ErrorEval.VALUE_INVALID; |
67 |
} |
67 |
} |
68 |
TwoDEval db = (TwoDEval)database; |
68 |
AreaEval db = (AreaEval)database; |
69 |
TwoDEval cdb = (TwoDEval)conditionDatabase; |
69 |
AreaEval cdb = (AreaEval)conditionDatabase; |
|
|
70 |
|
71 |
try { |
72 |
filterColumn = OperandResolver.getSingleValue(filterColumn, srcRowIndex, srcColumnIndex); |
73 |
} catch (EvaluationException e) { |
74 |
return e.getErrorEval(); |
75 |
} |
70 |
|
76 |
|
71 |
int fc; |
77 |
int fc; |
72 |
try { |
78 |
try { |
Lines 100-114
public final class DStarRunner implements Function3Arg {
Link Here
|
100 |
} |
106 |
} |
101 |
// Filter each entry. |
107 |
// Filter each entry. |
102 |
if(matches) { |
108 |
if(matches) { |
103 |
try { |
109 |
ValueEval currentValueEval = resolveReference(db, row, fc); |
104 |
ValueEval currentValueEval = solveReference(db.getValue(row, fc)); |
110 |
// Pass the match to the algorithm and conditionally abort the search. |
105 |
// Pass the match to the algorithm and conditionally abort the search. |
111 |
boolean shouldContinue = algorithm.processMatch(currentValueEval); |
106 |
boolean shouldContinue = algorithm.processMatch(currentValueEval); |
112 |
if(! shouldContinue) { |
107 |
if(! shouldContinue) { |
113 |
break; |
108 |
break; |
|
|
109 |
} |
110 |
} catch (EvaluationException e) { |
111 |
return e.getErrorEval(); |
112 |
} |
114 |
} |
113 |
} |
115 |
} |
114 |
} |
116 |
} |
Lines 126-181
public final class DStarRunner implements Function3Arg {
Link Here
|
126 |
} |
128 |
} |
127 |
|
129 |
|
128 |
/** |
130 |
/** |
129 |
* Resolve reference(-chains) until we have a normal value. |
131 |
* |
130 |
* |
132 |
* |
131 |
* @param field a ValueEval which can be a RefEval. |
133 |
* @param nameValueEval Must not be a RefEval or AreaEval. Thus make sure resolveReference() is called on the value first! |
132 |
* @return a ValueEval which is guaranteed not to be a RefEval |
|
|
133 |
* @throws EvaluationException If a multi-sheet reference was found along the way. |
134 |
*/ |
135 |
private static ValueEval solveReference(ValueEval field) throws EvaluationException { |
136 |
if (field instanceof RefEval) { |
137 |
RefEval refEval = (RefEval)field; |
138 |
if (refEval.getNumberOfSheets() > 1) { |
139 |
throw new EvaluationException(ErrorEval.VALUE_INVALID); |
140 |
} |
141 |
return solveReference(refEval.getInnerValueEval(refEval.getFirstSheetIndex())); |
142 |
} |
143 |
else { |
144 |
return field; |
145 |
} |
146 |
} |
147 |
|
148 |
/** |
149 |
* Returns the first column index that matches the given name. The name can either be |
150 |
* a string or an integer, when it's an integer, then the respective column |
151 |
* (1 based index) is returned. |
152 |
* @param nameValueEval |
153 |
* @param db |
134 |
* @param db |
154 |
* @return the first column index that matches the given name (or int) |
135 |
* @return |
155 |
* @throws EvaluationException |
136 |
* @throws EvaluationException |
156 |
*/ |
137 |
*/ |
157 |
@SuppressWarnings("unused") |
138 |
private static int getColumnForName(ValueEval nameValueEval, AreaEval db) |
158 |
private static int getColumnForTag(ValueEval nameValueEval, TwoDEval db) |
|
|
159 |
throws EvaluationException { |
160 |
int resultColumn = -1; |
161 |
|
162 |
// Numbers as column indicator are allowed, check that. |
163 |
if(nameValueEval instanceof NumericValueEval) { |
164 |
double doubleResultColumn = ((NumericValueEval)nameValueEval).getNumberValue(); |
165 |
resultColumn = (int)doubleResultColumn; |
166 |
// Floating comparisions are usually not possible, but should work for 0.0. |
167 |
if(doubleResultColumn - resultColumn != 0.0) |
168 |
throw new EvaluationException(ErrorEval.VALUE_INVALID); |
169 |
resultColumn -= 1; // Numbers are 1-based not 0-based. |
170 |
} else { |
171 |
resultColumn = getColumnForName(nameValueEval, db); |
172 |
} |
173 |
return resultColumn; |
174 |
} |
175 |
|
176 |
private static int getColumnForName(ValueEval nameValueEval, TwoDEval db) |
177 |
throws EvaluationException { |
139 |
throws EvaluationException { |
178 |
String name = getStringFromValueEval(nameValueEval); |
140 |
String name = OperandResolver.coerceValueToString(nameValueEval); |
179 |
return getColumnForString(db, name); |
141 |
return getColumnForString(db, name); |
180 |
} |
142 |
} |
181 |
|
143 |
|
Lines 187-202
public final class DStarRunner implements Function3Arg {
Link Here
|
187 |
* @return Corresponding column number. |
149 |
* @return Corresponding column number. |
188 |
* @throws EvaluationException If it's not possible to turn all headings into strings. |
150 |
* @throws EvaluationException If it's not possible to turn all headings into strings. |
189 |
*/ |
151 |
*/ |
190 |
private static int getColumnForString(TwoDEval db,String name) |
152 |
private static int getColumnForString(AreaEval db,String name) |
191 |
throws EvaluationException { |
153 |
throws EvaluationException { |
192 |
int resultColumn = -1; |
154 |
int resultColumn = -1; |
193 |
final int width = db.getWidth(); |
155 |
final int width = db.getWidth(); |
194 |
for(int column = 0; column < width; ++column) { |
156 |
for(int column = 0; column < width; ++column) { |
195 |
ValueEval columnNameValueEval = db.getValue(0, column); |
157 |
ValueEval columnNameValueEval = resolveReference(db, 0, column); |
196 |
if(solveReference(columnNameValueEval) instanceof BlankEval) { |
158 |
if(columnNameValueEval instanceof BlankEval) { |
197 |
continue; |
159 |
continue; |
198 |
} |
160 |
} |
199 |
String columnName = getStringFromValueEval(columnNameValueEval); |
161 |
if(columnNameValueEval instanceof ErrorEval) { |
|
|
162 |
continue; |
163 |
} |
164 |
String columnName = OperandResolver.coerceValueToString(columnNameValueEval); |
200 |
if(name.equals(columnName)) { |
165 |
if(name.equals(columnName)) { |
201 |
resultColumn = column; |
166 |
resultColumn = column; |
202 |
break; |
167 |
break; |
Lines 215-221
public final class DStarRunner implements Function3Arg {
Link Here
|
215 |
* @throws EvaluationException If references could not be resolved or comparison |
180 |
* @throws EvaluationException If references could not be resolved or comparison |
216 |
* operators and operands didn't match. |
181 |
* operators and operands didn't match. |
217 |
*/ |
182 |
*/ |
218 |
private static boolean fullfillsConditions(TwoDEval db, int row, TwoDEval cdb) |
183 |
private static boolean fullfillsConditions(AreaEval db, int row, AreaEval cdb) |
219 |
throws EvaluationException { |
184 |
throws EvaluationException { |
220 |
// Only one row must match to accept the input, so rows are ORed. |
185 |
// Only one row must match to accept the input, so rows are ORed. |
221 |
// Each row is made up of cells where each cell is a condition, |
186 |
// Each row is made up of cells where each cell is a condition, |
Lines 229-248
public final class DStarRunner implements Function3Arg {
Link Here
|
229 |
// special column that accepts formulas. |
194 |
// special column that accepts formulas. |
230 |
boolean columnCondition = true; |
195 |
boolean columnCondition = true; |
231 |
ValueEval condition = null; |
196 |
ValueEval condition = null; |
232 |
try { |
197 |
|
233 |
// The condition to apply. |
198 |
// The condition to apply. |
234 |
condition = solveReference(cdb.getValue(conditionRow, column)); |
199 |
condition = resolveReference(cdb, conditionRow, column); |
235 |
} catch (java.lang.RuntimeException e) { |
200 |
|
236 |
// It might be a special formula, then it is ok if it fails. |
|
|
237 |
columnCondition = false; |
238 |
} |
239 |
// If the condition is empty it matches. |
201 |
// If the condition is empty it matches. |
240 |
if(condition instanceof BlankEval) |
202 |
if(condition instanceof BlankEval) |
241 |
continue; |
203 |
continue; |
242 |
// The column in the DB to apply the condition to. |
204 |
// The column in the DB to apply the condition to. |
243 |
ValueEval targetHeader = solveReference(cdb.getValue(0, column)); |
205 |
ValueEval targetHeader = resolveReference(cdb, 0, column); |
244 |
targetHeader = solveReference(targetHeader); |
|
|
245 |
|
246 |
|
206 |
|
247 |
if(!(targetHeader instanceof StringValueEval)) { |
207 |
if(!(targetHeader instanceof StringValueEval)) { |
248 |
throw new EvaluationException(ErrorEval.VALUE_INVALID); |
208 |
throw new EvaluationException(ErrorEval.VALUE_INVALID); |
Lines 254-267
public final class DStarRunner implements Function3Arg {
Link Here
|
254 |
|
214 |
|
255 |
if(columnCondition == true) { // normal column condition |
215 |
if(columnCondition == true) { // normal column condition |
256 |
// Should not throw, checked above. |
216 |
// Should not throw, checked above. |
257 |
ValueEval value = db.getValue( |
217 |
ValueEval value = resolveReference(db, row, getColumnForName(targetHeader, db)); |
258 |
row, getColumnForName(targetHeader, db)); |
|
|
259 |
if(!testNormalCondition(value, condition)) { |
218 |
if(!testNormalCondition(value, condition)) { |
260 |
matches = false; |
219 |
matches = false; |
261 |
break; |
220 |
break; |
262 |
} |
221 |
} |
263 |
} else { // It's a special formula condition. |
222 |
} else { // It's a special formula condition. |
264 |
if(getStringFromValueEval(condition).isEmpty()) { |
223 |
// TODO: Check whether the condition cell contains a formula and return #VALUE! if it doesn't. |
|
|
224 |
if(OperandResolver.coerceValueToString(condition).isEmpty()) { |
265 |
throw new EvaluationException(ErrorEval.VALUE_INVALID); |
225 |
throw new EvaluationException(ErrorEval.VALUE_INVALID); |
266 |
} |
226 |
} |
267 |
throw new NotImplementedException( |
227 |
throw new NotImplementedException( |
Lines 328-334
public final class DStarRunner implements Function3Arg {
Link Here
|
328 |
if(itsANumber) { |
288 |
if(itsANumber) { |
329 |
return testNumericCondition(value, operator.equal, stringOrNumber); |
289 |
return testNumericCondition(value, operator.equal, stringOrNumber); |
330 |
} else { // It's a string. |
290 |
} else { // It's a string. |
331 |
String valueString = value instanceof BlankEval ? "" : getStringFromValueEval(value); |
291 |
String valueString = value instanceof BlankEval ? "" : OperandResolver.coerceValueToString(value); |
332 |
return stringOrNumber.equals(valueString); |
292 |
return stringOrNumber.equals(valueString); |
333 |
} |
293 |
} |
334 |
} else { // It's a text starts-with condition. |
294 |
} else { // It's a text starts-with condition. |
Lines 336-342
public final class DStarRunner implements Function3Arg {
Link Here
|
336 |
return value instanceof StringEval; |
296 |
return value instanceof StringEval; |
337 |
} |
297 |
} |
338 |
else { |
298 |
else { |
339 |
String valueString = value instanceof BlankEval ? "" : getStringFromValueEval(value); |
299 |
String valueString = value instanceof BlankEval ? "" : OperandResolver.coerceValueToString(value); |
340 |
return valueString.startsWith(conditionString); |
300 |
return valueString.startsWith(conditionString); |
341 |
} |
301 |
} |
342 |
} |
302 |
} |
Lines 424-443
public final class DStarRunner implements Function3Arg {
Link Here
|
424 |
return null; |
384 |
return null; |
425 |
} |
385 |
} |
426 |
} |
386 |
} |
427 |
|
387 |
|
428 |
/** |
388 |
/** |
429 |
* Takes a ValueEval and tries to retrieve a String value from it. |
389 |
* Resolve a ValueEval that's in an AreaEval. |
430 |
* It tries to resolve references if there are any. |
|
|
431 |
* |
390 |
* |
432 |
* @param value ValueEval to retrieve the string from. |
391 |
* @param db AreaEval from which the cell to resolve is retrieved. |
433 |
* @return String corresponding to the given ValueEval. |
392 |
* @param dbRow Relative row in the AreaEval. |
434 |
* @throws EvaluationException If it's not possible to retrieve a String value. |
393 |
* @param dbCol Relative column in the AreaEval. |
|
|
394 |
* @return A ValueEval that is a NumberEval, StringEval, BoolEval, BlankEval or ErrorEval. |
435 |
*/ |
395 |
*/ |
436 |
private static String getStringFromValueEval(ValueEval value) |
396 |
private static ValueEval resolveReference(AreaEval db, int dbRow, int dbCol) { |
437 |
throws EvaluationException { |
397 |
try { |
438 |
value = solveReference(value); |
398 |
return OperandResolver.getSingleValue(db.getValue(dbRow, dbCol), db.getFirstRow()+dbRow, db.getFirstColumn()+dbCol); |
439 |
if(!(value instanceof StringValueEval)) |
399 |
} catch (EvaluationException e) { |
440 |
throw new EvaluationException(ErrorEval.VALUE_INVALID); |
400 |
return e.getErrorEval(); |
441 |
return ((StringValueEval)value).getStringValue(); |
401 |
} |
442 |
} |
402 |
} |
443 |
} |
403 |
} |