git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1037436 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_8_BETA1
@@ -0,0 +1,164 @@ | |||
/* ==================================================================== | |||
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.ss.formula.atp; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import org.apache.poi.ss.formula.eval.ValueEval; | |||
import org.apache.poi.ss.formula.functions.FreeRefFunction; | |||
import org.apache.poi.ss.formula.udf.UDFFinder; | |||
import org.apache.poi.ss.formula.OperationEvaluationContext; | |||
import org.apache.poi.ss.formula.eval.NotImplementedException; | |||
/** | |||
* @author Josh Micich | |||
* @author Petr Udalau - systematized work of add-in libraries and user defined functions. | |||
*/ | |||
public final class AnalysisToolPak implements UDFFinder { | |||
public static final UDFFinder instance = new AnalysisToolPak(); | |||
private static final class NotImplemented implements FreeRefFunction { | |||
private final String _functionName; | |||
public NotImplemented(String functionName) { | |||
_functionName = functionName; | |||
} | |||
public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) { | |||
throw new NotImplementedException(_functionName); | |||
} | |||
}; | |||
private final Map<String, FreeRefFunction> _functionsByName = createFunctionsMap(); | |||
private AnalysisToolPak() { | |||
// enforce singleton | |||
} | |||
public FreeRefFunction findFunction(String name) { | |||
return _functionsByName.get(name); | |||
} | |||
private Map<String, FreeRefFunction> createFunctionsMap() { | |||
Map<String, FreeRefFunction> m = new HashMap<String, FreeRefFunction>(100); | |||
r(m, "ACCRINT", null); | |||
r(m, "ACCRINTM", null); | |||
r(m, "AMORDEGRC", null); | |||
r(m, "AMORLINC", null); | |||
r(m, "BESSELI", null); | |||
r(m, "BESSELJ", null); | |||
r(m, "BESSELK", null); | |||
r(m, "BESSELY", null); | |||
r(m, "BIN2DEC", null); | |||
r(m, "BIN2HEX", null); | |||
r(m, "BIN2OCT", null); | |||
r(m, "CO MPLEX", null); | |||
r(m, "CONVERT", null); | |||
r(m, "COUPDAYBS", null); | |||
r(m, "COUPDAYS", null); | |||
r(m, "COUPDAYSNC", null); | |||
r(m, "COUPNCD", null); | |||
r(m, "COUPNUM", null); | |||
r(m, "COUPPCD", null); | |||
r(m, "CUMIPMT", null); | |||
r(m, "CUMPRINC", null); | |||
r(m, "DEC2BIN", null); | |||
r(m, "DEC2HEX", null); | |||
r(m, "DEC2OCT", null); | |||
r(m, "DELTA", null); | |||
r(m, "DISC", null); | |||
r(m, "DOLLARDE", null); | |||
r(m, "DOLLARFR", null); | |||
r(m, "DURATION", null); | |||
r(m, "EDATE", null); | |||
r(m, "EFFECT", null); | |||
r(m, "EOMONTH", null); | |||
r(m, "ERF", null); | |||
r(m, "ERFC", null); | |||
r(m, "FACTDOUBLE", null); | |||
r(m, "FVSCHEDULE", null); | |||
r(m, "GCD", null); | |||
r(m, "GESTEP", null); | |||
r(m, "HEX2BIN", null); | |||
r(m, "HEX2DEC", null); | |||
r(m, "HEX2OCT", null); | |||
r(m, "IMABS", null); | |||
r(m, "IMAGINARY", null); | |||
r(m, "IMARGUMENT", null); | |||
r(m, "IMCONJUGATE", null); | |||
r(m, "IMCOS", null); | |||
r(m, "IMDIV", null); | |||
r(m, "IMEXP", null); | |||
r(m, "IMLN", null); | |||
r(m, "IMLOG10", null); | |||
r(m, "IMLOG2", null); | |||
r(m, "IMPOWER", null); | |||
r(m, "IMPRODUCT", null); | |||
r(m, "IMREAL", null); | |||
r(m, "IMSIN", null); | |||
r(m, "IMSQRT", null); | |||
r(m, "IMSUB", null); | |||
r(m, "IMSUM", null); | |||
r(m, "INTRATE", null); | |||
r(m, "ISEVEN", ParityFunction.IS_EVEN); | |||
r(m, "ISODD", ParityFunction.IS_ODD); | |||
r(m, "LCM", null); | |||
r(m, "MDURATION", null); | |||
r(m, "MROUND", null); | |||
r(m, "MULTINOMIAL", null); | |||
r(m, "NETWORKDAYS", null); | |||
r(m, "NOMINAL", null); | |||
r(m, "OCT2BIN", null); | |||
r(m, "OCT2DEC", null); | |||
r(m, "OCT2HEX", null); | |||
r(m, "ODDFPRICE", null); | |||
r(m, "ODDFYIELD", null); | |||
r(m, "ODDLPRICE", null); | |||
r(m, "ODDLYIELD", null); | |||
r(m, "PRICE", null); | |||
r(m, "PRICEDISC", null); | |||
r(m, "PRICEMAT", null); | |||
r(m, "QUOTIENT", null); | |||
r(m, "RANDBETWEEN", RandBetween.instance); | |||
r(m, "RECEIVED", null); | |||
r(m, "SERIESSUM", null); | |||
r(m, "SQRTPI", null); | |||
r(m, "TBILLEQ", null); | |||
r(m, "TBILLPRICE", null); | |||
r(m, "TBILLYIELD", null); | |||
r(m, "WEEKNUM", null); | |||
r(m, "WORKDAY", null); | |||
r(m, "XIRR", null); | |||
r(m, "XNPV", null); | |||
r(m, "YEARFRAC", YearFrac.instance); | |||
r(m, "YIELD", null); | |||
r(m, "YIELDDISC", null); | |||
r(m, "YIELDMAT", null); | |||
return m; | |||
} | |||
private static void r(Map<String, FreeRefFunction> m, String functionName, FreeRefFunction pFunc) { | |||
FreeRefFunction func = pFunc == null ? new NotImplemented(functionName) : pFunc; | |||
m.put(functionName, func); | |||
} | |||
} |
@@ -0,0 +1,67 @@ | |||
/* ==================================================================== | |||
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.ss.formula.atp; | |||
import org.apache.poi.ss.formula.eval.BoolEval; | |||
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.functions.FreeRefFunction; | |||
import org.apache.poi.ss.formula.OperationEvaluationContext; | |||
/** | |||
* Implementation of Excel 'Analysis ToolPak' function ISEVEN() ISODD()<br/> | |||
* | |||
* @author Josh Micich | |||
*/ | |||
final class ParityFunction implements FreeRefFunction { | |||
public static final FreeRefFunction IS_EVEN = new ParityFunction(0); | |||
public static final FreeRefFunction IS_ODD = new ParityFunction(1); | |||
private final int _desiredParity; | |||
private ParityFunction(int desiredParity) { | |||
_desiredParity = desiredParity; | |||
} | |||
public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) { | |||
if (args.length != 1) { | |||
return ErrorEval.VALUE_INVALID; | |||
} | |||
int val; | |||
try { | |||
val = evaluateArgParity(args[0], ec.getRowIndex(), ec.getColumnIndex()); | |||
} catch (EvaluationException e) { | |||
return e.getErrorEval(); | |||
} | |||
return BoolEval.valueOf(val == _desiredParity); | |||
} | |||
private static int evaluateArgParity(ValueEval arg, int srcCellRow, int srcCellCol) throws EvaluationException { | |||
ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, (short)srcCellCol); | |||
double d = OperandResolver.coerceValueToDouble(ve); | |||
if (d < 0) { | |||
d = -d; | |||
} | |||
long v = (long) Math.floor(d); | |||
return (int) (v & 0x0001); | |||
} | |||
} |
@@ -0,0 +1,84 @@ | |||
/* ==================================================================== | |||
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.ss.formula.atp; | |||
import org.apache.poi.ss.formula.eval.ErrorEval; | |||
import org.apache.poi.ss.formula.eval.EvaluationException; | |||
import org.apache.poi.ss.formula.eval.NumberEval; | |||
import org.apache.poi.ss.formula.eval.OperandResolver; | |||
import org.apache.poi.ss.formula.eval.ValueEval; | |||
import org.apache.poi.ss.formula.functions.FreeRefFunction; | |||
import org.apache.poi.ss.formula.OperationEvaluationContext; | |||
/** | |||
* Implementation of Excel 'Analysis ToolPak' function RANDBETWEEN()<br/> | |||
* | |||
* Returns a random integer number between the numbers you specify.<p/> | |||
* | |||
* <b>Syntax</b><br/> | |||
* <b>RANDBETWEEN</b>(<b>bottom</b>, <b>top</b>)<p/> | |||
* | |||
* <b>bottom</b> is the smallest integer RANDBETWEEN will return.<br/> | |||
* <b>top</b> is the largest integer RANDBETWEEN will return.<br/> | |||
* @author Brendan Nolan | |||
*/ | |||
final class RandBetween implements FreeRefFunction{ | |||
public static final FreeRefFunction instance = new RandBetween(); | |||
private RandBetween() { | |||
//enforces singleton | |||
} | |||
/** | |||
* Evaluate for RANDBETWEEN(). Must be given two arguments. Bottom must be greater than top. | |||
* Bottom is rounded up and top value is rounded down. After rounding top has to be set greater | |||
* than top. | |||
* | |||
* @see org.apache.poi.ss.formula.functions.FreeRefFunction#evaluate(org.apache.poi.ss.formula.eval.ValueEval[], org.apache.poi.ss.formula.OperationEvaluationContext) | |||
*/ | |||
public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) { | |||
double bottom, top; | |||
if (args.length != 2) { | |||
return ErrorEval.VALUE_INVALID; | |||
} | |||
try { | |||
bottom = OperandResolver.coerceValueToDouble(OperandResolver.getSingleValue(args[0], ec.getRowIndex(), ec.getColumnIndex())); | |||
top = OperandResolver.coerceValueToDouble(OperandResolver.getSingleValue(args[1], ec.getRowIndex(), ec.getColumnIndex())); | |||
if(bottom > top) { | |||
return ErrorEval.NUM_ERROR; | |||
} | |||
} catch (EvaluationException e) { | |||
return ErrorEval.VALUE_INVALID; | |||
} | |||
bottom = Math.ceil(bottom); | |||
top = Math.floor(top); | |||
if(bottom > top) { | |||
top = bottom; | |||
} | |||
return new NumberEval((bottom + (int)(Math.random() * ((top - bottom) + 1)))); | |||
} | |||
} |
@@ -0,0 +1,159 @@ | |||
/* ==================================================================== | |||
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.ss.formula.atp; | |||
import java.util.Calendar; | |||
import java.util.GregorianCalendar; | |||
import java.util.regex.Pattern; | |||
import org.apache.poi.ss.formula.eval.ErrorEval; | |||
import org.apache.poi.ss.formula.eval.EvaluationException; | |||
import org.apache.poi.ss.formula.eval.NumberEval; | |||
import org.apache.poi.ss.formula.eval.OperandResolver; | |||
import org.apache.poi.ss.formula.eval.StringEval; | |||
import org.apache.poi.ss.formula.eval.ValueEval; | |||
import org.apache.poi.ss.formula.functions.FreeRefFunction; | |||
import org.apache.poi.ss.formula.OperationEvaluationContext; | |||
import org.apache.poi.ss.usermodel.DateUtil; | |||
/** | |||
* Implementation of Excel 'Analysis ToolPak' function YEARFRAC()<br/> | |||
* | |||
* Returns the fraction of the year spanned by two dates.<p/> | |||
* | |||
* <b>Syntax</b><br/> | |||
* <b>YEARFRAC</b>(<b>startDate</b>, <b>endDate</b>, basis)<p/> | |||
* | |||
* The <b>basis</b> optionally specifies the behaviour of YEARFRAC as follows: | |||
* | |||
* <table border="0" cellpadding="1" cellspacing="0" summary="basis parameter description"> | |||
* <tr><th>Value</th><th>Days per Month</th><th>Days per Year</th></tr> | |||
* <tr align='center'><td>0 (default)</td><td>30</td><td>360</td></tr> | |||
* <tr align='center'><td>1</td><td>actual</td><td>actual</td></tr> | |||
* <tr align='center'><td>2</td><td>actual</td><td>360</td></tr> | |||
* <tr align='center'><td>3</td><td>actual</td><td>365</td></tr> | |||
* <tr align='center'><td>4</td><td>30</td><td>360</td></tr> | |||
* </table> | |||
* | |||
*/ | |||
final class YearFrac implements FreeRefFunction { | |||
public static final FreeRefFunction instance = new YearFrac(); | |||
private YearFrac() { | |||
// enforce singleton | |||
} | |||
public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) { | |||
int srcCellRow = ec.getRowIndex(); | |||
int srcCellCol = ec.getColumnIndex(); | |||
double result; | |||
try { | |||
int basis = 0; // default | |||
switch(args.length) { | |||
case 3: | |||
basis = evaluateIntArg(args[2], srcCellRow, srcCellCol); | |||
case 2: | |||
break; | |||
default: | |||
return ErrorEval.VALUE_INVALID; | |||
} | |||
double startDateVal = evaluateDateArg(args[0], srcCellRow, srcCellCol); | |||
double endDateVal = evaluateDateArg(args[1], srcCellRow, srcCellCol); | |||
result = YearFracCalculator.calculate(startDateVal, endDateVal, basis); | |||
} catch (EvaluationException e) { | |||
return e.getErrorEval(); | |||
} | |||
return new NumberEval(result); | |||
} | |||
private static double evaluateDateArg(ValueEval arg, int srcCellRow, int srcCellCol) throws EvaluationException { | |||
ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, (short) srcCellCol); | |||
if (ve instanceof StringEval) { | |||
String strVal = ((StringEval) ve).getStringValue(); | |||
Double dVal = OperandResolver.parseDouble(strVal); | |||
if (dVal != null) { | |||
return dVal.doubleValue(); | |||
} | |||
Calendar date = parseDate(strVal); | |||
return DateUtil.getExcelDate(date, false); | |||
} | |||
return OperandResolver.coerceValueToDouble(ve); | |||
} | |||
private static Calendar parseDate(String strVal) throws EvaluationException { | |||
String[] parts = Pattern.compile("/").split(strVal); | |||
if (parts.length != 3) { | |||
throw new EvaluationException(ErrorEval.VALUE_INVALID); | |||
} | |||
String part2 = parts[2]; | |||
int spacePos = part2.indexOf(' '); | |||
if (spacePos > 0) { | |||
// drop time portion if present | |||
part2 = part2.substring(0, spacePos); | |||
} | |||
int f0; | |||
int f1; | |||
int f2; | |||
try { | |||
f0 = Integer.parseInt(parts[0]); | |||
f1 = Integer.parseInt(parts[1]); | |||
f2 = Integer.parseInt(part2); | |||
} catch (NumberFormatException e) { | |||
throw new EvaluationException(ErrorEval.VALUE_INVALID); | |||
} | |||
if (f0<0 || f1<0 || f2<0 || (f0>12 && f1>12 && f2>12)) { | |||
// easy to see this cannot be a valid date | |||
throw new EvaluationException(ErrorEval.VALUE_INVALID); | |||
} | |||
if (f0 >= 1900 && f0 < 9999) { | |||
// when 4 digit value appears first, the format is YYYY/MM/DD, regardless of OS settings | |||
return makeDate(f0, f1, f2); | |||
} | |||
// otherwise the format seems to depend on OS settings (default date format) | |||
if (false) { | |||
// MM/DD/YYYY is probably a good guess, if the in the US | |||
return makeDate(f2, f0, f1); | |||
} | |||
// TODO - find a way to choose the correct date format | |||
throw new RuntimeException("Unable to determine date format for text '" + strVal + "'"); | |||
} | |||
/** | |||
* @param month 1-based | |||
*/ | |||
private static Calendar makeDate(int year, int month, int day) throws EvaluationException { | |||
if (month < 1 || month > 12) { | |||
throw new EvaluationException(ErrorEval.VALUE_INVALID); | |||
} | |||
Calendar cal = new GregorianCalendar(year, month-1, 1, 0, 0, 0); | |||
cal.set(Calendar.MILLISECOND, 0); | |||
if (day <1 || day>cal.getActualMaximum(Calendar.DAY_OF_MONTH)) { | |||
throw new EvaluationException(ErrorEval.VALUE_INVALID); | |||
} | |||
cal.set(Calendar.DAY_OF_MONTH, day); | |||
return cal; | |||
} | |||
private static int evaluateIntArg(ValueEval arg, int srcCellRow, int srcCellCol) throws EvaluationException { | |||
ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, (short) srcCellCol); | |||
return OperandResolver.coerceValueToInt(ve); | |||
} | |||
} |
@@ -0,0 +1,344 @@ | |||
/* ==================================================================== | |||
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.ss.formula.atp; | |||
import java.util.Calendar; | |||
import java.util.GregorianCalendar; | |||
import java.util.TimeZone; | |||
import org.apache.poi.ss.formula.eval.ErrorEval; | |||
import org.apache.poi.ss.formula.eval.EvaluationException; | |||
import org.apache.poi.ss.usermodel.DateUtil; | |||
/** | |||
* Internal calculation methods for Excel 'Analysis ToolPak' function YEARFRAC()<br/> | |||
* | |||
* Algorithm inspired by www.dwheeler.com/yearfrac | |||
* | |||
* @author Josh Micich | |||
*/ | |||
final class YearFracCalculator { | |||
/** use UTC time-zone to avoid daylight savings issues */ | |||
private static final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone("UTC"); | |||
private static final int MS_PER_HOUR = 60 * 60 * 1000; | |||
private static final int MS_PER_DAY = 24 * MS_PER_HOUR; | |||
private static final int DAYS_PER_NORMAL_YEAR = 365; | |||
private static final int DAYS_PER_LEAP_YEAR = DAYS_PER_NORMAL_YEAR + 1; | |||
/** the length of normal long months i.e. 31 */ | |||
private static final int LONG_MONTH_LEN = 31; | |||
/** the length of normal short months i.e. 30 */ | |||
private static final int SHORT_MONTH_LEN = 30; | |||
private static final int SHORT_FEB_LEN = 28; | |||
private static final int LONG_FEB_LEN = SHORT_FEB_LEN + 1; | |||
private YearFracCalculator() { | |||
// no instances of this class | |||
} | |||
public static double calculate(double pStartDateVal, double pEndDateVal, int basis) throws EvaluationException { | |||
if (basis < 0 || basis >= 5) { | |||
// if basis is invalid the result is #NUM! | |||
throw new EvaluationException(ErrorEval.NUM_ERROR); | |||
} | |||
// common logic for all bases | |||
// truncate day values | |||
int startDateVal = (int) Math.floor(pStartDateVal); | |||
int endDateVal = (int) Math.floor(pEndDateVal); | |||
if (startDateVal == endDateVal) { | |||
// when dates are equal, result is zero | |||
return 0; | |||
} | |||
// swap start and end if out of order | |||
if (startDateVal > endDateVal) { | |||
int temp = startDateVal; | |||
startDateVal = endDateVal; | |||
endDateVal = temp; | |||
} | |||
switch (basis) { | |||
case 0: return basis0(startDateVal, endDateVal); | |||
case 1: return basis1(startDateVal, endDateVal); | |||
case 2: return basis2(startDateVal, endDateVal); | |||
case 3: return basis3(startDateVal, endDateVal); | |||
case 4: return basis4(startDateVal, endDateVal); | |||
} | |||
throw new IllegalStateException("cannot happen"); | |||
} | |||
/** | |||
* @param startDateVal assumed to be less than or equal to endDateVal | |||
* @param endDateVal assumed to be greater than or equal to startDateVal | |||
*/ | |||
public static double basis0(int startDateVal, int endDateVal) { | |||
SimpleDate startDate = createDate(startDateVal); | |||
SimpleDate endDate = createDate(endDateVal); | |||
int date1day = startDate.day; | |||
int date2day = endDate.day; | |||
// basis zero has funny adjustments to the day-of-month fields when at end-of-month | |||
if (date1day == LONG_MONTH_LEN && date2day == LONG_MONTH_LEN) { | |||
date1day = SHORT_MONTH_LEN; | |||
date2day = SHORT_MONTH_LEN; | |||
} else if (date1day == LONG_MONTH_LEN) { | |||
date1day = SHORT_MONTH_LEN; | |||
} else if (date1day == SHORT_MONTH_LEN && date2day == LONG_MONTH_LEN) { | |||
date2day = SHORT_MONTH_LEN; | |||
// Note: If date2day==31, it STAYS 31 if date1day < 30. | |||
// Special fixes for February: | |||
} else if (startDate.month == 2 && isLastDayOfMonth(startDate)) { | |||
// Note - these assignments deliberately set Feb 30 date. | |||
date1day = SHORT_MONTH_LEN; | |||
if (endDate.month == 2 && isLastDayOfMonth(endDate)) { | |||
// only adjusted when first date is last day in Feb | |||
date2day = SHORT_MONTH_LEN; | |||
} | |||
} | |||
return calculateAdjusted(startDate, endDate, date1day, date2day); | |||
} | |||
/** | |||
* @param startDateVal assumed to be less than or equal to endDateVal | |||
* @param endDateVal assumed to be greater than or equal to startDateVal | |||
*/ | |||
public static double basis1(int startDateVal, int endDateVal) { | |||
SimpleDate startDate = createDate(startDateVal); | |||
SimpleDate endDate = createDate(endDateVal); | |||
double yearLength; | |||
if (isGreaterThanOneYear(startDate, endDate)) { | |||
yearLength = averageYearLength(startDate.year, endDate.year); | |||
} else if (shouldCountFeb29(startDate, endDate)) { | |||
yearLength = DAYS_PER_LEAP_YEAR; | |||
} else { | |||
yearLength = DAYS_PER_NORMAL_YEAR; | |||
} | |||
return dateDiff(startDate.tsMilliseconds, endDate.tsMilliseconds) / yearLength; | |||
} | |||
/** | |||
* @param startDateVal assumed to be less than or equal to endDateVal | |||
* @param endDateVal assumed to be greater than or equal to startDateVal | |||
*/ | |||
public static double basis2(int startDateVal, int endDateVal) { | |||
return (endDateVal - startDateVal) / 360.0; | |||
} | |||
/** | |||
* @param startDateVal assumed to be less than or equal to endDateVal | |||
* @param endDateVal assumed to be greater than or equal to startDateVal | |||
*/ | |||
public static double basis3(double startDateVal, double endDateVal) { | |||
return (endDateVal - startDateVal) / 365.0; | |||
} | |||
/** | |||
* @param startDateVal assumed to be less than or equal to endDateVal | |||
* @param endDateVal assumed to be greater than or equal to startDateVal | |||
*/ | |||
public static double basis4(int startDateVal, int endDateVal) { | |||
SimpleDate startDate = createDate(startDateVal); | |||
SimpleDate endDate = createDate(endDateVal); | |||
int date1day = startDate.day; | |||
int date2day = endDate.day; | |||
// basis four has funny adjustments to the day-of-month fields when at end-of-month | |||
if (date1day == LONG_MONTH_LEN) { | |||
date1day = SHORT_MONTH_LEN; | |||
} | |||
if (date2day == LONG_MONTH_LEN) { | |||
date2day = SHORT_MONTH_LEN; | |||
} | |||
// Note - no adjustments for end of Feb | |||
return calculateAdjusted(startDate, endDate, date1day, date2day); | |||
} | |||
private static double calculateAdjusted(SimpleDate startDate, SimpleDate endDate, int date1day, | |||
int date2day) { | |||
double dayCount | |||
= (endDate.year - startDate.year) * 360 | |||
+ (endDate.month - startDate.month) * SHORT_MONTH_LEN | |||
+ (date2day - date1day) * 1; | |||
return dayCount / 360; | |||
} | |||
private static boolean isLastDayOfMonth(SimpleDate date) { | |||
if (date.day < SHORT_FEB_LEN) { | |||
return false; | |||
} | |||
return date.day == getLastDayOfMonth(date); | |||
} | |||
private static int getLastDayOfMonth(SimpleDate date) { | |||
switch (date.month) { | |||
case 1: | |||
case 3: | |||
case 5: | |||
case 7: | |||
case 8: | |||
case 10: | |||
case 12: | |||
return LONG_MONTH_LEN; | |||
case 4: | |||
case 6: | |||
case 9: | |||
case 11: | |||
return SHORT_MONTH_LEN; | |||
} | |||
if (isLeapYear(date.year)) { | |||
return LONG_FEB_LEN; | |||
} | |||
return SHORT_FEB_LEN; | |||
} | |||
/** | |||
* Assumes dates are no more than 1 year apart. | |||
* @return <code>true</code> if dates both within a leap year, or span a period including Feb 29 | |||
*/ | |||
private static boolean shouldCountFeb29(SimpleDate start, SimpleDate end) { | |||
boolean startIsLeapYear = isLeapYear(start.year); | |||
if (startIsLeapYear && start.year == end.year) { | |||
// note - dates may not actually span Feb-29, but it gets counted anyway in this case | |||
return true; | |||
} | |||
boolean endIsLeapYear = isLeapYear(end.year); | |||
if (!startIsLeapYear && !endIsLeapYear) { | |||
return false; | |||
} | |||
if (startIsLeapYear) { | |||
switch (start.month) { | |||
case SimpleDate.JANUARY: | |||
case SimpleDate.FEBRUARY: | |||
return true; | |||
} | |||
return false; | |||
} | |||
if (endIsLeapYear) { | |||
switch (end.month) { | |||
case SimpleDate.JANUARY: | |||
return false; | |||
case SimpleDate.FEBRUARY: | |||
break; | |||
default: | |||
return true; | |||
} | |||
return end.day == LONG_FEB_LEN; | |||
} | |||
return false; | |||
} | |||
/** | |||
* @return the whole number of days between the two time-stamps. Both time-stamps are | |||
* assumed to represent 12:00 midnight on the respective day. | |||
*/ | |||
private static int dateDiff(long startDateMS, long endDateMS) { | |||
long msDiff = endDateMS - startDateMS; | |||
// some extra checks to make sure we don't hide some other bug with the rounding | |||
int remainderHours = (int) ((msDiff % MS_PER_DAY) / MS_PER_HOUR); | |||
switch (remainderHours) { | |||
case 0: // normal case | |||
break; | |||
case 1: // transition from normal time to daylight savings adjusted | |||
case 23: // transition from daylight savings adjusted to normal time | |||
// Unexpected since we are using UTC_TIME_ZONE | |||
default: | |||
throw new RuntimeException("Unexpected date diff between " + startDateMS + " and " + endDateMS); | |||
} | |||
return (int) (0.5 + ((double)msDiff / MS_PER_DAY)); | |||
} | |||
private static double averageYearLength(int startYear, int endYear) { | |||
int dayCount = 0; | |||
for (int i=startYear; i<=endYear; i++) { | |||
dayCount += DAYS_PER_NORMAL_YEAR; | |||
if (isLeapYear(i)) { | |||
dayCount++; | |||
} | |||
} | |||
double numberOfYears = endYear-startYear+1; | |||
return dayCount / numberOfYears; | |||
} | |||
private static boolean isLeapYear(int i) { | |||
// leap years are always divisible by 4 | |||
if (i % 4 != 0) { | |||
return false; | |||
} | |||
// each 4th century is a leap year | |||
if (i % 400 == 0) { | |||
return true; | |||
} | |||
// all other centuries are *not* leap years | |||
if (i % 100 == 0) { | |||
return false; | |||
} | |||
return true; | |||
} | |||
private static boolean isGreaterThanOneYear(SimpleDate start, SimpleDate end) { | |||
if (start.year == end.year) { | |||
return false; | |||
} | |||
if (start.year + 1 != end.year) { | |||
return true; | |||
} | |||
if (start.month > end.month) { | |||
return false; | |||
} | |||
if (start.month < end.month) { | |||
return true; | |||
} | |||
return start.day < end.day; | |||
} | |||
private static SimpleDate createDate(int dayCount) { | |||
GregorianCalendar calendar = new GregorianCalendar(UTC_TIME_ZONE); | |||
DateUtil.setCalendar(calendar, dayCount, 0, false); | |||
return new SimpleDate(calendar); | |||
} | |||
private static final class SimpleDate { | |||
public static final int JANUARY = 1; | |||
public static final int FEBRUARY = 2; | |||
public final int year; | |||
/** 1-based month */ | |||
public final int month; | |||
/** day of month */ | |||
public final int day; | |||
/** milliseconds since 1970 */ | |||
public long tsMilliseconds; | |||
public SimpleDate(Calendar cal) { | |||
year = cal.get(Calendar.YEAR); | |||
month = cal.get(Calendar.MONTH) + 1; | |||
day = cal.get(Calendar.DAY_OF_MONTH); | |||
tsMilliseconds = cal.getTimeInMillis(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,93 @@ | |||
/* ==================================================================== | |||
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.ss.formula.eval; | |||
import org.apache.poi.ss.formula.TwoDEval; | |||
/** | |||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com > | |||
* | |||
*/ | |||
public interface AreaEval extends TwoDEval { | |||
/** | |||
* returns the 0-based index of the first row in | |||
* this area. | |||
*/ | |||
int getFirstRow(); | |||
/** | |||
* returns the 0-based index of the last row in | |||
* this area. | |||
*/ | |||
int getLastRow(); | |||
/** | |||
* returns the 0-based index of the first col in | |||
* this area. | |||
*/ | |||
int getFirstColumn(); | |||
/** | |||
* returns the 0-based index of the last col in | |||
* this area. | |||
*/ | |||
int getLastColumn(); | |||
/** | |||
* @return the ValueEval from within this area at the specified row and col index. Never | |||
* <code>null</code> (possibly {@link BlankEval}). The specified indexes should be absolute | |||
* indexes in the sheet and not relative indexes within the area. | |||
*/ | |||
ValueEval getAbsoluteValue(int row, int col); | |||
/** | |||
* 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 | |||
*/ | |||
boolean contains(int row, int col); | |||
/** | |||
* returns true if the specified col is in range | |||
* @param col | |||
*/ | |||
boolean containsColumn(int col); | |||
/** | |||
* returns true if the specified row is in range | |||
* @param row | |||
*/ | |||
boolean containsRow(int row); | |||
int getWidth(); | |||
int getHeight(); | |||
/** | |||
* @return the ValueEval from within this area at the specified relativeRowIndex and | |||
* relativeColumnIndex. Never <code>null</code> (possibly {@link BlankEval}). The | |||
* specified indexes should relative to the top left corner of this area. | |||
*/ | |||
ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex); | |||
/** | |||
* Creates an {@link AreaEval} offset by a relative amount from from the upper left cell | |||
* of this area | |||
*/ | |||
AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx); | |||
} |
@@ -0,0 +1,117 @@ | |||
/* ==================================================================== | |||
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.ss.formula.eval; | |||
import org.apache.poi.hssf.record.formula.AreaI; | |||
/** | |||
* @author Josh Micich | |||
*/ | |||
public abstract class AreaEvalBase implements AreaEval { | |||
private final int _firstColumn; | |||
private final int _firstRow; | |||
private final int _lastColumn; | |||
private final int _lastRow; | |||
private final int _nColumns; | |||
private final int _nRows; | |||
protected AreaEvalBase(int firstRow, int firstColumn, int lastRow, int lastColumn) { | |||
_firstColumn = firstColumn; | |||
_firstRow = firstRow; | |||
_lastColumn = lastColumn; | |||
_lastRow = lastRow; | |||
_nColumns = _lastColumn - _firstColumn + 1; | |||
_nRows = _lastRow - _firstRow + 1; | |||
} | |||
protected AreaEvalBase(AreaI ptg) { | |||
_firstRow = ptg.getFirstRow(); | |||
_firstColumn = ptg.getFirstColumn(); | |||
_lastRow = ptg.getLastRow(); | |||
_lastColumn = ptg.getLastColumn(); | |||
_nColumns = _lastColumn - _firstColumn + 1; | |||
_nRows = _lastRow - _firstRow + 1; | |||
} | |||
public final int getFirstColumn() { | |||
return _firstColumn; | |||
} | |||
public final int getFirstRow() { | |||
return _firstRow; | |||
} | |||
public final int getLastColumn() { | |||
return _lastColumn; | |||
} | |||
public final int getLastRow() { | |||
return _lastRow; | |||
} | |||
public final ValueEval getAbsoluteValue(int row, int col) { | |||
int rowOffsetIx = row - _firstRow; | |||
int colOffsetIx = col - _firstColumn; | |||
if(rowOffsetIx < 0 || rowOffsetIx >= _nRows) { | |||
throw new IllegalArgumentException("Specified row index (" + row | |||
+ ") is outside the allowed range (" + _firstRow + ".." + _lastRow + ")"); | |||
} | |||
if(colOffsetIx < 0 || colOffsetIx >= _nColumns) { | |||
throw new IllegalArgumentException("Specified column index (" + col | |||
+ ") is outside the allowed range (" + _firstColumn + ".." + col + ")"); | |||
} | |||
return getRelativeValue(rowOffsetIx, colOffsetIx); | |||
} | |||
public final boolean contains(int row, int col) { | |||
return _firstRow <= row && _lastRow >= row | |||
&& _firstColumn <= col && _lastColumn >= col; | |||
} | |||
public final boolean containsRow(int row) { | |||
return _firstRow <= row && _lastRow >= row; | |||
} | |||
public final boolean containsColumn(int col) { | |||
return _firstColumn <= col && _lastColumn >= col; | |||
} | |||
public final boolean isColumn() { | |||
return _firstColumn == _lastColumn; | |||
} | |||
public final boolean isRow() { | |||
return _firstRow == _lastRow; | |||
} | |||
public int getHeight() { | |||
return _lastRow-_firstRow+1; | |||
} | |||
public final ValueEval getValue(int row, int col) { | |||
return getRelativeValue(row, col); | |||
} | |||
public abstract ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex); | |||
public int getWidth() { | |||
return _lastColumn-_firstColumn+1; | |||
} | |||
} |
@@ -0,0 +1,35 @@ | |||
/* ==================================================================== | |||
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.ss.formula.eval; | |||
/** | |||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com > This class is a | |||
* marker class. It is a special value for empty cells. | |||
*/ | |||
public final class BlankEval implements ValueEval { | |||
public static final BlankEval instance = new BlankEval(); | |||
/** | |||
* @deprecated (Nov 2009) use {@link #instance} | |||
*/ | |||
public static final BlankEval INSTANCE = instance; | |||
private BlankEval() { | |||
// enforce singleton | |||
} | |||
} |
@@ -0,0 +1,64 @@ | |||
/* ==================================================================== | |||
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.ss.formula.eval; | |||
/** | |||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com > | |||
*/ | |||
public final class BoolEval implements NumericValueEval, StringValueEval { | |||
private boolean _value; | |||
public static final BoolEval FALSE = new BoolEval(false); | |||
public static final BoolEval TRUE = new BoolEval(true); | |||
/** | |||
* Convenience method for the following:<br/> | |||
* <code>(b ? BoolEval.TRUE : BoolEval.FALSE)</code> | |||
* | |||
* @return the <tt>BoolEval</tt> instance representing <tt>b</tt>. | |||
*/ | |||
public static final BoolEval valueOf(boolean b) { | |||
return b ? TRUE : FALSE; | |||
} | |||
private BoolEval(boolean value) { | |||
_value = value; | |||
} | |||
public boolean getBooleanValue() { | |||
return _value; | |||
} | |||
public double getNumberValue() { | |||
return _value ? 1 : 0; | |||
} | |||
public String getStringValue() { | |||
return _value ? "TRUE" : "FALSE"; | |||
} | |||
public String toString() { | |||
StringBuilder sb = new StringBuilder(64); | |||
sb.append(getClass().getName()).append(" ["); | |||
sb.append(getStringValue()); | |||
sb.append("]"); | |||
return sb.toString(); | |||
} | |||
} |
@@ -0,0 +1,60 @@ | |||
/* ==================================================================== | |||
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.ss.formula.eval; | |||
import org.apache.poi.ss.formula.functions.Fixed2ArgFunction; | |||
import org.apache.poi.ss.formula.functions.Function; | |||
/** | |||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com > | |||
*/ | |||
public final class ConcatEval extends Fixed2ArgFunction { | |||
public static final Function instance = new ConcatEval(); | |||
private ConcatEval() { | |||
// enforce singleton | |||
} | |||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) { | |||
ValueEval ve0; | |||
ValueEval ve1; | |||
try { | |||
ve0 = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex); | |||
ve1 = OperandResolver.getSingleValue(arg1, srcRowIndex, srcColumnIndex); | |||
} catch (EvaluationException e) { | |||
return e.getErrorEval(); | |||
} | |||
StringBuilder sb = new StringBuilder(); | |||
sb.append(getText(ve0)); | |||
sb.append(getText(ve1)); | |||
return new StringEval(sb.toString()); | |||
} | |||
private Object getText(ValueEval ve) { | |||
if (ve instanceof StringValueEval) { | |||
StringValueEval sve = (StringValueEval) ve; | |||
return sve.getStringValue(); | |||
} | |||
if (ve == BlankEval.instance) { | |||
return ""; | |||
} | |||
throw new IllegalAccessError("Unexpected value type (" | |||
+ ve.getClass().getName() + ")"); | |||
} | |||
} |
@@ -0,0 +1,111 @@ | |||
/* | |||
* 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.ss.formula.eval; | |||
import org.apache.poi.ss.usermodel.ErrorConstants; | |||
/** | |||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com > | |||
* | |||
*/ | |||
public final class ErrorEval implements ValueEval { | |||
// convenient access to namespace | |||
private static final ErrorConstants EC = null; | |||
/** <b>#NULL!</b> - Intersection of two cell ranges is empty */ | |||
public static final ErrorEval NULL_INTERSECTION = new ErrorEval(EC.ERROR_NULL); | |||
/** <b>#DIV/0!</b> - Division by zero */ | |||
public static final ErrorEval DIV_ZERO = new ErrorEval(EC.ERROR_DIV_0); | |||
/** <b>#VALUE!</b> - Wrong type of operand */ | |||
public static final ErrorEval VALUE_INVALID = new ErrorEval(EC.ERROR_VALUE); | |||
/** <b>#REF!</b> - Illegal or deleted cell reference */ | |||
public static final ErrorEval REF_INVALID = new ErrorEval(EC.ERROR_REF); | |||
/** <b>#NAME?</b> - Wrong function or range name */ | |||
public static final ErrorEval NAME_INVALID = new ErrorEval(EC.ERROR_NAME); | |||
/** <b>#NUM!</b> - Value range overflow */ | |||
public static final ErrorEval NUM_ERROR = new ErrorEval(EC.ERROR_NUM); | |||
/** <b>#N/A</b> - Argument or function not available */ | |||
public static final ErrorEval NA = new ErrorEval(EC.ERROR_NA); | |||
// POI internal error codes | |||
private static final int CIRCULAR_REF_ERROR_CODE = 0xFFFFFFC4; | |||
private static final int FUNCTION_NOT_IMPLEMENTED_CODE = 0xFFFFFFE2; | |||
// Note - Excel does not seem to represent this condition with an error code | |||
public static final ErrorEval CIRCULAR_REF_ERROR = new ErrorEval(CIRCULAR_REF_ERROR_CODE); | |||
/** | |||
* Translates an Excel internal error code into the corresponding POI ErrorEval instance | |||
* @param errorCode | |||
*/ | |||
public static ErrorEval valueOf(int errorCode) { | |||
switch(errorCode) { | |||
case ErrorConstants.ERROR_NULL: return NULL_INTERSECTION; | |||
case ErrorConstants.ERROR_DIV_0: return DIV_ZERO; | |||
case ErrorConstants.ERROR_VALUE: return VALUE_INVALID; | |||
case ErrorConstants.ERROR_REF: return REF_INVALID; | |||
case ErrorConstants.ERROR_NAME: return NAME_INVALID; | |||
case ErrorConstants.ERROR_NUM: return NUM_ERROR; | |||
case ErrorConstants.ERROR_NA: return NA; | |||
// non-std errors (conditions modeled as errors by POI) | |||
case CIRCULAR_REF_ERROR_CODE: return CIRCULAR_REF_ERROR; | |||
} | |||
throw new RuntimeException("Unexpected error code (" + errorCode + ")"); | |||
} | |||
/** | |||
* Converts error codes to text. Handles non-standard error codes OK. | |||
* For debug/test purposes (and for formatting error messages). | |||
* @return the String representation of the specified Excel error code. | |||
*/ | |||
public static String getText(int errorCode) { | |||
if(ErrorConstants.isValidCode(errorCode)) { | |||
return ErrorConstants.getText(errorCode); | |||
} | |||
// It is desirable to make these (arbitrary) strings look clearly different from any other | |||
// value expression that might appear in a formula. In addition these error strings should | |||
// look unlike the standard Excel errors. Hence tilde ('~') was used. | |||
switch(errorCode) { | |||
case CIRCULAR_REF_ERROR_CODE: return "~CIRCULAR~REF~"; | |||
case FUNCTION_NOT_IMPLEMENTED_CODE: return "~FUNCTION~NOT~IMPLEMENTED~"; | |||
} | |||
return "~non~std~err(" + errorCode + ")~"; | |||
} | |||
private int _errorCode; | |||
/** | |||
* @param errorCode an 8-bit value | |||
*/ | |||
private ErrorEval(int errorCode) { | |||
_errorCode = errorCode; | |||
} | |||
public int getErrorCode() { | |||
return _errorCode; | |||
} | |||
public String toString() { | |||
StringBuffer sb = new StringBuffer(64); | |||
sb.append(getClass().getName()).append(" ["); | |||
sb.append(getText(_errorCode)); | |||
sb.append("]"); | |||
return sb.toString(); | |||
} | |||
} |
@@ -0,0 +1,134 @@ | |||
/* ==================================================================== | |||
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.ss.formula.eval; | |||
/** | |||
* This class is used to simplify error handling logic <i>within</i> operator and function | |||
* implementations. Note - <tt>OperationEval.evaluate()</tt> and <tt>Function.evaluate()</tt> | |||
* method signatures do not throw this exception so it cannot propagate outside.<p/> | |||
* | |||
* Here is an example coded without <tt>EvaluationException</tt>, to show how it can help: | |||
* <pre> | |||
* public Eval evaluate(Eval[] args, int srcRow, short srcCol) { | |||
* // ... | |||
* Eval arg0 = args[0]; | |||
* if(arg0 instanceof ErrorEval) { | |||
* return arg0; | |||
* } | |||
* if(!(arg0 instanceof AreaEval)) { | |||
* return ErrorEval.VALUE_INVALID; | |||
* } | |||
* double temp = 0; | |||
* AreaEval area = (AreaEval)arg0; | |||
* ValueEval[] values = area.getValues(); | |||
* for (int i = 0; i < values.length; i++) { | |||
* ValueEval ve = values[i]; | |||
* if(ve instanceof ErrorEval) { | |||
* return ve; | |||
* } | |||
* if(!(ve instanceof NumericValueEval)) { | |||
* return ErrorEval.VALUE_INVALID; | |||
* } | |||
* temp += ((NumericValueEval)ve).getNumberValue(); | |||
* } | |||
* // ... | |||
* } | |||
* </pre> | |||
* In this example, if any error is encountered while processing the arguments, an error is | |||
* returned immediately. This code is difficult to refactor due to all the points where errors | |||
* are returned.<br/> | |||
* Using <tt>EvaluationException</tt> allows the error returning code to be consolidated to one | |||
* place.<p/> | |||
* <pre> | |||
* public Eval evaluate(Eval[] args, int srcRow, short srcCol) { | |||
* try { | |||
* // ... | |||
* AreaEval area = getAreaArg(args[0]); | |||
* double temp = sumValues(area.getValues()); | |||
* // ... | |||
* } catch (EvaluationException e) { | |||
* return e.getErrorEval(); | |||
* } | |||
*} | |||
* | |||
*private static AreaEval getAreaArg(Eval arg0) throws EvaluationException { | |||
* if (arg0 instanceof ErrorEval) { | |||
* throw new EvaluationException((ErrorEval) arg0); | |||
* } | |||
* if (arg0 instanceof AreaEval) { | |||
* return (AreaEval) arg0; | |||
* } | |||
* throw EvaluationException.invalidValue(); | |||
*} | |||
* | |||
*private double sumValues(ValueEval[] values) throws EvaluationException { | |||
* double temp = 0; | |||
* for (int i = 0; i < values.length; i++) { | |||
* ValueEval ve = values[i]; | |||
* if (ve instanceof ErrorEval) { | |||
* throw new EvaluationException((ErrorEval) ve); | |||
* } | |||
* if (!(ve instanceof NumericValueEval)) { | |||
* throw EvaluationException.invalidValue(); | |||
* } | |||
* temp += ((NumericValueEval) ve).getNumberValue(); | |||
* } | |||
* return temp; | |||
*} | |||
* </pre> | |||
* It is not mandatory to use EvaluationException, doing so might give the following advantages:<br/> | |||
* - Methods can more easily be extracted, allowing for re-use.<br/> | |||
* - Type management (typecasting etc) is simpler because error conditions have been separated from | |||
* intermediate calculation values.<br/> | |||
* - Fewer local variables are required. Local variables can have stronger types.<br/> | |||
* - It is easier to mimic common Excel error handling behaviour (exit upon encountering first | |||
* error), because exceptions conveniently propagate up the call stack regardless of execution | |||
* points or the number of levels of nested calls.<p/> | |||
* | |||
* <b>Note</b> - Only standard evaluation errors are represented by <tt>EvaluationException</tt> ( | |||
* i.e. conditions expected to be encountered when evaluating arbitrary Excel formulas). Conditions | |||
* that could never occur in an Excel spreadsheet should result in runtime exceptions. Care should | |||
* be taken to not translate any POI internal error into an Excel evaluation error code. | |||
* | |||
* @author Josh Micich | |||
*/ | |||
public final class EvaluationException extends Exception { | |||
private final ErrorEval _errorEval; | |||
public EvaluationException(ErrorEval errorEval) { | |||
_errorEval = errorEval; | |||
} | |||
// some convenience factory methods | |||
/** <b>#VALUE!</b> - Wrong type of operand */ | |||
public static EvaluationException invalidValue() { | |||
return new EvaluationException(ErrorEval.VALUE_INVALID); | |||
} | |||
/** <b>#REF!</b> - Illegal or deleted cell reference */ | |||
public static EvaluationException invalidRef() { | |||
return new EvaluationException(ErrorEval.REF_INVALID); | |||
} | |||
/** <b>#NUM!</b> - Value range overflow */ | |||
public static EvaluationException numberError() { | |||
return new EvaluationException(ErrorEval.NUM_ERROR); | |||
} | |||
public ErrorEval getErrorEval() { | |||
return _errorEval; | |||
} | |||
} |
@@ -0,0 +1,249 @@ | |||
/* ==================================================================== | |||
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.ss.formula.eval; | |||
import org.apache.poi.ss.formula.function.FunctionMetadata; | |||
import org.apache.poi.ss.formula.function.FunctionMetadataRegistry; | |||
import org.apache.poi.ss.formula.functions.*; | |||
/** | |||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com > | |||
*/ | |||
public final class FunctionEval { | |||
/** | |||
* Some function IDs that require special treatment | |||
*/ | |||
private static final class FunctionID { | |||
/** 1 */ | |||
public static final int IF = FunctionMetadataRegistry.FUNCTION_INDEX_IF; | |||
/** 4 */ | |||
public static final int SUM = FunctionMetadataRegistry.FUNCTION_INDEX_SUM; | |||
/** 78 */ | |||
public static final int OFFSET = 78; | |||
/** 100 */ | |||
public static final int CHOOSE = FunctionMetadataRegistry.FUNCTION_INDEX_CHOOSE; | |||
/** 148 */ | |||
public static final int INDIRECT = FunctionMetadataRegistry.FUNCTION_INDEX_INDIRECT; | |||
/** 255 */ | |||
public static final int EXTERNAL_FUNC = FunctionMetadataRegistry.FUNCTION_INDEX_EXTERNAL; | |||
} | |||
// convenient access to namespace | |||
private static final FunctionID ID = null; | |||
/** | |||
* Array elements corresponding to unimplemented functions are <code>null</code> | |||
*/ | |||
protected static final Function[] functions = produceFunctions(); | |||
private static Function[] produceFunctions() { | |||
Function[] retval = new Function[368]; | |||
retval[0] = new Count(); | |||
retval[ID.IF] = new IfFunc(); | |||
retval[2] = LogicalFunction.ISNA; | |||
retval[3] = LogicalFunction.ISERROR; | |||
retval[ID.SUM] = AggregateFunction.SUM; | |||
retval[5] = AggregateFunction.AVERAGE; | |||
retval[6] = AggregateFunction.MIN; | |||
retval[7] = AggregateFunction.MAX; | |||
retval[8] = new RowFunc(); // ROW | |||
retval[9] = new Column(); | |||
retval[10] = new Na(); | |||
retval[11] = new Npv(); | |||
retval[12] = AggregateFunction.STDEV; | |||
retval[13] = NumericFunction.DOLLAR; | |||
retval[15] = NumericFunction.SIN; | |||
retval[16] = NumericFunction.COS; | |||
retval[17] = NumericFunction.TAN; | |||
retval[18] = NumericFunction.ATAN; | |||
retval[19] = NumericFunction.PI; | |||
retval[20] = NumericFunction.SQRT; | |||
retval[21] = NumericFunction.EXP; | |||
retval[22] = NumericFunction.LN; | |||
retval[23] = NumericFunction.LOG10; | |||
retval[24] = NumericFunction.ABS; | |||
retval[25] = NumericFunction.INT; | |||
retval[26] = NumericFunction.SIGN; | |||
retval[27] = NumericFunction.ROUND; | |||
retval[28] = new Lookup(); | |||
retval[29] = new Index(); | |||
retval[31] = TextFunction.MID; | |||
retval[32] = TextFunction.LEN; | |||
retval[33] = new Value(); | |||
retval[34] = BooleanFunction.TRUE; | |||
retval[35] = BooleanFunction.FALSE; | |||
retval[36] = BooleanFunction.AND; | |||
retval[37] = BooleanFunction.OR; | |||
retval[38] = BooleanFunction.NOT; | |||
retval[39] = NumericFunction.MOD; | |||
retval[48] = TextFunction.TEXT; | |||
retval[56] = FinanceFunction.PV; | |||
retval[57] = FinanceFunction.FV; | |||
retval[58] = FinanceFunction.NPER; | |||
retval[59] = FinanceFunction.PMT; | |||
retval[63] = NumericFunction.RAND; | |||
retval[64] = new Match(); | |||
retval[65] = DateFunc.instance; | |||
retval[66] = new TimeFunc(); | |||
retval[67] = CalendarFieldFunction.DAY; | |||
retval[68] = CalendarFieldFunction.MONTH; | |||
retval[69] = CalendarFieldFunction.YEAR; | |||
retval[74] = new Now(); | |||
retval[76] = new Rows(); | |||
retval[77] = new Columns(); | |||
retval[82] = TextFunction.SEARCH; | |||
retval[ID.OFFSET] = new Offset(); | |||
retval[82] = TextFunction.SEARCH; | |||
retval[97] = NumericFunction.ATAN2; | |||
retval[98] = NumericFunction.ASIN; | |||
retval[99] = NumericFunction.ACOS; | |||
retval[ID.CHOOSE] = new Choose(); | |||
retval[101] = new Hlookup(); | |||
retval[102] = new Vlookup(); | |||
retval[105] = LogicalFunction.ISREF; | |||
retval[109] = NumericFunction.LOG; | |||
retval[112] = TextFunction.LOWER; | |||
retval[113] = TextFunction.UPPER; | |||
retval[115] = TextFunction.LEFT; | |||
retval[116] = TextFunction.RIGHT; | |||
retval[117] = TextFunction.EXACT; | |||
retval[118] = TextFunction.TRIM; | |||
retval[119] = new Replace(); | |||
retval[120] = new Substitute(); | |||
retval[124] = TextFunction.FIND; | |||
retval[127] = LogicalFunction.ISTEXT; | |||
retval[128] = LogicalFunction.ISNUMBER; | |||
retval[129] = LogicalFunction.ISBLANK; | |||
retval[130] = new T(); | |||
retval[ID.INDIRECT] = null; // Indirect.evaluate has different signature | |||
retval[169] = new Counta(); | |||
retval[183] = AggregateFunction.PRODUCT; | |||
retval[184] = NumericFunction.FACT; | |||
retval[190] = LogicalFunction.ISNONTEXT; | |||
retval[197] = NumericFunction.TRUNC; | |||
retval[198] = LogicalFunction.ISLOGICAL; | |||
retval[212] = NumericFunction.ROUNDUP; | |||
retval[213] = NumericFunction.ROUNDDOWN; | |||
retval[220] = new Days360(); | |||
retval[221] = new Today(); | |||
retval[227] = AggregateFunction.MEDIAN; | |||
retval[228] = new Sumproduct(); | |||
retval[229] = NumericFunction.SINH; | |||
retval[230] = NumericFunction.COSH; | |||
retval[231] = NumericFunction.TANH; | |||
retval[232] = NumericFunction.ASINH; | |||
retval[233] = NumericFunction.ACOSH; | |||
retval[234] = NumericFunction.ATANH; | |||
retval[ID.EXTERNAL_FUNC] = null; // ExternalFunction is a FreeREfFunction | |||
retval[261] = new Errortype(); | |||
retval[269] = AggregateFunction.AVEDEV; | |||
retval[276] = NumericFunction.COMBIN; | |||
retval[279] = new Even(); | |||
retval[285] = NumericFunction.FLOOR; | |||
retval[288] = NumericFunction.CEILING; | |||
retval[298] = new Odd(); | |||
retval[300] = NumericFunction.POISSON; | |||
retval[303] = new Sumxmy2(); | |||
retval[304] = new Sumx2my2(); | |||
retval[305] = new Sumx2py2(); | |||
retval[318] = AggregateFunction.DEVSQ; | |||
retval[321] = AggregateFunction.SUMSQ; | |||
retval[325] = AggregateFunction.LARGE; | |||
retval[326] = AggregateFunction.SMALL; | |||
retval[330] = new Mode(); | |||
retval[336] = TextFunction.CONCATENATE; | |||
retval[337] = NumericFunction.POWER; | |||
retval[342] = NumericFunction.RADIANS; | |||
retval[343] = NumericFunction.DEGREES; | |||
retval[344] = new Subtotal(); | |||
retval[345] = new Sumif(); | |||
retval[346] = new Countif(); | |||
retval[347] = new Countblank(); | |||
retval[359] = new Hyperlink(); | |||
retval[362] = MinaMaxa.MAXA; | |||
retval[363] = MinaMaxa.MINA; | |||
for (int i = 0; i < retval.length; i++) { | |||
Function f = retval[i]; | |||
if (f == null) { | |||
FunctionMetadata fm = FunctionMetadataRegistry.getFunctionByIndex(i); | |||
if (fm == null) { | |||
continue; | |||
} | |||
retval[i] = new NotImplementedFunction(fm.getName()); | |||
} | |||
} | |||
return retval; | |||
} | |||
/** | |||
* @return <code>null</code> if the specified functionIndex is for INDIRECT() or any external (add-in) function. | |||
*/ | |||
public static Function getBasicFunction(int functionIndex) { | |||
// check for 'free ref' functions first | |||
switch (functionIndex) { | |||
case FunctionID.INDIRECT: | |||
case FunctionID.EXTERNAL_FUNC: | |||
return null; | |||
} | |||
// else - must be plain function | |||
Function result = functions[functionIndex]; | |||
if (result == null) { | |||
throw new NotImplementedException("FuncIx=" + functionIndex); | |||
} | |||
return result; | |||
} | |||
} |
@@ -0,0 +1,96 @@ | |||
/* ==================================================================== | |||
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.ss.formula.eval; | |||
import org.apache.poi.ss.formula.functions.Fixed2ArgFunction; | |||
import org.apache.poi.ss.formula.functions.Function; | |||
/** | |||
* @author Josh Micich | |||
*/ | |||
public final class IntersectionEval extends Fixed2ArgFunction { | |||
public static final Function instance = new IntersectionEval(); | |||
private IntersectionEval() { | |||
// enforces singleton | |||
} | |||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) { | |||
try { | |||
AreaEval reA = evaluateRef(arg0); | |||
AreaEval reB = evaluateRef(arg1); | |||
AreaEval result = resolveRange(reA, reB); | |||
if (result == null) { | |||
return ErrorEval.NULL_INTERSECTION; | |||
} | |||
return result; | |||
} catch (EvaluationException e) { | |||
return e.getErrorEval(); | |||
} | |||
} | |||
/** | |||
* @return simple rectangular {@link AreaEval} which represents the intersection of areas | |||
* <tt>aeA</tt> and <tt>aeB</tt>. If the two areas do not intersect, the result is <code>null</code>. | |||
*/ | |||
private static AreaEval resolveRange(AreaEval aeA, AreaEval aeB) { | |||
int aeAfr = aeA.getFirstRow(); | |||
int aeAfc = aeA.getFirstColumn(); | |||
int aeBlc = aeB.getLastColumn(); | |||
if (aeAfc > aeBlc) { | |||
return null; | |||
} | |||
int aeBfc = aeB.getFirstColumn(); | |||
if (aeBfc > aeA.getLastColumn()) { | |||
return null; | |||
} | |||
int aeBlr = aeB.getLastRow(); | |||
if (aeAfr > aeBlr) { | |||
return null; | |||
} | |||
int aeBfr = aeB.getFirstRow(); | |||
int aeAlr = aeA.getLastRow(); | |||
if (aeBfr > aeAlr) { | |||
return null; | |||
} | |||
int top = Math.max(aeAfr, aeBfr); | |||
int bottom = Math.min(aeAlr, aeBlr); | |||
int left = Math.max(aeAfc, aeBfc); | |||
int right = Math.min(aeA.getLastColumn(), aeBlc); | |||
return aeA.offset(top-aeAfr, bottom-aeAfr, left-aeAfc, right-aeAfc); | |||
} | |||
private static AreaEval evaluateRef(ValueEval arg) throws EvaluationException { | |||
if (arg instanceof AreaEval) { | |||
return (AreaEval) arg; | |||
} | |||
if (arg instanceof RefEval) { | |||
return ((RefEval) arg).offset(0, 0, 0, 0); | |||
} | |||
if (arg instanceof ErrorEval) { | |||
throw new EvaluationException((ErrorEval)arg); | |||
} | |||
throw new IllegalArgumentException("Unexpected ref arg class (" + arg.getClass().getName() + ")"); | |||
} | |||
} |
@@ -0,0 +1,36 @@ | |||
/* ==================================================================== | |||
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.ss.formula.eval; | |||
/** | |||
* Represents the (intermediate) evaluated result of a missing function argument. In most cases | |||
* this can be translated into {@link BlankEval} but there are some notable exceptions. Functions | |||
* COUNT and COUNTA <em>do</em> count their missing args. Note - the differences between | |||
* {@link MissingArgEval} and {@link BlankEval} have not been investigated fully, so the POI | |||
* evaluator may need to be updated to account for these as they are found. | |||
* | |||
* @author Josh Micich | |||
*/ | |||
public final class MissingArgEval implements ValueEval { | |||
public static final MissingArgEval instance = new MissingArgEval(); | |||
private MissingArgEval() { | |||
// enforce singleton | |||
} | |||
} |
@@ -0,0 +1,46 @@ | |||
/* ==================================================================== | |||
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.ss.formula.eval; | |||
/** | |||
* @author Josh Micich | |||
*/ | |||
public final class NameEval implements ValueEval { | |||
private final String _functionName; | |||
/** | |||
* Creates a NameEval representing a function name | |||
*/ | |||
public NameEval(String functionName) { | |||
_functionName = functionName; | |||
} | |||
public String getFunctionName() { | |||
return _functionName; | |||
} | |||
public String toString() { | |||
StringBuffer sb = new StringBuffer(64); | |||
sb.append(getClass().getName()).append(" ["); | |||
sb.append(_functionName); | |||
sb.append("]"); | |||
return sb.toString(); | |||
} | |||
} |
@@ -0,0 +1,44 @@ | |||
/* ==================================================================== | |||
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.ss.formula.eval; | |||
import org.apache.poi.hssf.record.formula.NameXPtg; | |||
/** | |||
* @author Josh Micich | |||
*/ | |||
public final class NameXEval implements ValueEval { | |||
private final NameXPtg _ptg; | |||
public NameXEval(NameXPtg ptg) { | |||
_ptg = ptg; | |||
} | |||
public NameXPtg getPtg() { | |||
return _ptg; | |||
} | |||
public String toString() { | |||
StringBuffer sb = new StringBuffer(64); | |||
sb.append(getClass().getName()).append(" ["); | |||
sb.append(_ptg.getSheetRefIndex()).append(", ").append(_ptg.getNameIndex()); | |||
sb.append("]"); | |||
return sb.toString(); | |||
} | |||
} |
@@ -0,0 +1,73 @@ | |||
/* | |||
* 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 8, 2005 | |||
* | |||
*/ | |||
package org.apache.poi.ss.formula.eval; | |||
import org.apache.poi.hssf.record.formula.IntPtg; | |||
import org.apache.poi.hssf.record.formula.NumberPtg; | |||
import org.apache.poi.hssf.record.formula.Ptg; | |||
import org.apache.poi.ss.util.NumberToTextConverter; | |||
/** | |||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com > | |||
* | |||
*/ | |||
public final class NumberEval implements NumericValueEval, StringValueEval { | |||
public static final NumberEval ZERO = new NumberEval(0); | |||
private final double _value; | |||
private String _stringValue; | |||
public NumberEval(Ptg ptg) { | |||
if (ptg == null) { | |||
throw new IllegalArgumentException("ptg must not be null"); | |||
} | |||
if (ptg instanceof IntPtg) { | |||
_value = ((IntPtg) ptg).getValue(); | |||
} else if (ptg instanceof NumberPtg) { | |||
_value = ((NumberPtg) ptg).getValue(); | |||
} else { | |||
throw new IllegalArgumentException("bad argument type (" + ptg.getClass().getName() + ")"); | |||
} | |||
} | |||
public NumberEval(double value) { | |||
_value = value; | |||
} | |||
public double getNumberValue() { | |||
return _value; | |||
} | |||
public String getStringValue() { | |||
if (_stringValue == null) { | |||
_stringValue = NumberToTextConverter.toText(_value); | |||
} | |||
return _stringValue; | |||
} | |||
public final String toString() { | |||
StringBuffer sb = new StringBuffer(64); | |||
sb.append(getClass().getName()).append(" ["); | |||
sb.append(getStringValue()); | |||
sb.append("]"); | |||
return sb.toString(); | |||
} | |||
} |
@@ -0,0 +1,30 @@ | |||
/* | |||
* 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 8, 2005 | |||
* | |||
*/ | |||
package org.apache.poi.ss.formula.eval; | |||
/** | |||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com > | |||
* | |||
*/ | |||
public interface NumericValueEval extends ValueEval { | |||
public abstract double getNumberValue(); | |||
} |
@@ -0,0 +1,324 @@ | |||
/* ==================================================================== | |||
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.ss.formula.eval; | |||
import java.util.regex.Pattern; | |||
/** | |||
* Provides functionality for evaluating arguments to functions and operators. | |||
* | |||
* @author Josh Micich | |||
* @author Brendan Nolan | |||
*/ | |||
public final class OperandResolver { | |||
// Based on regular expression defined in JavaDoc at {@link java.lang.Double#valueOf} | |||
// modified to remove support for NaN, Infinity, Hexadecimal support and floating type suffixes | |||
private static final String Digits = "(\\p{Digit}+)"; | |||
private static final String Exp = "[eE][+-]?"+Digits; | |||
private static final String fpRegex = | |||
("[\\x00-\\x20]*" + | |||
"[+-]?(" + | |||
"((("+Digits+"(\\.)?("+Digits+"?)("+Exp+")?)|"+ | |||
"(\\.("+Digits+")("+Exp+")?))))"+ | |||
"[\\x00-\\x20]*"); | |||
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. | |||
* @param arg the evaluated argument as passed to the function or operator. | |||
* @param srcCellRow used when arg is a single column AreaRef | |||
* @param srcCellCol used when arg is a single row AreaRef | |||
* @return a <tt>NumberEval</tt>, <tt>StringEval</tt>, <tt>BoolEval</tt> or <tt>BlankEval</tt>. | |||
* Never <code>null</code> or <tt>ErrorEval</tt>. | |||
* @throws EvaluationException(#VALUE!) if srcCellRow or srcCellCol do not properly index into | |||
* an AreaEval. If the actual value retrieved is an ErrorEval, a corresponding | |||
* EvaluationException is thrown. | |||
*/ | |||
public static ValueEval getSingleValue(ValueEval arg, int srcCellRow, int srcCellCol) | |||
throws EvaluationException { | |||
ValueEval result; | |||
if (arg instanceof RefEval) { | |||
result = ((RefEval) arg).getInnerValueEval(); | |||
} else if (arg instanceof AreaEval) { | |||
result = chooseSingleElementFromArea((AreaEval) arg, srcCellRow, srcCellCol); | |||
} else { | |||
result = arg; | |||
} | |||
if (result instanceof ErrorEval) { | |||
throw new EvaluationException((ErrorEval) result); | |||
} | |||
return result; | |||
} | |||
/** | |||
* Implements (some perhaps not well known) Excel functionality to select a single cell from an | |||
* area depending on the coordinates of the calling cell. Here is an example demonstrating | |||
* both selection from a single row area and a single column area in the same formula. | |||
* | |||
* <table border="1" cellpadding="1" cellspacing="1" summary="sample spreadsheet"> | |||
* <tr><th> </th><th> A </th><th> B </th><th> C </th><th> D </th></tr> | |||
* <tr><th>1</th><td>15</td><td>20</td><td>25</td><td> </td></tr> | |||
* <tr><th>2</th><td> </td><td> </td><td> </td><td>200</td></tr> | |||
* <tr><th>3</th><td> </td><td> </td><td> </td><td>300</td></tr> | |||
* <tr><th>3</th><td> </td><td> </td><td> </td><td>400</td></tr> | |||
* </table> | |||
* | |||
* If the formula "=1000+A1:B1+D2:D3" is put into the 9 cells from A2 to C4, the spreadsheet | |||
* will look like this: | |||
* | |||
* <table border="1" cellpadding="1" cellspacing="1" summary="sample spreadsheet"> | |||
* <tr><th> </th><th> A </th><th> B </th><th> C </th><th> D </th></tr> | |||
* <tr><th>1</th><td>15</td><td>20</td><td>25</td><td> </td></tr> | |||
* <tr><th>2</th><td>1215</td><td>1220</td><td>#VALUE!</td><td>200</td></tr> | |||
* <tr><th>3</th><td>1315</td><td>1320</td><td>#VALUE!</td><td>300</td></tr> | |||
* <tr><th>4</th><td>#VALUE!</td><td>#VALUE!</td><td>#VALUE!</td><td>400</td></tr> | |||
* </table> | |||
* | |||
* Note that the row area (A1:B1) does not include column C and the column area (D2:D3) does | |||
* not include row 4, so the values in C1(=25) and D4(=400) are not accessible to the formula | |||
* as written, but in the 4 cells A2:B3, the row and column selection works ok.<p/> | |||
* | |||
* The same concept is extended to references across sheets, such that even multi-row, | |||
* multi-column areas can be useful.<p/> | |||
* | |||
* Of course with carefully (or carelessly) chosen parameters, cyclic references can occur and | |||
* hence this method <b>can</b> throw a 'circular reference' EvaluationException. Note that | |||
* this method does not attempt to detect cycles. Every cell in the specified Area <tt>ae</tt> | |||
* has already been evaluated prior to this method call. Any cell (or cell<b>s</b>) part of | |||
* <tt>ae</tt> that would incur a cyclic reference error if selected by this method, will | |||
* already have the value <t>ErrorEval.CIRCULAR_REF_ERROR</tt> upon entry to this method. It | |||
* is assumed logic exists elsewhere to produce this behaviour. | |||
* | |||
* @return whatever the selected cell's evaluated value is. Never <code>null</code>. Never | |||
* <tt>ErrorEval</tt>. | |||
* @throws EvaluationException if there is a problem with indexing into the area, or if the | |||
* evaluated cell has an error. | |||
*/ | |||
public static ValueEval chooseSingleElementFromArea(AreaEval ae, | |||
int srcCellRow, int srcCellCol) throws EvaluationException { | |||
ValueEval result = chooseSingleElementFromAreaInternal(ae, srcCellRow, srcCellCol); | |||
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, int srcCellCol) throws EvaluationException { | |||
if(false) { | |||
// this is too simplistic | |||
if(ae.containsRow(srcCellRow) && ae.containsColumn(srcCellCol)) { | |||
throw new EvaluationException(ErrorEval.CIRCULAR_REF_ERROR); | |||
} | |||
/* | |||
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 FormulaEvaluator). | |||
*/ | |||
} | |||
if (ae.isColumn()) { | |||
if(ae.isRow()) { | |||
return ae.getRelativeValue(0, 0); | |||
} | |||
if(!ae.containsRow(srcCellRow)) { | |||
throw EvaluationException.invalidValue(); | |||
} | |||
return ae.getAbsoluteValue(srcCellRow, ae.getFirstColumn()); | |||
} | |||
if(!ae.isRow()) { | |||
// multi-column, multi-row area | |||
if(ae.containsRow(srcCellRow) && ae.containsColumn(srcCellCol)) { | |||
return ae.getAbsoluteValue(ae.getFirstRow(), ae.getFirstColumn()); | |||
} | |||
throw EvaluationException.invalidValue(); | |||
} | |||
if(!ae.containsColumn(srcCellCol)) { | |||
throw EvaluationException.invalidValue(); | |||
} | |||
return ae.getAbsoluteValue(ae.getFirstRow(), srcCellCol); | |||
} | |||
/** | |||
* Applies some conversion rules if the supplied value is not already an integer.<br/> | |||
* Value is first coerced to a <tt>double</tt> ( See <tt>coerceValueToDouble()</tt> ). | |||
* Note - <tt>BlankEval</tt> is converted to <code>0</code>.<p/> | |||
* | |||
* Excel typically converts doubles to integers by truncating toward negative infinity.<br/> | |||
* The equivalent java code is:<br/> | |||
* <code>return (int)Math.floor(d);</code><br/> | |||
* <b>not</b>:<br/> | |||
* <code>return (int)d; // wrong - rounds toward zero</code> | |||
* | |||
*/ | |||
public static int coerceValueToInt(ValueEval ev) throws EvaluationException { | |||
if (ev == BlankEval.instance) { | |||
return 0; | |||
} | |||
double d = coerceValueToDouble(ev); | |||
// Note - the standard java type conversion from double to int truncates toward zero. | |||
// but Math.floor() truncates toward negative infinity | |||
return (int)Math.floor(d); | |||
} | |||
/** | |||
* Applies some conversion rules if the supplied value is not already a number. | |||
* Note - <tt>BlankEval</tt> is converted to {@link NumberEval#ZERO}. | |||
* @param ev must be a {@link NumberEval}, {@link StringEval}, {@link BoolEval} or | |||
* {@link BlankEval} | |||
* @return actual, parsed or interpreted double value (respectively). | |||
* @throws EvaluationException(#VALUE!) only if a StringEval is supplied and cannot be parsed | |||
* as a double (See <tt>parseDouble()</tt> for allowable formats). | |||
* @throws RuntimeException if the supplied parameter is not {@link NumberEval}, | |||
* {@link StringEval}, {@link BoolEval} or {@link BlankEval} | |||
*/ | |||
public static double coerceValueToDouble(ValueEval ev) throws EvaluationException { | |||
if (ev == BlankEval.instance) { | |||
return 0.0; | |||
} | |||
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 leading and trailing spaces, <p/> | |||
* | |||
* Doesn't support currency prefixes, commas, percentage signs or arithmetic operations strings. | |||
* | |||
* Some examples:<br/> | |||
* " 123 " -> 123.0<br/> | |||
* ".123" -> 0.123<br/> | |||
* "1E4" -> 1000<br/> | |||
* "-123" -> -123.0<br/> | |||
* These not supported yet:<br/> | |||
* " $ 1,000.00 " -> 1000.0<br/> | |||
* "$1.25E4" -> 12500.0<br/> | |||
* "5**2" -> 500<br/> | |||
* "250%" -> 2.5<br/> | |||
* | |||
* @return <code>null</code> if the specified text cannot be parsed as a number | |||
*/ | |||
public static Double parseDouble(String pText) { | |||
if (Pattern.matches(fpRegex, pText)) | |||
try { | |||
return Double.parseDouble(pText); | |||
} catch (NumberFormatException e) { | |||
return null; | |||
} | |||
else { | |||
return null; | |||
} | |||
} | |||
/** | |||
* @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> | |||
*/ | |||
public static String coerceValueToString(ValueEval ve) { | |||
if (ve instanceof StringValueEval) { | |||
StringValueEval sve = (StringValueEval) ve; | |||
return sve.getStringValue(); | |||
} | |||
if (ve == BlankEval.instance) { | |||
return ""; | |||
} | |||
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 == BlankEval.instance) { | |||
// TODO - remove 've == null' condition once AreaEval is fixed | |||
return null; | |||
} | |||
if (ve instanceof BoolEval) { | |||
return Boolean.valueOf(((BoolEval) ve).getBooleanValue()); | |||
} | |||
if (ve == BlankEval.instance) { | |||
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() + ")"); | |||
} | |||
} |
@@ -0,0 +1,49 @@ | |||
/* ==================================================================== | |||
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.ss.formula.eval; | |||
import org.apache.poi.ss.formula.functions.Fixed1ArgFunction; | |||
import org.apache.poi.ss.formula.functions.Function; | |||
/** | |||
* Implementation of Excel formula token '%'. <p/> | |||
* @author Josh Micich | |||
*/ | |||
public final class PercentEval extends Fixed1ArgFunction { | |||
public static final Function instance = new PercentEval(); | |||
private PercentEval() { | |||
// enforce singleton | |||
} | |||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) { | |||
double d; | |||
try { | |||
ValueEval ve = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex); | |||
d = OperandResolver.coerceValueToDouble(ve); | |||
} catch (EvaluationException e) { | |||
return e.getErrorEval(); | |||
} | |||
if (d == 0.0) { // this '==' matches +0.0 and -0.0 | |||
return NumberEval.ZERO; | |||
} | |||
return new NumberEval(d / 100); | |||
} | |||
} |
@@ -0,0 +1,75 @@ | |||
/* ==================================================================== | |||
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.ss.formula.eval; | |||
import org.apache.poi.ss.formula.functions.Fixed2ArgFunction; | |||
import org.apache.poi.ss.formula.functions.Function; | |||
/** | |||
* | |||
* @author Josh Micich | |||
*/ | |||
public final class RangeEval extends Fixed2ArgFunction { | |||
public static final Function instance = new RangeEval(); | |||
private RangeEval() { | |||
// enforces singleton | |||
} | |||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) { | |||
try { | |||
AreaEval reA = evaluateRef(arg0); | |||
AreaEval reB = evaluateRef(arg1); | |||
return resolveRange(reA, reB); | |||
} catch (EvaluationException e) { | |||
return e.getErrorEval(); | |||
} | |||
} | |||
/** | |||
* @return simple rectangular {@link AreaEval} which fully encloses both areas | |||
* <tt>aeA</tt> and <tt>aeB</tt> | |||
*/ | |||
private static AreaEval resolveRange(AreaEval aeA, AreaEval aeB) { | |||
int aeAfr = aeA.getFirstRow(); | |||
int aeAfc = aeA.getFirstColumn(); | |||
int top = Math.min(aeAfr, aeB.getFirstRow()); | |||
int bottom = Math.max(aeA.getLastRow(), aeB.getLastRow()); | |||
int left = Math.min(aeAfc, aeB.getFirstColumn()); | |||
int right = Math.max(aeA.getLastColumn(), aeB.getLastColumn()); | |||
return aeA.offset(top-aeAfr, bottom-aeAfr, left-aeAfc, right-aeAfc); | |||
} | |||
private static AreaEval evaluateRef(ValueEval arg) throws EvaluationException { | |||
if (arg instanceof AreaEval) { | |||
return (AreaEval) arg; | |||
} | |||
if (arg instanceof RefEval) { | |||
return ((RefEval) arg).offset(0, 0, 0, 0); | |||
} | |||
if (arg instanceof ErrorEval) { | |||
throw new EvaluationException((ErrorEval)arg); | |||
} | |||
throw new IllegalArgumentException("Unexpected ref arg class (" + arg.getClass().getName() + ")"); | |||
} | |||
} |
@@ -0,0 +1,51 @@ | |||
/* ==================================================================== | |||
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.ss.formula.eval; | |||
/** | |||
* @author Amol S Deshmukh < amolweb at ya hoo dot com > | |||
* | |||
* RefEval is the super interface for Ref2D and Ref3DEval. Basically a RefEval | |||
* impl should contain reference to the original ReferencePtg or Ref3DPtg as | |||
* well as the final "value" resulting from the evaluation of the cell | |||
* reference. Thus if the Cell has type CELL_TYPE_NUMERIC, the contained | |||
* value object should be of type NumberEval; if cell type is CELL_TYPE_STRING, | |||
* contained value object should be of type StringEval | |||
*/ | |||
public interface RefEval extends ValueEval { | |||
/** | |||
* @return the evaluated value of the cell referred to by this RefEval. | |||
*/ | |||
ValueEval getInnerValueEval(); | |||
/** | |||
* returns the zero based column index. | |||
*/ | |||
int getColumn(); | |||
/** | |||
* returns the zero based row index. | |||
*/ | |||
int getRow(); | |||
/** | |||
* Creates an {@link AreaEval} offset by a relative amount from this RefEval | |||
*/ | |||
AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx); | |||
} |
@@ -0,0 +1,40 @@ | |||
/* ==================================================================== | |||
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.ss.formula.eval; | |||
/** | |||
* Common base class for implementors of {@link RefEval} | |||
* | |||
* @author Josh Micich | |||
*/ | |||
public abstract class RefEvalBase implements RefEval { | |||
private final int _rowIndex; | |||
private final int _columnIndex; | |||
protected RefEvalBase(int rowIndex, int columnIndex) { | |||
_rowIndex = rowIndex; | |||
_columnIndex = columnIndex; | |||
} | |||
public final int getRow() { | |||
return _rowIndex; | |||
} | |||
public final int getColumn() { | |||
return _columnIndex; | |||
} | |||
} |
@@ -0,0 +1,168 @@ | |||
/* ==================================================================== | |||
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.ss.formula.eval; | |||
import org.apache.poi.ss.formula.functions.Fixed2ArgFunction; | |||
import org.apache.poi.ss.formula.functions.Function; | |||
import org.apache.poi.ss.util.NumberComparer; | |||
/** | |||
* Base class for all comparison operator evaluators | |||
* | |||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com > | |||
*/ | |||
public abstract class RelationalOperationEval extends Fixed2ArgFunction { | |||
/** | |||
* Converts a standard compare result (-1, 0, 1) to <code>true</code> or <code>false</code> | |||
* according to subclass' comparison type. | |||
*/ | |||
protected abstract boolean convertComparisonResult(int cmpResult); | |||
/** | |||
* This is a description of how the relational operators apply in MS Excel. | |||
* Use this as a guideline when testing/implementing the evaluate methods | |||
* for the relational operators Evals. | |||
* | |||
* <pre> | |||
* Bool.TRUE > any number. | |||
* Bool > any string. ALWAYS | |||
* Bool.TRUE > Bool.FALSE | |||
* Bool.FALSE == Blank | |||
* | |||
* Strings are never converted to numbers or booleans | |||
* String > any number. ALWAYS | |||
* Non-empty String > Blank | |||
* Empty String == Blank | |||
* String are sorted dictionary wise | |||
* | |||
* Blank > Negative numbers | |||
* Blank == 0 | |||
* Blank < Positive numbers | |||
* </pre> | |||
*/ | |||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) { | |||
ValueEval vA; | |||
ValueEval vB; | |||
try { | |||
vA = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex); | |||
vB = OperandResolver.getSingleValue(arg1, srcRowIndex, srcColumnIndex); | |||
} catch (EvaluationException e) { | |||
return e.getErrorEval(); | |||
} | |||
int cmpResult = doCompare(vA, vB); | |||
boolean result = convertComparisonResult(cmpResult); | |||
return BoolEval.valueOf(result); | |||
} | |||
private static int doCompare(ValueEval va, ValueEval vb) { | |||
// special cases when one operand is blank | |||
if (va == BlankEval.instance) { | |||
return compareBlank(vb); | |||
} | |||
if (vb == BlankEval.instance) { | |||
return -compareBlank(va); | |||
} | |||
if (va instanceof BoolEval) { | |||
if (vb instanceof BoolEval) { | |||
BoolEval bA = (BoolEval) va; | |||
BoolEval bB = (BoolEval) vb; | |||
if (bA.getBooleanValue() == bB.getBooleanValue()) { | |||
return 0; | |||
} | |||
return bA.getBooleanValue() ? 1 : -1; | |||
} | |||
return 1; | |||
} | |||
if (vb instanceof BoolEval) { | |||
return -1; | |||
} | |||
if (va instanceof StringEval) { | |||
if (vb instanceof StringEval) { | |||
StringEval sA = (StringEval) va; | |||
StringEval sB = (StringEval) vb; | |||
return sA.getStringValue().compareToIgnoreCase(sB.getStringValue()); | |||
} | |||
return 1; | |||
} | |||
if (vb instanceof StringEval) { | |||
return -1; | |||
} | |||
if (va instanceof NumberEval) { | |||
if (vb instanceof NumberEval) { | |||
NumberEval nA = (NumberEval) va; | |||
NumberEval nB = (NumberEval) vb; | |||
return NumberComparer.compare(nA.getNumberValue(), nB.getNumberValue()); | |||
} | |||
} | |||
throw new IllegalArgumentException("Bad operand types (" + va.getClass().getName() + "), (" | |||
+ vb.getClass().getName() + ")"); | |||
} | |||
private static int compareBlank(ValueEval v) { | |||
if (v == BlankEval.instance) { | |||
return 0; | |||
} | |||
if (v instanceof BoolEval) { | |||
BoolEval boolEval = (BoolEval) v; | |||
return boolEval.getBooleanValue() ? -1 : 0; | |||
} | |||
if (v instanceof NumberEval) { | |||
NumberEval ne = (NumberEval) v; | |||
return NumberComparer.compare(0.0, ne.getNumberValue()); | |||
} | |||
if (v instanceof StringEval) { | |||
StringEval se = (StringEval) v; | |||
return se.getStringValue().length() < 1 ? 0 : -1; | |||
} | |||
throw new IllegalArgumentException("bad value class (" + v.getClass().getName() + ")"); | |||
} | |||
public static final Function EqualEval = new RelationalOperationEval() { | |||
protected boolean convertComparisonResult(int cmpResult) { | |||
return cmpResult == 0; | |||
} | |||
}; | |||
public static final Function GreaterEqualEval = new RelationalOperationEval() { | |||
protected boolean convertComparisonResult(int cmpResult) { | |||
return cmpResult >= 0; | |||
} | |||
}; | |||
public static final Function GreaterThanEval = new RelationalOperationEval() { | |||
protected boolean convertComparisonResult(int cmpResult) { | |||
return cmpResult > 0; | |||
} | |||
}; | |||
public static final Function LessEqualEval = new RelationalOperationEval() { | |||
protected boolean convertComparisonResult(int cmpResult) { | |||
return cmpResult <= 0; | |||
} | |||
}; | |||
public static final Function LessThanEval = new RelationalOperationEval() { | |||
protected boolean convertComparisonResult(int cmpResult) { | |||
return cmpResult < 0; | |||
} | |||
}; | |||
public static final Function NotEqualEval = new RelationalOperationEval() { | |||
protected boolean convertComparisonResult(int cmpResult) { | |||
return cmpResult != 0; | |||
} | |||
}; | |||
} |
@@ -0,0 +1,54 @@ | |||
/* ==================================================================== | |||
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.ss.formula.eval; | |||
import org.apache.poi.hssf.record.formula.Ptg; | |||
import org.apache.poi.hssf.record.formula.StringPtg; | |||
/** | |||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com > | |||
*/ | |||
public final class StringEval implements StringValueEval { | |||
public static final StringEval EMPTY_INSTANCE = new StringEval(""); | |||
private final String _value; | |||
public StringEval(Ptg ptg) { | |||
this(((StringPtg) ptg).getValue()); | |||
} | |||
public StringEval(String value) { | |||
if (value == null) { | |||
throw new IllegalArgumentException("value must not be null"); | |||
} | |||
_value = value; | |||
} | |||
public String getStringValue() { | |||
return _value; | |||
} | |||
public String toString() { | |||
StringBuilder sb = new StringBuilder(64); | |||
sb.append(getClass().getName()).append(" ["); | |||
sb.append(_value); | |||
sb.append("]"); | |||
return sb.toString(); | |||
} | |||
} |
@@ -0,0 +1,30 @@ | |||
/* | |||
* 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.ss.formula.eval; | |||
/** | |||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com > | |||
* | |||
*/ | |||
public interface StringValueEval extends ValueEval { | |||
/** | |||
* @return never <code>null</code>, possibly empty string. | |||
*/ | |||
String getStringValue(); | |||
} |
@@ -0,0 +1,87 @@ | |||
/* ==================================================================== | |||
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.ss.formula.eval; | |||
import org.apache.poi.ss.formula.functions.Fixed2ArgFunction; | |||
import org.apache.poi.ss.formula.functions.Function; | |||
/** | |||
* @author Josh Micich | |||
*/ | |||
public abstract class TwoOperandNumericOperation extends Fixed2ArgFunction { | |||
protected final double singleOperandEvaluate(ValueEval arg, int srcCellRow, int srcCellCol) throws EvaluationException { | |||
ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol); | |||
return OperandResolver.coerceValueToDouble(ve); | |||
} | |||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) { | |||
double result; | |||
try { | |||
double d0 = singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex); | |||
double d1 = singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex); | |||
result = evaluate(d0, d1); | |||
if (result == 0.0) { // this '==' matches +0.0 and -0.0 | |||
// Excel converts -0.0 to +0.0 for '*', '/', '%', '+' and '^' | |||
if (!(this instanceof SubtractEvalClass)) { | |||
return NumberEval.ZERO; | |||
} | |||
} | |||
if (Double.isNaN(result) || Double.isInfinite(result)) { | |||
return ErrorEval.NUM_ERROR; | |||
} | |||
} catch (EvaluationException e) { | |||
return e.getErrorEval(); | |||
} | |||
return new NumberEval(result); | |||
} | |||
protected abstract double evaluate(double d0, double d1) throws EvaluationException; | |||
public static final Function AddEval = new TwoOperandNumericOperation() { | |||
protected double evaluate(double d0, double d1) { | |||
return d0+d1; | |||
} | |||
}; | |||
public static final Function DivideEval = new TwoOperandNumericOperation() { | |||
protected double evaluate(double d0, double d1) throws EvaluationException { | |||
if (d1 == 0.0) { | |||
throw new EvaluationException(ErrorEval.DIV_ZERO); | |||
} | |||
return d0/d1; | |||
} | |||
}; | |||
public static final Function MultiplyEval = new TwoOperandNumericOperation() { | |||
protected double evaluate(double d0, double d1) { | |||
return d0*d1; | |||
} | |||
}; | |||
public static final Function PowerEval = new TwoOperandNumericOperation() { | |||
protected double evaluate(double d0, double d1) { | |||
return Math.pow(d0, d1); | |||
} | |||
}; | |||
private static final class SubtractEvalClass extends TwoOperandNumericOperation { | |||
public SubtractEvalClass() { | |||
// | |||
} | |||
protected double evaluate(double d0, double d1) { | |||
return d0-d1; | |||
} | |||
} | |||
public static final Function SubtractEval = new SubtractEvalClass(); | |||
} |
@@ -0,0 +1,47 @@ | |||
/* ==================================================================== | |||
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.ss.formula.eval; | |||
import org.apache.poi.ss.formula.functions.Fixed1ArgFunction; | |||
import org.apache.poi.ss.formula.functions.Function; | |||
/** | |||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com > | |||
*/ | |||
public final class UnaryMinusEval extends Fixed1ArgFunction { | |||
public static final Function instance = new UnaryMinusEval(); | |||
private UnaryMinusEval() { | |||
// enforce singleton | |||
} | |||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) { | |||
double d; | |||
try { | |||
ValueEval ve = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex); | |||
d = OperandResolver.coerceValueToDouble(ve); | |||
} catch (EvaluationException e) { | |||
return e.getErrorEval(); | |||
} | |||
if (d == 0.0) { // this '==' matches +0.0 and -0.0 | |||
return NumberEval.ZERO; | |||
} | |||
return new NumberEval(-d); | |||
} | |||
} |
@@ -0,0 +1,51 @@ | |||
/* ==================================================================== | |||
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.ss.formula.eval; | |||
import org.apache.poi.ss.formula.functions.Fixed1ArgFunction; | |||
import org.apache.poi.ss.formula.functions.Function; | |||
/** | |||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com > | |||
*/ | |||
public final class UnaryPlusEval extends Fixed1ArgFunction { | |||
public static final Function instance = new UnaryPlusEval(); | |||
private UnaryPlusEval() { | |||
// enforce singleton | |||
} | |||
public ValueEval evaluate(int srcCellRow, int srcCellCol, ValueEval arg0) { | |||
double d; | |||
try { | |||
ValueEval ve = OperandResolver.getSingleValue(arg0, srcCellRow, srcCellCol); | |||
if(ve instanceof StringEval) { | |||
// Note - asymmetric with UnaryMinus | |||
// -"hello" evaluates to #VALUE! | |||
// but +"hello" evaluates to "hello" | |||
return ve; | |||
} | |||
d = OperandResolver.coerceValueToDouble(ve); | |||
} catch (EvaluationException e) { | |||
return e.getErrorEval(); | |||
} | |||
return new NumberEval(+d); | |||
} | |||
} |
@@ -0,0 +1,25 @@ | |||
/* ==================================================================== | |||
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.ss.formula.eval; | |||
/** | |||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com > | |||
*/ | |||
public interface ValueEval { | |||
// no methods | |||
} |
@@ -17,13 +17,12 @@ | |||
package org.apache.poi.ss.formula.eval.forked; | |||
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.NumberEval; | |||
import org.apache.poi.hssf.record.formula.eval.StringEval; | |||
import org.apache.poi.hssf.record.formula.eval.ValueEval; | |||
import org.apache.poi.hssf.usermodel.HSSFCell; | |||
import org.apache.poi.ss.formula.eval.BlankEval; | |||
import org.apache.poi.ss.formula.eval.BoolEval; | |||
import org.apache.poi.ss.formula.eval.ErrorEval; | |||
import org.apache.poi.ss.formula.eval.NumberEval; | |||
import org.apache.poi.ss.formula.eval.StringEval; | |||
import org.apache.poi.ss.formula.eval.ValueEval; | |||
import org.apache.poi.ss.formula.EvaluationCell; | |||
import org.apache.poi.ss.formula.EvaluationSheet; | |||
import org.apache.poi.ss.usermodel.Cell; | |||
@@ -60,27 +59,27 @@ final class ForkedEvaluationCell implements EvaluationCell { | |||
Class<? extends ValueEval> cls = value.getClass(); | |||
if (cls == NumberEval.class) { | |||
_cellType = HSSFCell.CELL_TYPE_NUMERIC; | |||
_cellType = Cell.CELL_TYPE_NUMERIC; | |||
_numberValue = ((NumberEval)value).getNumberValue(); | |||
return; | |||
} | |||
if (cls == StringEval.class) { | |||
_cellType = HSSFCell.CELL_TYPE_STRING; | |||
_cellType = Cell.CELL_TYPE_STRING; | |||
_stringValue = ((StringEval)value).getStringValue(); | |||
return; | |||
} | |||
if (cls == BoolEval.class) { | |||
_cellType = HSSFCell.CELL_TYPE_BOOLEAN; | |||
_cellType = Cell.CELL_TYPE_BOOLEAN; | |||
_booleanValue = ((BoolEval)value).getBooleanValue(); | |||
return; | |||
} | |||
if (cls == ErrorEval.class) { | |||
_cellType = HSSFCell.CELL_TYPE_ERROR; | |||
_cellType = Cell.CELL_TYPE_ERROR; | |||
_errorValue = ((ErrorEval)value).getErrorCode(); | |||
return; | |||
} | |||
if (cls == BlankEval.class) { | |||
_cellType = HSSFCell.CELL_TYPE_BLANK; | |||
_cellType = Cell.CELL_TYPE_BLANK; | |||
return; | |||
} | |||
throw new IllegalArgumentException("Unexpected value class (" + cls.getName() + ")"); | |||
@@ -105,19 +104,19 @@ final class ForkedEvaluationCell implements EvaluationCell { | |||
return _cellType; | |||
} | |||
public boolean getBooleanCellValue() { | |||
checkCellType(HSSFCell.CELL_TYPE_BOOLEAN); | |||
checkCellType(Cell.CELL_TYPE_BOOLEAN); | |||
return _booleanValue; | |||
} | |||
public int getErrorCellValue() { | |||
checkCellType(HSSFCell.CELL_TYPE_ERROR); | |||
checkCellType(Cell.CELL_TYPE_ERROR); | |||
return _errorValue; | |||
} | |||
public double getNumericCellValue() { | |||
checkCellType(HSSFCell.CELL_TYPE_NUMERIC); | |||
checkCellType(Cell.CELL_TYPE_NUMERIC); | |||
return _numberValue; | |||
} | |||
public String getStringCellValue() { | |||
checkCellType(HSSFCell.CELL_TYPE_STRING); | |||
checkCellType(Cell.CELL_TYPE_STRING); | |||
return _stringValue; | |||
} | |||
public EvaluationSheet getSheet() { |
@@ -23,7 +23,6 @@ import java.util.Map; | |||
import org.apache.poi.hssf.record.formula.NamePtg; | |||
import org.apache.poi.hssf.record.formula.NameXPtg; | |||
import org.apache.poi.hssf.record.formula.Ptg; | |||
import org.apache.poi.hssf.record.formula.functions.FreeRefFunction; | |||
import org.apache.poi.ss.formula.EvaluationCell; | |||
import org.apache.poi.ss.formula.EvaluationName; | |||
import org.apache.poi.ss.formula.EvaluationSheet; |
@@ -17,13 +17,12 @@ | |||
package org.apache.poi.ss.formula.eval.forked; | |||
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.NumberEval; | |||
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.udf.UDFFinder; | |||
import org.apache.poi.hssf.usermodel.HSSFCell; | |||
import org.apache.poi.ss.formula.eval.BoolEval; | |||
import org.apache.poi.ss.formula.eval.ErrorEval; | |||
import org.apache.poi.ss.formula.eval.NumberEval; | |||
import org.apache.poi.ss.formula.eval.StringEval; | |||
import org.apache.poi.ss.formula.eval.ValueEval; | |||
import org.apache.poi.ss.formula.udf.UDFFinder; | |||
import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook; | |||
import org.apache.poi.hssf.usermodel.HSSFWorkbook; | |||
import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment; | |||
@@ -31,6 +30,7 @@ import org.apache.poi.ss.formula.EvaluationCell; | |||
import org.apache.poi.ss.formula.EvaluationWorkbook; | |||
import org.apache.poi.ss.formula.IStabilityClassifier; | |||
import org.apache.poi.ss.formula.WorkbookEvaluator; | |||
import org.apache.poi.ss.usermodel.Cell; | |||
import org.apache.poi.ss.usermodel.Workbook; | |||
/** | |||
@@ -113,17 +113,17 @@ public final class ForkedEvaluator { | |||
EvaluationCell cell = _sewb.getEvaluationCell(sheetName, rowIndex, columnIndex); | |||
switch (cell.getCellType()) { | |||
case HSSFCell.CELL_TYPE_BOOLEAN: | |||
case Cell.CELL_TYPE_BOOLEAN: | |||
return BoolEval.valueOf(cell.getBooleanCellValue()); | |||
case HSSFCell.CELL_TYPE_ERROR: | |||
case Cell.CELL_TYPE_ERROR: | |||
return ErrorEval.valueOf(cell.getErrorCellValue()); | |||
case HSSFCell.CELL_TYPE_FORMULA: | |||
case Cell.CELL_TYPE_FORMULA: | |||
return _evaluator.evaluate(cell); | |||
case HSSFCell.CELL_TYPE_NUMERIC: | |||
case Cell.CELL_TYPE_NUMERIC: | |||
return new NumberEval(cell.getNumericCellValue()); | |||
case HSSFCell.CELL_TYPE_STRING: | |||
case Cell.CELL_TYPE_STRING: | |||
return new StringEval(cell.getStringCellValue()); | |||
case HSSFCell.CELL_TYPE_BLANK: | |||
case Cell.CELL_TYPE_BLANK: | |||
return null; | |||
} | |||
throw new IllegalStateException("Bad cell type (" + cell.getCellType() + ")"); |
@@ -17,8 +17,7 @@ | |||
package org.apache.poi.ss.usermodel; | |||
import org.apache.poi.hssf.record.formula.eval.ErrorEval; | |||
import org.apache.poi.ss.usermodel.Cell; | |||
import org.apache.poi.ss.formula.eval.ErrorEval; | |||
/** | |||
* Mimics the 'data view' of a cell. This allows formula evaluator |