package org.apache.poi.hssf.record.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
}
/**
* Converts a string to a double using standard rules that Excel would use.<br/>
- * Tolerates currency prefixes, commas, leading and trailing spaces.<p/>
+ * 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/>
* @return <code>null</code> if the specified text cannot be parsed as a number
*/
public static Double parseDouble(String pText) {
- String text = pText.trim();
- if(text.length() < 1) {
- return null;
- }
- boolean isPositive = true;
- if(text.charAt(0) == '-') {
- isPositive = false;
- text= text.substring(1).trim();
- }
-
- if(!Character.isDigit(text.charAt(0))) {
- // avoid using NumberFormatException to tell when string is not a number
- return null;
- }
- // TODO - support notation like '1E3' (==1000)
-
- double val;
- try {
- val = Double.parseDouble(text);
- } catch (NumberFormatException e) {
- return null;
- }
- return new Double(isPositive ? +val : -val);
+
+ if (Pattern.matches(fpRegex, pText))
+ try {
+ return Double.parseDouble(pText);
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ else {
+ return null;
+ }
+
}
/**
--- /dev/null
+/* ====================================================================
+ 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.eval;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
+/**
+ * Tests for <tt>OperandResolver</tt>
+ *
+ * @author Brendan Nolan
+ */
+public final class TestOperandResolver extends TestCase {
+
+ public void testParseDouble_bug48472() {
+
+ String value = "-";
+
+ Double resolvedValue = null;
+
+ try {
+ resolvedValue = OperandResolver.parseDouble(value);
+ } catch (StringIndexOutOfBoundsException e) {
+ throw new AssertionFailedError("Identified bug 48472");
+ }
+
+ assertEquals(null, resolvedValue);
+
+ }
+
+ public void testParseDouble_bug49723() {
+
+ String value = ".1";
+
+ Double resolvedValue = null;
+
+ resolvedValue = OperandResolver.parseDouble(value);
+
+ assertNotNull("Identified bug 49723", resolvedValue);
+
+ }
+
+ /**
+ *
+ * Tests that a list of valid strings all return a non null value from {@link OperandResolver#parseDouble(String)}
+ *
+ */
+ public void testParseDoubleValidStrings() {
+
+ String[] values = new String[]{".19", "0.19", "1.9", "1E4", "-.19", "-0.19", "8.5","-1E4", ".5E6","+1.5","+1E5", " +1E5 "};
+
+ for (String value : values) {
+ assertTrue(OperandResolver.parseDouble(value) != null);
+ assertEquals(OperandResolver.parseDouble(value), Double.parseDouble(value));
+ }
+
+ }
+
+ /**
+ *
+ * Tests that a list of invalid strings all return null from {@link OperandResolver#parseDouble(String)}
+ *
+ */
+ public void testParseDoubleInvalidStrings() {
+
+ String[] values = new String[]{"-", "ABC", "-X", "1E5a", "Infinity", "NaN", ".5F", "1,000"};
+
+ for (String value : values) {
+ assertEquals(null, OperandResolver.parseDouble(value));
+ }
+
+ }
+
+}