diff options
5 files changed, 75 insertions, 6 deletions
diff --git a/src/main/java/com/healthmarketscience/jackcess/expr/package-info.java b/src/main/java/com/healthmarketscience/jackcess/expr/package-info.java index 6531eea..416c96e 100644 --- a/src/main/java/com/healthmarketscience/jackcess/expr/package-info.java +++ b/src/main/java/com/healthmarketscience/jackcess/expr/package-info.java @@ -178,7 +178,7 @@ limitations under the License. * <tr class="TableRowColor"><td>IsError</td><td></td></tr> * <tr class="TableRowColor"><td>IsMissing</td><td></td></tr> * <tr class="TableRowColor"><td>IsNull</td><td>Y</td></tr> - * <tr class="TableRowColor"><td>IsNumeric</td><td></td></tr> + * <tr class="TableRowColor"><td>IsNumeric</td><td>Y</td></tr> * <tr class="TableRowColor"><td>IsObject</td><td></td></tr> * <tr class="TableRowColor"><td>TypeName</td><td>Y</td></tr> * <tr class="TableRowColor"><td>VarType</td><td>Y</td></tr> @@ -216,7 +216,7 @@ limitations under the License. * * <table border="1" width="25%" cellpadding="3" cellspacing="0"> * <tr class="TableHeadingColor" align="left"><th>Function</th><th>Supported</th></tr> - * <tr class="TableRowColor"><td>Format</td><td></td></tr> + * <tr class="TableRowColor"><td>Format[$]</td><td></td></tr> * <tr class="TableRowColor"><td>InStr</td><td>Y</td></tr> * <tr class="TableRowColor"><td>InStrRev</td><td>Y</td></tr> * <tr class="TableRowColor"><td>LCase[$]</td><td>Y</td></tr> diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctions.java b/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctions.java index e927f9a..20de8ca 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctions.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctions.java @@ -246,6 +246,26 @@ public class DefaultFunctions } }); + public static final Function ISNUMERIC = registerFunc(new Func1("IsNumeric") { + @Override + protected Value eval1(EvalContext ctx, Value param1) { + if(param1.getType().isNumeric()) { + return ValueSupport.TRUE_VAL; + } + + if(param1.getType() == Value.Type.STRING) { + try { + param1.getAsBigDecimal(); + return ValueSupport.TRUE_VAL; + } catch(NumberFormatException ignored) { + // fall through to FALSE_VAL + } + } + + return ValueSupport.FALSE_VAL; + } + }); + public static final Function VARTYPE = registerFunc(new Func1("VarType") { @Override protected Value eval1(EvalContext ctx, Value param1) { diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/expr/StringValue.java b/src/main/java/com/healthmarketscience/jackcess/impl/expr/StringValue.java index d3e2cac..51b9c6a 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/StringValue.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/StringValue.java @@ -17,6 +17,8 @@ limitations under the License. package com.healthmarketscience.jackcess.impl.expr; import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.regex.Pattern; /** * @@ -26,6 +28,12 @@ public class StringValue extends BaseValue { private static final Object NOT_A_NUMBER = new Object(); + private static final char NUMBER_BASE_PREFIX = '&'; + private static final Pattern OCTAL_PAT = + Pattern.compile(NUMBER_BASE_PREFIX + "[oO][0-7]+"); + private static final Pattern HEX_PAT = + Pattern.compile(NUMBER_BASE_PREFIX + "[hH]\\p{XDigit}+"); + private final String _val; private Object _num; @@ -75,13 +83,36 @@ public class StringValue extends BaseValue if(_num == null) { // see if it is parseable as a number try { - _num = ValueSupport.normalize(new BigDecimal(_val)); - return (BigDecimal)_num; + // ignore extraneous whitespace whitespace and handle "&[hH]" or + // "&[oO]" prefix (only supports integers) + String tmpVal = _val.trim(); + if(tmpVal.length() > 0) { + + if(tmpVal.charAt(0) != NUMBER_BASE_PREFIX) { + // parse using standard numeric support + _num = ValueSupport.normalize(new BigDecimal(tmpVal)); + return (BigDecimal)_num; + } + + // parse as hex/octal symbolic value + if(HEX_PAT.matcher(tmpVal).matches()) { + return parseIntegerString(tmpVal, 16); + } else if(OCTAL_PAT.matcher(tmpVal).matches()) { + return parseIntegerString(tmpVal, 8); + } + + // fall through to NaN + } } catch(NumberFormatException nfe) { - _num = NOT_A_NUMBER; - // fall through to throw... + // fall through to NaN... } + _num = NOT_A_NUMBER; } throw new NumberFormatException("Invalid number '" + _val + "'"); } + + private BigDecimal parseIntegerString(String tmpVal, int radix) { + _num = new BigDecimal(new BigInteger(tmpVal.substring(2), radix)); + return (BigDecimal)_num; + } } diff --git a/src/test/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctionsTest.java b/src/test/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctionsTest.java index ca1a67c..e8aa234 100644 --- a/src/test/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctionsTest.java +++ b/src/test/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctionsTest.java @@ -80,7 +80,21 @@ public class DefaultFunctionsTest extends TestCase assertEquals("-42", eval("=CStr(-42)")); assertEquals(-1, eval("=IsNull(Null)")); + assertEquals(0, eval("=IsNull(13)")); assertEquals(-1, eval("=IsDate(#01/02/2003#)")); + assertEquals(0, eval("=IsDate('foo')")); + + assertEquals(0, eval("=IsNumeric(Null)")); + assertEquals(0, eval("=IsNumeric('foo')")); + assertEquals(0, eval("=IsNumeric(#01/02/2003#)")); + assertEquals(-1, eval("=IsNumeric(37)")); + assertEquals(-1, eval("=IsNumeric(' 37 ')")); + assertEquals(-1, eval("=IsNumeric(' -37.5e2 ')")); + assertEquals(-1, eval("=IsNumeric(' &H37 ')")); + assertEquals(0, eval("=IsNumeric(' &H37foo ')")); + assertEquals(0, eval("=IsNumeric(' &o39 ')")); + assertEquals(-1, eval("=IsNumeric(' &o36 ')")); + assertEquals(0, eval("=IsNumeric(' &o36.1 ')")); assertEquals(1, eval("=VarType(Null)")); assertEquals(8, eval("=VarType('blah')")); diff --git a/src/test/java/com/healthmarketscience/jackcess/impl/expr/ExpressionatorTest.java b/src/test/java/com/healthmarketscience/jackcess/impl/expr/ExpressionatorTest.java index 188172d..5b623a0 100644 --- a/src/test/java/com/healthmarketscience/jackcess/impl/expr/ExpressionatorTest.java +++ b/src/test/java/com/healthmarketscience/jackcess/impl/expr/ExpressionatorTest.java @@ -383,6 +383,10 @@ public class ExpressionatorTest extends TestCase assertEquals(37d, eval("=\"25\" + 12")); assertEquals(37d, eval("=12 + \"25\"")); + assertEquals(37d, eval("=\" 25 \" + 12")); + assertEquals(37d, eval("=\" &h1A \" + 11")); + assertEquals(37d, eval("=\" &h1a \" + 11")); + assertEquals(37d, eval("=\" &O32 \" + 11")); evalFail(("=12 - \"foo\""), RuntimeException.class); evalFail(("=\"foo\" - 12"), RuntimeException.class); |