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:
parent
16dac5b8f3
commit
1371cca98f
@ -34,6 +34,8 @@
|
|||||||
|
|
||||||
<changes>
|
<changes>
|
||||||
<release version="3.7-beta3" date="2010-??-??">
|
<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>
|
<action dev="POI-DEVELOPERS" type="add">initial support for Excel autofilter</action>
|
||||||
</release>
|
</release>
|
||||||
<release version="3.7-beta2" date="2010-08-09">
|
<release version="3.7-beta2" date="2010-08-09">
|
||||||
|
@ -17,13 +17,28 @@
|
|||||||
|
|
||||||
package org.apache.poi.hssf.record.formula.eval;
|
package org.apache.poi.hssf.record.formula.eval;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides functionality for evaluating arguments to functions and operators.
|
* Provides functionality for evaluating arguments to functions and operators.
|
||||||
*
|
*
|
||||||
* @author Josh Micich
|
* @author Josh Micich
|
||||||
|
* @author Brendan Nolan
|
||||||
*/
|
*/
|
||||||
public final class OperandResolver {
|
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() {
|
private OperandResolver() {
|
||||||
// no instances of this class
|
// 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/>
|
* 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/>
|
* Some examples:<br/>
|
||||||
* " 123 " -> 123.0<br/>
|
* " 123 " -> 123.0<br/>
|
||||||
* ".123" -> 0.123<br/>
|
* ".123" -> 0.123<br/>
|
||||||
|
* "1E4" -> 1000<br/>
|
||||||
|
* "-123" -> -123.0<br/>
|
||||||
* These not supported yet:<br/>
|
* These not supported yet:<br/>
|
||||||
* " $ 1,000.00 " -> 1000.0<br/>
|
* " $ 1,000.00 " -> 1000.0<br/>
|
||||||
* "$1.25E4" -> 12500.0<br/>
|
* "$1.25E4" -> 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
|
* @return <code>null</code> if the specified text cannot be parsed as a number
|
||||||
*/
|
*/
|
||||||
public static Double parseDouble(String pText) {
|
public static Double parseDouble(String pText) {
|
||||||
String text = pText.trim();
|
|
||||||
if(text.length() < 1) {
|
if (Pattern.matches(fpRegex, pText))
|
||||||
return null;
|
try {
|
||||||
}
|
return Double.parseDouble(pText);
|
||||||
boolean isPositive = true;
|
} catch (NumberFormatException e) {
|
||||||
if(text.charAt(0) == '-') {
|
return null;
|
||||||
isPositive = false;
|
}
|
||||||
text= text.substring(1).trim();
|
else {
|
||||||
}
|
return null;
|
||||||
|
}
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user