Skip to content
Permalink
Browse files
JEXL-354: allow signed numbers as pragma values
  • Loading branch information
henrib committed Dec 1, 2021
1 parent a08f904 commit abd63473c899d98ce8e0f1ea236bc38a63806b3f
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 24 deletions.
@@ -18,6 +18,21 @@ Its goal is to expose scripting features usable by technical operatives or consu

https://commons.apache.org/jexl/

========================================================================================================================
Release 3.2.2
========================================================================================================================

Version 3.2.2 is a maintenance release.

Compatibility with previous releases
====================================
Version 3.2.2 is source and binary compatible with 3.2.

Bugs Fixed in 3.2.1:
==================
* JEXL-354: #pragma does not handle negative integer or real literals
* JEXL-353: Documentation error for not-in/not-match operator

========================================================================================================================
Release 3.2.1
========================================================================================================================
@@ -58,7 +58,7 @@ public boolean isInteger() {
* @param s the natural as string
*/
void setNatural(final String s) {
nlp.setNatural(s);
nlp.assignNatural(s);
}

/**
@@ -67,7 +67,7 @@ void setNatural(final String s) {
* @param s the real as string
*/
void setReal(final String s) {
nlp.setReal(s);
nlp.assignReal(s);
}

@Override
@@ -22,6 +22,9 @@
import java.text.DecimalFormatSymbols;
import java.util.Locale;

/**
* Parses number literals.
*/
public final class NumberParser {
/** The type literal value. */
private Number literal = null;
@@ -66,24 +69,52 @@ Number getLiteralValue() {
return literal;
}

static Number parseInteger(final String s) {
final NumberParser np = new NumberParser();
np.setNatural(s);
return np.getLiteralValue();
private static boolean isNegative(Token token) {
return token != null && "-".equals(token.image);
}

static Number parseInteger(Token negative, final Token s) {
return new NumberParser().assignNatural(isNegative(negative), s.image).getLiteralValue();
}

static Number parseDouble(final String s) {
final NumberParser np = new NumberParser();
np.setReal(s);
return np.getLiteralValue();
static Number parseDouble(final Token negative, final Token s) {
return new NumberParser().assignReal(isNegative(negative), s.image).getLiteralValue();
}

/**
* Sets this node as an (optionally) signed natural literal.
* Originally from OGNL.
* @param str the natural as string
* @return this parser instance
*/
NumberParser assignNatural(String str) {
String s;
// determine negative sign if any, ignore +
final boolean negative;
switch (str.charAt(0)) {
case '-':
negative = true;
s = str.substring(1);
break;
case '+':
negative = false;
s = str.substring(1);
break;
default:
negative = false;
s = str;
}
return assignNatural(negative, s);
}

/**
* Sets this node as a natural literal.
* Originally from OGNL.
* @param negative whether the natural should be negative
* @param s the natural as string
* @return this parser instance
*/
void setNatural(String s) {
NumberParser assignNatural(boolean negative, String s) {
Number result;
Class<? extends Number> rclass;
// determine the base
@@ -98,43 +129,79 @@ void setNatural(String s) {
} else {
base = 10;
}
// switch on suffix if any
final int last = s.length() - 1;
switch (s.charAt(last)) {
case 'l':
case 'L': {
rclass = Long.class;
result = Long.valueOf(s.substring(0, last), base);
long l = Long.parseLong(s.substring(0, last), base);
result = negative? -l : l;
break;
}
case 'h':
case 'H': {
rclass = BigInteger.class;
result = new BigInteger(s.substring(0, last), base);
BigInteger bi = new BigInteger(s.substring(0, last), base);
result = negative? bi.negate() : bi;
break;
}
default: {
// preferred literal class is integer
rclass = Integer.class;
try {
result = Integer.valueOf(s, base);
int i = Integer.parseInt(s, base);
result = negative? -i : i;
} catch (final NumberFormatException take2) {
try {
result = Long.valueOf(s, base);
long l = Long.parseLong(s, base);
result = negative? -l : l;
} catch (final NumberFormatException take3) {
result = new BigInteger(s, base);
BigInteger bi = new BigInteger(s, base);
result = negative? bi.negate() : bi;
}
}
}
}
literal = result;
clazz = rclass;
return this;
}

/**
* Sets this node as an (optionally) signed real literal.
* Originally from OGNL.
* @param str the real as string
* @return this parser instance
*/
NumberParser assignReal(final String str) {
String s;
// determine negative sign if any, ignore +
final boolean negative;
switch (str.charAt(0)) {
case '-':
negative = true;
s = str.substring(1);
break;
case '+':
negative = false;
s = str.substring(1);
break;
default:
negative = false;
s = str;
}
return assignReal(negative, s);
}

/**
* Sets this node as a real literal.
* Originally from OGNL.
* @param negative whether the real should be negative
* @param s the real as string
* @return this parser instance
*/
void setReal(final String s) {
NumberParser assignReal(boolean negative, String s) {
Number result;
Class<? extends Number> rclass;
if ("#NaN".equals(s) || "NaN".equals(s)) {
@@ -146,33 +213,40 @@ void setReal(final String s) {
case 'b':
case 'B': {
rclass = BigDecimal.class;
result = new BigDecimal(s.substring(0, last));
BigDecimal bd = new BigDecimal(s.substring(0, last));
result = negative? bd.negate() : bd;
break;
}
case 'f':
case 'F': {
rclass = Float.class;
result = Float.valueOf(s.substring(0, last));
float f4 = Float.parseFloat(s.substring(0, last));
result = negative? -f4 : f4;
break;
}
case 'd':
case 'D':
rclass = Double.class;
result = Double.valueOf(s.substring(0, last));
double f8 = Double.parseDouble(s.substring(0, last));
result = negative? -f8 : f8;
break;
default: {
// preferred literal class is double
rclass = Double.class;
try {
result = Double.valueOf(s);
double d = Double.parseDouble(s);
result = negative? -d : d;
} catch (final NumberFormatException take3) {
result = new BigDecimal(s);
BigDecimal bd = new BigDecimal(s);
result = negative? bd.negate() : bd;
}
break;
}
}
}
literal = result;
clazz = rclass;
return this;
}

}
@@ -100,6 +100,7 @@ TOKEN_MGR_DECLS : {
dotLexState = defaultLexState;
}
}

}
/***************************************
* Skip & Number literal tokens
@@ -453,14 +454,15 @@ void pragmaKey(LinkedList<String> lstr) #void :

Object pragmaValue() #void :
{
Token s = null;
Token v;
LinkedList<String> lstr = new LinkedList<String>();
Object result;
}
{
(
LOOKAHEAD(1) v=<INTEGER_LITERAL> { result = NumberParser.parseInteger(v.image); }
| LOOKAHEAD(1) v=<FLOAT_LITERAL> { result = NumberParser.parseDouble(v.image); }
LOOKAHEAD(2) (s=<plus>|s=<minus>)? v=<INTEGER_LITERAL> { result = NumberParser.parseInteger(s, v); }
| LOOKAHEAD(2) (s=<plus>|s=<minus>)? v=<FLOAT_LITERAL> { result = NumberParser.parseDouble(s, v); }
| LOOKAHEAD(1) v=<STRING_LITERAL> { result = Parser.buildString(v.image, true); }
| LOOKAHEAD(1) pragmaKey(lstr) { result = stringify(lstr); }
| LOOKAHEAD(1) <TRUE> { result = true; }
@@ -16,8 +16,12 @@
*/
package org.apache.commons.jexl3;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;

import org.junit.Assert;
import org.junit.Test;

@@ -182,4 +186,43 @@ public void testNamespacePragmaCtl() throws Exception {
Assert.assertEquals(42, result);
}

@Test public void test354() throws Exception {
Map<String, Number> values = new TreeMap<>();
values.put("1", 1);
values.put("+1", 1);
values.put("-1", -1);
values.put("1l", 1l);
values.put("+1l", 1l);
values.put("-1l", -1l);
values.put("10h", BigInteger.valueOf(10));
values.put("-11h", BigInteger.valueOf(-11));
values.put("+12h", BigInteger.valueOf(12));
values.put("0xa", 0xa);
values.put("+0xa", 0xa);
values.put("-0xa", -0xa);
values.put("0xacl", 0xacl);
values.put("+0xadl", 0xadl);
values.put("-0xafl", -0xafl);
values.put("1d", 1d);
values.put("-1d", -1d);
values.put("+1d", 1d);
values.put("1f", 1f);
values.put("-1f", -1f);
values.put("+1f", 1f);
values.put("1B", new BigDecimal(1));
values.put("-1B", new BigDecimal(-1));
values.put("+1B", new BigDecimal(1));
values.put("-42424242424242424242424242424242", new BigInteger("-42424242424242424242424242424242"));
values.put("+42424242424242424242424242424242", new BigInteger("+42424242424242424242424242424242"));
values.put("42424242424242424242424242424242", new BigInteger("42424242424242424242424242424242"));
JexlEngine jexl = new JexlBuilder().safe(true).create();
for(Map.Entry<String, Number> e : values.entrySet()) {
String text = "#pragma number " + e.getKey();
JexlScript script = jexl.createScript(text);
Assert.assertNotNull(script);
Map<String, Object> pragmas = script.getPragmas();
Assert.assertNotNull(pragmas);
Assert.assertEquals(e.getKey(), e.getValue(), pragmas.get("number"));
}
}
}

0 comments on commit abd6347

Please sign in to comment.