Bugzilla 46065 - added implementation for VALUE function
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@708262 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
d8650576fb
commit
e11be8a729
@ -37,6 +37,7 @@
|
|||||||
|
|
||||||
<!-- Don't forget to update status.xml too! -->
|
<!-- Don't forget to update status.xml too! -->
|
||||||
<release version="3.5-beta4" date="2008-??-??">
|
<release version="3.5-beta4" date="2008-??-??">
|
||||||
|
<action dev="POI-DEVELOPERS" type="add">46065 - added implementation for VALUE function</action>
|
||||||
<action dev="POI-DEVELOPERS" type="add">45966 - added implementation for FIND function</action>
|
<action dev="POI-DEVELOPERS" type="add">45966 - added implementation for FIND function</action>
|
||||||
<action dev="POI-DEVELOPERS" type="fix">45778 - fixed ObjRecord to read ftLbsData properly</action>
|
<action dev="POI-DEVELOPERS" type="fix">45778 - fixed ObjRecord to read ftLbsData properly</action>
|
||||||
<action dev="POI-DEVELOPERS" type="fix">46053 - fixed evaluation cache dependency analysis when changing blank cells</action>
|
<action dev="POI-DEVELOPERS" type="fix">46053 - fixed evaluation cache dependency analysis when changing blank cells</action>
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
<!-- Don't forget to update changes.xml too! -->
|
<!-- Don't forget to update changes.xml too! -->
|
||||||
<changes>
|
<changes>
|
||||||
<release version="3.5-beta4" date="2008-??-??">
|
<release version="3.5-beta4" date="2008-??-??">
|
||||||
|
<action dev="POI-DEVELOPERS" type="add">46065 - added implementation for VALUE function</action>
|
||||||
<action dev="POI-DEVELOPERS" type="add">45966 - added implementation for FIND function</action>
|
<action dev="POI-DEVELOPERS" type="add">45966 - added implementation for FIND function</action>
|
||||||
<action dev="POI-DEVELOPERS" type="fix">45778 - fixed ObjRecord to read ftLbsData properly</action>
|
<action dev="POI-DEVELOPERS" type="fix">45778 - fixed ObjRecord to read ftLbsData properly</action>
|
||||||
<action dev="POI-DEVELOPERS" type="fix">46053 - fixed evaluation cache dependency analysis when changing blank cells</action>
|
<action dev="POI-DEVELOPERS" type="fix">46053 - fixed evaluation cache dependency analysis when changing blank cells</action>
|
||||||
|
@ -1,25 +1,188 @@
|
|||||||
/*
|
/* ====================================================================
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
* contributor license agreements. See the NOTICE file distributed with
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
* this work for additional information regarding copyright ownership.
|
this work for additional information regarding copyright ownership.
|
||||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
* (the "License"); you may not use this file except in compliance with
|
(the "License"); you may not use this file except in compliance with
|
||||||
* the License. You may obtain a copy of the License at
|
the License. You may obtain a copy of the License at
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
limitations under the License.
|
||||||
*/
|
==================================================================== */
|
||||||
/*
|
|
||||||
* Created on May 15, 2005
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package org.apache.poi.hssf.record.formula.functions;
|
package org.apache.poi.hssf.record.formula.functions;
|
||||||
|
|
||||||
public class Value extends NotImplementedFunction {
|
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||||
|
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||||
|
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
|
||||||
|
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||||
|
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
|
||||||
|
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation for Excel VALUE() function.<p/>
|
||||||
|
*
|
||||||
|
* <b>Syntax</b>:<br/> <b>VALUE</b>(<b>text</b>)<br/>
|
||||||
|
*
|
||||||
|
* Converts the text argument to a number. Leading and/or trailing whitespace is
|
||||||
|
* ignored. Currency symbols and thousands separators are stripped out.
|
||||||
|
* Scientific notation is also supported. If the supplied text does not convert
|
||||||
|
* properly the result is <b>#VALUE!</b> error. Blank string converts to zero.
|
||||||
|
*
|
||||||
|
* @author Josh Micich
|
||||||
|
*/
|
||||||
|
public final class Value implements Function {
|
||||||
|
|
||||||
|
/** "1,0000" is valid, "1,00" is not */
|
||||||
|
private static final int MIN_DISTANCE_BETWEEN_THOUSANDS_SEPARATOR = 4;
|
||||||
|
private static final Double ZERO = new Double(0.0);
|
||||||
|
|
||||||
|
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
|
||||||
|
if (args.length != 1) {
|
||||||
|
return ErrorEval.VALUE_INVALID;
|
||||||
|
}
|
||||||
|
ValueEval veText;
|
||||||
|
try {
|
||||||
|
veText = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
|
||||||
|
} catch (EvaluationException e) {
|
||||||
|
return e.getErrorEval();
|
||||||
|
}
|
||||||
|
String strText = OperandResolver.coerceValueToString(veText);
|
||||||
|
Double result = convertTextToNumber(strText);
|
||||||
|
if (result == null) {
|
||||||
|
return ErrorEval.VALUE_INVALID;
|
||||||
|
}
|
||||||
|
return new NumberEval(result.doubleValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO see if the same functionality is needed in {@link OperandResolver#parseDouble(String)}
|
||||||
|
*
|
||||||
|
* @return <code>null</code> if there is any problem converting the text
|
||||||
|
*/
|
||||||
|
private static Double convertTextToNumber(String strText) {
|
||||||
|
boolean foundCurrency = false;
|
||||||
|
boolean foundUnaryPlus = false;
|
||||||
|
boolean foundUnaryMinus = false;
|
||||||
|
|
||||||
|
int len = strText.length();
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
char ch = strText.charAt(i);
|
||||||
|
if (Character.isDigit(ch) || ch == '.') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch (ch) {
|
||||||
|
case ' ':
|
||||||
|
// intervening spaces between '$', '-', '+' are OK
|
||||||
|
continue;
|
||||||
|
case '$':
|
||||||
|
if (foundCurrency) {
|
||||||
|
// only one currency symbols is allowed
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
foundCurrency = true;
|
||||||
|
continue;
|
||||||
|
case '+':
|
||||||
|
if (foundUnaryMinus || foundUnaryPlus) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
foundUnaryPlus = true;
|
||||||
|
continue;
|
||||||
|
case '-':
|
||||||
|
if (foundUnaryMinus || foundUnaryPlus) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
foundUnaryMinus = true;
|
||||||
|
continue;
|
||||||
|
default:
|
||||||
|
// all other characters are illegal
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i >= len) {
|
||||||
|
// didn't find digits or '.'
|
||||||
|
if (foundCurrency || foundUnaryMinus || foundUnaryPlus) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove thousands separators
|
||||||
|
|
||||||
|
boolean foundDecimalPoint = false;
|
||||||
|
int lastThousandsSeparatorIndex = Short.MIN_VALUE;
|
||||||
|
|
||||||
|
StringBuffer sb = new StringBuffer(len);
|
||||||
|
for (; i < len; i++) {
|
||||||
|
char ch = strText.charAt(i);
|
||||||
|
if (Character.isDigit(ch)) {
|
||||||
|
sb.append(ch);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
switch (ch) {
|
||||||
|
case ' ':
|
||||||
|
String remainingText = strText.substring(i);
|
||||||
|
if (remainingText.trim().length() > 0) {
|
||||||
|
// intervening spaces not allowed once the digits start
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '.':
|
||||||
|
if (foundDecimalPoint) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (i - lastThousandsSeparatorIndex < MIN_DISTANCE_BETWEEN_THOUSANDS_SEPARATOR) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
foundDecimalPoint = true;
|
||||||
|
sb.append('.');
|
||||||
|
continue;
|
||||||
|
case ',':
|
||||||
|
if (foundDecimalPoint) {
|
||||||
|
// thousands separators not allowed after '.' or 'E'
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int distanceBetweenThousandsSeparators = i - lastThousandsSeparatorIndex;
|
||||||
|
// as long as there are 3 or more digits between
|
||||||
|
if (distanceBetweenThousandsSeparators < MIN_DISTANCE_BETWEEN_THOUSANDS_SEPARATOR) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
lastThousandsSeparatorIndex = i;
|
||||||
|
// don't append ','
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case 'E':
|
||||||
|
case 'e':
|
||||||
|
if (i - lastThousandsSeparatorIndex < MIN_DISTANCE_BETWEEN_THOUSANDS_SEPARATOR) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// append rest of strText and skip to end of loop
|
||||||
|
sb.append(strText.substring(i));
|
||||||
|
i = len;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// all other characters are illegal
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!foundDecimalPoint) {
|
||||||
|
if (i - lastThousandsSeparatorIndex < MIN_DISTANCE_BETWEEN_THOUSANDS_SEPARATOR) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
double d;
|
||||||
|
try {
|
||||||
|
d = Double.parseDouble(sb.toString());
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// still a problem parsing the number - probably out of range
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new Double(foundUnaryMinus ? -d : d);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,7 @@ public final class AllIndividualFunctionEvaluationTests {
|
|||||||
result.addTestSuite(TestStatsLib.class);
|
result.addTestSuite(TestStatsLib.class);
|
||||||
result.addTestSuite(TestTFunc.class);
|
result.addTestSuite(TestTFunc.class);
|
||||||
result.addTestSuite(TestTrim.class);
|
result.addTestSuite(TestTrim.class);
|
||||||
|
result.addTestSuite(TestValue.class);
|
||||||
result.addTestSuite(TestXYNumericFunction.class);
|
result.addTestSuite(TestXYNumericFunction.class);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,94 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==================================================================== */
|
||||||
|
|
||||||
|
package org.apache.poi.hssf.record.formula.functions;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||||
|
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||||
|
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||||
|
import org.apache.poi.hssf.record.formula.eval.StringEval;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link Value}
|
||||||
|
*
|
||||||
|
* @author Josh Micich
|
||||||
|
*/
|
||||||
|
public final class TestValue extends TestCase {
|
||||||
|
|
||||||
|
private static Eval invokeValue(String strText) {
|
||||||
|
Eval[] args = new Eval[] { new StringEval(strText), };
|
||||||
|
return new Value().evaluate(args, -1, (short) -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void confirmValue(String strText, double expected) {
|
||||||
|
Eval result = invokeValue(strText);
|
||||||
|
assertEquals(NumberEval.class, result.getClass());
|
||||||
|
assertEquals(expected, ((NumberEval) result).getNumberValue(), 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void confirmValueError(String strText) {
|
||||||
|
Eval result = invokeValue(strText);
|
||||||
|
assertEquals(ErrorEval.class, result.getClass());
|
||||||
|
assertEquals(ErrorEval.VALUE_INVALID, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testBasic() {
|
||||||
|
|
||||||
|
confirmValue("100", 100);
|
||||||
|
confirmValue("-2.3", -2.3);
|
||||||
|
confirmValue(".5", 0.5);
|
||||||
|
confirmValue(".5e2", 50);
|
||||||
|
confirmValue(".5e-2", 0.005);
|
||||||
|
confirmValue(".5e+2", 50);
|
||||||
|
confirmValue("+5", 5);
|
||||||
|
confirmValue("$1,000", 1000);
|
||||||
|
confirmValue("100.5e1", 1005);
|
||||||
|
confirmValue("1,0000", 10000);
|
||||||
|
confirmValue("1,000,0000", 10000000);
|
||||||
|
confirmValue("1,000,0000,00000", 1000000000000.0);
|
||||||
|
confirmValue(" 100 ", 100);
|
||||||
|
confirmValue(" + 100", 100);
|
||||||
|
confirmValue("10000", 10000);
|
||||||
|
confirmValue("$-5", -5);
|
||||||
|
confirmValue("$.5", 0.5);
|
||||||
|
confirmValue("123e+5", 12300000);
|
||||||
|
confirmValue("1,000e2", 100000);
|
||||||
|
confirmValue("$10e2", 1000);
|
||||||
|
confirmValue("$1,000e2", 100000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testErrors() {
|
||||||
|
confirmValueError("1+1");
|
||||||
|
confirmValueError("1 1");
|
||||||
|
confirmValueError("1,00.0");
|
||||||
|
confirmValueError("1,00");
|
||||||
|
confirmValueError("$1,00.5e1");
|
||||||
|
confirmValueError("1,00.5e1");
|
||||||
|
confirmValueError("1,0,000");
|
||||||
|
confirmValueError("1,00,000");
|
||||||
|
confirmValueError("++100");
|
||||||
|
confirmValueError("$$5");
|
||||||
|
confirmValueError("-");
|
||||||
|
confirmValueError("+");
|
||||||
|
confirmValueError("$");
|
||||||
|
confirmValueError(",300");
|
||||||
|
confirmValueError("0.233,4");
|
||||||
|
confirmValueError("1e2.5");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user