fixed OperandResolver to correctly handle inputs with leading decimal place, see Bug #49723

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@984161 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yegor Kozlov 2010-08-10 18:49:21 +00:00
parent 14b5b74989
commit 125c86b6de
3 changed files with 122 additions and 24 deletions

View File

@ -34,6 +34,8 @@
<changes>
<release version="3.7-beta3" date="2010-??-??">
<action dev="POI-DEVELOPERS" type="fix">49725 - avoid exception in OperandResolver.parseDouble when input is minus ("-")</action>
<action dev="POI-DEVELOPERS" type="fix">49723 - fixed OperandResolver to correctly handle inputs with leading decimal place</action>
<action dev="POI-DEVELOPERS" type="add">initial support for Excel autofilter</action>
</release>
<release version="3.7-beta2" date="2010-08-09">

View File

@ -17,13 +17,28 @@
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
}
@ -214,11 +229,15 @@ public final class OperandResolver {
/**
* 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 " -&gt; 123.0<br/>
* ".123" -&gt; 0.123<br/>
* "1E4" -&gt; 1000<br/>
* "-123" -&gt; -123.0<br/>
* These not supported yet:<br/>
* " $ 1,000.00 " -&gt; 1000.0<br/>
* "$1.25E4" -&gt; 12500.0<br/>
@ -228,29 +247,17 @@ public final class OperandResolver {
* @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;
}
}
/**

View File

@ -0,0 +1,89 @@
/* ====================================================================
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));
}
}
}