git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@1199 f203690c-595d-4dc9-a70b-905162fa7fd2tags/jackcess-2.2.1
@@ -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> |
@@ -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) { |
@@ -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; | |||
} | |||
} |
@@ -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')")); |
@@ -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); |