\r
steps:\r
- uses: actions/checkout@v3\r
- - uses: actions/cache@v3.0.4\r
+ - uses: actions/cache@v3.0.6\r
with:\r
path: ~/.m2/repository\r
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}\r
a security manager that controls what JEXL can introspect and thus expose to scripts.
Used in conjunction with options (JexlOptions) and features (JexlFeatures), the permissions (JexlPermissions)
allow fine-tuning the scripting integration into any project.
+JEXL 3.3 also adds some syntactic (ECMAScript) features (let, const, =>, for, ...) to further reduce
+the skill set required to write scripts.
New Features in 3.3:
====================
* JEXL-373: Add support for prefix/postfix increment/decrement operators
* JEXL-372: Add support for 'standard' for loop
* JEXL-369: Add 'let' and 'const' variable declarations
+* JEXL-367: Named function and fat-arrow (=>) lambda syntax
+* JEXL-366: Fail to evaluate string and number comparison
* JEXL-365: Lambda expressions
* JEXL-363: Allow retrieving captured variables in script
* JEXL-360: Add missing bitshift operators ( >>, >>>, <<)
Bugs Fixed in 3.3:
==================
+* JEXL-376: Introspector captures methods on non-exported classes (modules, java9+)
+* JEXL-375: Cannot access enums by their name when using sandbox
+* JEXL-374: No exception if dereferencing null object using safe(false) and antish(false)
* JEXL-371: Override of a protected method with public visibility is not callable
* JEXL-370: Cannot check if variable is defined using ObjectContext if the value is null
+* JEXL-368: Namespace functor resolution is not cached
* JEXL-364: Evaluator options not propagated in closures
* JEXL-362: JexlInfo position reporting is off
* JEXL-361: Null may be used as operand silently even in arithmetic strict(true) mode
<commons.jira.id>JEXL</commons.jira.id>
<commons.jira.pid>12310479</commons.jira.pid>
<checkstyle.plugin.version>3.1.2</checkstyle.plugin.version>
- <checkstyle.version>10.3</checkstyle.version>
+ <checkstyle.version>10.3.2</checkstyle.version>
<japicmp.skip>false</japicmp.skip>
+ <commons.rat.version>0.14</commons.rat.version>
<commons.japicmp.version>0.15.7</commons.japicmp.version>
<commons.pmd.version>3.17.0</commons.pmd.version>
- <commons.spotbugs.version>4.7.0.0</commons.spotbugs.version>
+ <commons.pmd-impl.version>6.46.0</commons.pmd-impl.version>
+ <commons.spotbugs.version>4.7.1.1</commons.spotbugs.version>
<!-- override of Jacoco properties defined in CP52 -->
<commons.jacoco.version>0.8.8</commons.jacoco.version>
<exclude>org/apache/commons/jexl3/parser/*Provider.java</exclude>
</excludes>
</configuration>
- <dependencies>
- <dependency>
- <groupId>org.ow2.asm</groupId>
- <artifactId>asm</artifactId>
- <version>9.3</version>
- </dependency>
- </dependencies>
</plugin>
<!-- japicmp -->
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>animal-sniffer-maven-plugin</artifactId>
+ <version>1.21</version>
+ <configuration><ignores>java.lang.invoke.*</ignores></configuration>
+ </plugin>
+
</plugins>
</build>
<body>\r
<release version="3.3" date="YYYY-MM-DD">\r
<!-- ADD -->\r
- <action dev="henrib" type="add" issue="JEXL-357" >\r
+ <action dev="henrib" type="add" issue="JEXL-373" due-to="Dmitri Blinov">\r
+ Add support for prefix/postfix increment/decrement operators\r
+ </action>\r
+ <action dev="henrib" type="add" issue="JEXL-372" due-to="Dmitri Blinov">\r
+ Add support for 'standard' for loop\r
+ </action>\r
+ <action dev="henrib" type="add" issue="JEXL-369" due-to="Dmitri Blinov">\r
+ Add 'let' and 'const' variable declarations\r
+ </action>\r
+ <action dev="henrib" type="add" issue="JEXL-367" due-to="Hussachai Puripunpinyo">\r
+ Named function and fat-arrow (=>) lambda syntax\r
+ </action>\r
+ <action dev="henrib" type="add" issue="JEXL-366" due-to="Hussachai Puripunpinyo">\r
+ Fail to evaluate string and number comparison\r
+ </action>\r
+ <action dev="henrib" type="add" issue="JEXL-365" due-to="Dmitri Blinov">\r
+ Lambda expressions\r
+ </action>\r
+ <action dev="henrib" type="add" issue="JEXL-363">\r
+ Allow retrieving captured variables in script\r
+ </action>\r
+ <action dev="henrib" type="add" issue="JEXL-360" due-to="Ian Hawkins">\r
+ Add missing bitshift operators (<<, >>>, >>)\r
+ </action>\r
+ <action dev="henrib" type="add" issue="JEXL-359">\r
+ Allow per-operator arithmetic handling of null arguments\r
+ </action>\r
+ <action dev="henrib" type="add" issue="JEXL-357">\r
Configure accessible packages/classes/methods/fields\r
</action>\r
<!-- FIX -->\r
+ <action dev="henrib" type="fix" issue="JEXL-376">\r
+ Introspector captures methods on non-exported classes (modules, java9+)\r
+ </action>\r
+ <action dev="henrib" type="fix" issue="JEXL-375" due-to="Jan Klicka">\r
+ Cannot access enums by their name when using sandbox\r
+ </action>\r
+ <action dev="henrib" type="fix" issue="JEXL-374" due-to="Alex Hutton">\r
+ No exception if dereferencing null object using safe(false) and antish(false)\r
+ </action>\r
<action dev="henrib" type="fix" issue="JEXL-371">\r
Override of a protected method with public visibility is not callable\r
</action>\r
+ <action dev="henrib" type="fix" issue="JEXL-370" due-to="Alex Hutton">\r
+ Cannot check if variable is defined using ObjectContext if the value is null\r
+ </action>\r
+ <action dev="henrib" type="add" issue="JEXL-368">\r
+ Namespace functor resolution is not cached\r
+ </action>\r
+ <action dev="henrib" type="fix" issue="JEXL-364">\r
+ Evaluator options not propagated in closures\r
+ </action>\r
+ <action dev="henrib" type="fix" issue="JEXL-362">\r
+ JexlInfo position reporting is off\r
+ </action>\r
+ <action dev="henrib" type="fix" issue="JEXL-361">\r
+ Null may be used as operand silently even in arithmetic strict(true) mode\r
+ </action>\r
+ <action dev="henrib" type="fix" issue="JEXL-358">\r
+ JexlScript.curry(...) resulting scripts don't evaluate correctly\r
+ </action>\r
<action dev="henrib" type="fix" issue="JEXL-354" due-to="William Price">\r
#pragma does not handle negative integer or real literals\r
</action>\r
</action>\r
<!-- UPDATE -->\r
<action dev="ggregory" type="update" due-to="Gary Gregory">\r
- Bump actions/cache from 3 to 3.0.4.\r
+ Bump actions/cache from 3 to 3.0.6.\r
</action>\r
<action dev="ggregory" type="update" due-to="Dependabot">\r
Bump actions/checkout from 2 to 3 #79.\r
-<?xml version="1.0"?>
-<!--
- 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.
--->
-
-<!--
- This file removes JavaCC generated classes from being analyzed by findbugs.
- Having no way to influence their generation, instructing findbugs to ignore them reduces clutter.
--->
-<FindBugsFilter>
- <Match>
- <Class name="org.apache.commons.jexl3.parser.AbstractCharStream"/>
- </Match>
- <Match>
- <Class name="org.apache.commons.jexl3.parser.JexlLexicalNode"/>
- </Match>
- <Match>
- <Class name="org.apache.commons.jexl3.parser.JexlParser"/>
- </Match>
- <Match>
- <Class name="org.apache.commons.jexl3.parser.ParseException"/>
- </Match>
- <Match>
- <Class name="org.apache.commons.jexl3.parser.Parser"/>
- </Match>
- <Match>
- <Class name="org.apache.commons.jexl3.parser.ParserConstants"/>
- </Match>
- <Match>
- <Class name="org.apache.commons.jexl3.parser.ParserTokenManager"/>
- </Match>
- <Match>
- <Class name="org.apache.commons.jexl3.parser.ParserTreeConstants"/>
- </Match>
- <Match>
- <Class name="org.apache.commons.jexl3.parser.TokenMgrError"/>
- </Match>
- <Match>
- <Class name="org.apache.commons.jexl3.parser.SimpleNode"/>
- </Match>
- <Match>
- <Class name="org.apache.commons.jexl3.JexlBuilder"/>
- <Bug code="EI2,EI"></Bug>
- </Match>
- <Match>
- <Package name="org.apache.commons.jexl3.internal"/>
- <Bug code="EI2,EI"></Bug>
- </Match>
- <Match>
- <Package name="org.apache.commons.jexl3.introspection.internal"/>
- <Bug code="EI2,EI"></Bug>
- </Match>
- <Match>
- <Package name="org.apache.commons.jexl3.parser"/>
- <Bug code="EI2,EI"></Bug>
- </Match>
-</FindBugsFilter>
+<?xml version="1.0"?>\r
+<!--\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to You under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+-->\r
+\r
+<!--\r
+ This file removes JavaCC generated classes from being analyzed by findbugs.\r
+ Having no way to influence their generation, instructing findbugs to ignore them reduces clutter.\r
+-->\r
+<FindBugsFilter\r
+ xmlns="https://github.com/spotbugs/filter/3.0.0"\r
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+ xsi:schemaLocation="https://github.com/spotbugs/filter/3.0.0 https://raw.githubusercontent.com/spotbugs/spotbugs/3.1.0/spotbugs/etc/findbugsfilter.xsd">\r
+\r
+ <Match>\r
+ <Class name="org.apache.commons.jexl3.parser.AbstractCharStream"/>\r
+ </Match>\r
+ <Match>\r
+ <Class name="org.apache.commons.jexl3.parser.JexlLexicalNode"/>\r
+ </Match>\r
+ <Match>\r
+ <Class name="org.apache.commons.jexl3.parser.JexlParser"/>\r
+ </Match>\r
+ <Match>\r
+ <Class name="org.apache.commons.jexl3.parser.ParseException"/>\r
+ </Match>\r
+ <Match>\r
+ <Class name="org.apache.commons.jexl3.parser.Parser"/>\r
+ </Match>\r
+ <Match>\r
+ <Class name="org.apache.commons.jexl3.parser.ParserConstants"/>\r
+ </Match>\r
+ <Match>\r
+ <Class name="org.apache.commons.jexl3.parser.ParserTokenManager"/>\r
+ </Match>\r
+ <Match>\r
+ <Class name="org.apache.commons.jexl3.parser.ParserTreeConstants"/>\r
+ </Match>\r
+ <Match>\r
+ <Class name="org.apache.commons.jexl3.parser.TokenMgrError"/>\r
+ </Match>\r
+ <Match>\r
+ <Class name="org.apache.commons.jexl3.parser.SimpleNode"/>\r
+ </Match>\r
+ <Match>\r
+ <Class name="org.apache.commons.jexl3.JexlBuilder"/>\r
+ <Bug code="EI2,EI"></Bug>\r
+ </Match>\r
+ <Match>\r
+ <Package name="org.apache.commons.jexl3.internal"/>\r
+ <Bug code="EI2,EI"></Bug>\r
+ </Match>\r
+ <Match>\r
+ <Package name="org.apache.commons.jexl3.introspection.internal"/>\r
+ <Bug code="EI2,EI"></Bug>\r
+ </Match>\r
+ <Match>\r
+ <Package name="org.apache.commons.jexl3.parser"/>\r
+ <Bug code="EI2,EI"></Bug>\r
+ </Match>\r
+</FindBugsFilter>\r
}
/**
- * Given a Number, return back the value attempting to narrow it to a target class.
+ * Given a Number, return the value attempting to narrow it to a target class.
*
* @param original the original number
* @param narrow the attempted target class
* if either arguments is a Long, no narrowing to Integer will occur
* </p>
*
- * @param lhs the left hand side operand that lead to the bigi result
- * @param rhs the right hand side operand that lead to the bigi result
+ * @param lhs the left-hand side operand that lead to the bigi result
+ * @param rhs the right-hand side operand that lead to the bigi result
* @param bigi the BigInteger to narrow
* @return an Integer or Long if narrowing is possible, the original BigInteger otherwise
*/
protected Number narrowBigInteger(final Object lhs, final Object rhs, final BigInteger bigi) {
//coerce to long if possible
- if (!(lhs instanceof BigInteger || rhs instanceof BigInteger)
+ if ((isNumberable(lhs) || isNumberable(rhs))
&& bigi.compareTo(BIGI_LONG_MAX_VALUE) <= 0
&& bigi.compareTo(BIGI_LONG_MIN_VALUE) >= 0) {
// coerce to int if possible
}
/**
- * Given a BigDecimal, attempt to narrow it to an Integer or Long if it fits if
+ * Given a BigDecimal, attempt to narrow it to an Integer or Long if it fits and
* one of the arguments is a numberable.
*
- * @param lhs the left hand side operand that lead to the bigd result
- * @param rhs the right hand side operand that lead to the bigd result
+ * @param lhs the left-hand side operand that lead to the bigd result
+ * @param rhs the right-hand side operand that lead to the bigd result
* @param bigd the BigDecimal to narrow
- * @return an Integer or Long if narrowing is possible, the original BigInteger otherwise
+ * @return an Integer or Long if narrowing is possible, the original BigDecimal otherwise
*/
protected Number narrowBigDecimal(final Object lhs, final Object rhs, final BigDecimal bigd) {
if (isNumberable(lhs) || isNumberable(rhs)) {
return ((Long) val) + incr;
}
if (val instanceof BigDecimal) {
- return ((BigDecimal) val).add(BigDecimal.valueOf(incr));
+ BigDecimal bd = (BigDecimal) val;
+ return bd.add(BigDecimal.valueOf(incr), this.mathContext);
}
if (val instanceof BigInteger) {
- return ((BigInteger) val).add(BigInteger.valueOf(incr));
+ BigInteger bi = (BigInteger) val;
+ return bi.add(BigInteger.valueOf(incr));
}
if (val instanceof Float) {
return ((Float) val) + incr;
}
if (val instanceof Short) {
- return (short) ((Short) val) + incr;
+ return (short) (((Short) val) + incr);
}
if (val instanceof Byte) {
- return (byte) ((Byte) val) + incr;
+ return (byte) (((Byte) val) + incr);
}
throw new ArithmeticException("Object "+(incr < 0? "decrement":"increment")+":(" + val + ")");
}
/**
* Negates a value (unary minus for numbers).
*
+ * @see #isNegateStable()
* @param val the value to negate
* @return the negated value
*/
* <p>This is used to determine whether negate results on number literals can be cached.
* If the result on calling negate with the same constant argument may change between calls,
* which means the function is not deterministic, this method must return false.
- * @see #isNegateStable()
* @return true if negate is idempotent, false otherwise
*/
public boolean isNegateStable() {
*/
private double parseDouble(String arg) throws ArithmeticException {
try {
- return arg.isEmpty()? Double.NaN : Double.parseDouble((String) arg);
+ return arg.isEmpty()? Double.NaN : Double.parseDouble(arg);
} catch(NumberFormatException xformat) {
throw new ArithmeticException("Double coercion: ("+ arg +")");
}
return parseDouble((String) val);
}
if (val instanceof Character) {
- final int i = ((Character) val);
- return i;
+ return ((Character) val);
}
throw new ArithmeticException("Double coercion: "
+ val.getClass().getName() + ":(" + val + ")");
stackJexl.add(se);
}
}
- xthrow.setStackTrace(stackJexl.toArray(new StackTraceElement[stackJexl.size()]));
+ xthrow.setStackTrace(stackJexl.toArray(new StackTraceElement[0]));
}
return xthrow;
}
public Property(final JexlNode node, final String pty) {
this(node, pty, true, null);
}
+
/**
* Creates a new Property exception instance.
*
* <p>The default JexlArithmetic implements generic versions of these methods using Object as arguments.
* You can use your own derived JexlArithmetic that override and/or overload those operator methods.
* Note that these are overloads by convention, not actual Java overloads.
- * The following rules apply to operator methods:</p>
+ * The following rules apply to all operator methods:</p>
* <ul>
* <li>Operator methods should be public</li>
* <li>Operators return type should be respected when primitive (int, boolean,...)</li>
* <li>Operators may return JexlEngine.TRY_AGAIN to fallback on default JEXL implementation</li>
* </ul>
*
+ * For side effect operators, operators that modify the left-hand size value (+=, -=, etc), the user implemented
+ * overload methods may return:
+ * <ul>
+ * <li>JexlEngine.TRY_FAIL to let the default fallback behavior be executed.</li>
+ * <li>Any other value will be used as the new value to be assigned to the left-hand-side.</li>
+ * </ul>
+ * Note that side effect operators always return the left-hand side value (with an exception for postfix ++ and --).
+ *
* @since 3.0
*/
public enum JexlOperator {
* Add operator.
* <br><strong>Syntax:</strong> <code>x + y</code>
* <br><strong>Method:</strong> <code>T add(L x, R y);</code>.
- * @see JexlArithmetic#add
+ * @see JexlArithmetic#add(Object, Object)
*/
ADD("+", "add", 2),
* Subtract operator.
* <br><strong>Syntax:</strong> <code>x - y</code>
* <br><strong>Method:</strong> <code>T subtract(L x, R y);</code>.
- * @see JexlArithmetic#subtract
+ * @see JexlArithmetic#subtract(Object, Object)
*/
SUBTRACT("-", "subtract", 2),
* Multiply operator.
* <br><strong>Syntax:</strong> <code>x * y</code>
* <br><strong>Method:</strong> <code>T multiply(L x, R y);</code>.
- * @see JexlArithmetic#multiply
+ * @see JexlArithmetic#multiply(Object, Object)
*/
MULTIPLY("*", "multiply", 2),
* Divide operator.
* <br><strong>Syntax:</strong> <code>x / y</code>
* <br><strong>Method:</strong> <code>T divide(L x, R y);</code>.
- * @see JexlArithmetic#divide
+ * @see JexlArithmetic#divide(Object, Object)
*/
DIVIDE("/", "divide", 2),
* Modulo operator.
* <br><strong>Syntax:</strong> <code>x % y</code>
* <br><strong>Method:</strong> <code>T mod(L x, R y);</code>.
- * @see JexlArithmetic#mod
+ * @see JexlArithmetic#mod(Object, Object)
*/
MOD("%", "mod", 2),
* Bitwise-and operator.
* <br><strong>Syntax:</strong> <code>x & y</code>
* <br><strong>Method:</strong> <code>T and(L x, R y);</code>.
- * @see JexlArithmetic#and
+ * @see JexlArithmetic#and(Object, Object)
*/
AND("&", "and", 2),
* Bitwise-or operator.
* <br><strong>Syntax:</strong> <code>x | y</code>
* <br><strong>Method:</strong> <code>T or(L x, R y);</code>.
- * @see JexlArithmetic#or
+ * @see JexlArithmetic#or(Object, Object)
*/
OR("|", "or", 2),
* Bitwise-xor operator.
* <br><strong>Syntax:</strong> <code>x ^ y</code>
* <br><strong>Method:</strong> <code>T xor(L x, R y);</code>.
- * @see JexlArithmetic#xor
+ * @see JexlArithmetic#xor(Object, Object)
*/
XOR("^", "xor", 2),
* Equals operator.
* <br><strong>Syntax:</strong> <code>x == y</code>
* <br><strong>Method:</strong> <code>boolean equals(L x, R y);</code>.
- * @see JexlArithmetic#equals
+ * @see JexlArithmetic#equals(Object, Object)
*/
EQ("==", "equals", 2),
* Less-than operator.
* <br><strong>Syntax:</strong> <code>x < y</code>
* <br><strong>Method:</strong> <code>boolean lessThan(L x, R y);</code>.
- * @see JexlArithmetic#lessThan
+ * @see JexlArithmetic#lessThan(Object, Object)
*/
LT("<", "lessThan", 2),
* Less-than-or-equal operator.
* <br><strong>Syntax:</strong> <code>x <= y</code>
* <br><strong>Method:</strong> <code>boolean lessThanOrEqual(L x, R y);</code>.
- * @see JexlArithmetic#lessThanOrEqual
+ * @see JexlArithmetic#lessThanOrEqual(Object, Object)
*/
LTE("<=", "lessThanOrEqual", 2),
* Greater-than operator.
* <br><strong>Syntax:</strong> <code>x > y</code>
* <br><strong>Method:</strong> <code>boolean greaterThan(L x, R y);</code>.
- * @see JexlArithmetic#greaterThan
+ * @see JexlArithmetic#greaterThan(Object, Object)
*/
GT(">", "greaterThan", 2),
* Greater-than-or-equal operator.
* <br><strong>Syntax:</strong> <code>x >= y</code>
* <br><strong>Method:</strong> <code>boolean greaterThanOrEqual(L x, R y);</code>.
- * @see JexlArithmetic#greaterThanOrEqual
+ * @see JexlArithmetic#greaterThanOrEqual(Object, Object)
*/
GTE(">=", "greaterThanOrEqual", 2),
* Contains operator.
* <br><strong>Syntax:</strong> <code>x =~ y</code>
* <br><strong>Method:</strong> <code>boolean contains(L x, R y);</code>.
- * @see JexlArithmetic#contains
+ * @see JexlArithmetic#contains(Object, Object)
*/
CONTAINS("=~", "contains", 2),
* Starts-with operator.
* <br><strong>Syntax:</strong> <code>x =^ y</code>
* <br><strong>Method:</strong> <code>boolean startsWith(L x, R y);</code>.
- * @see JexlArithmetic#startsWith
+ * @see JexlArithmetic#startsWith(Object, Object)
*/
STARTSWITH("=^", "startsWith", 2),
* Ends-with operator.
* <br><strong>Syntax:</strong> <code>x =$ y</code>
* <br><strong>Method:</strong> <code>boolean endsWith(L x, R y);</code>.
- * @see JexlArithmetic#endsWith
+ * @see JexlArithmetic#endsWith(Object, Object)
*/
ENDSWITH("=$", "endsWith", 2),
* Not operator.
* <br><strong>Syntax:</strong> <code>!x</code>
* <br><strong>Method:</strong> <code>T not(L x);</code>.
- * @see JexlArithmetic#not
+ * @see JexlArithmetic#not(Object)
*/
NOT("!", "not", 1),
* Complement operator.
* <br><strong>Syntax:</strong> <code>~x</code>
* <br><strong>Method:</strong> <code>T complement(L x);</code>.
- * @see JexlArithmetic#complement
+ * @see JexlArithmetic#complement(Object)
*/
COMPLEMENT("~", "complement", 1),
* Negate operator.
* <br><strong>Syntax:</strong> <code>-x</code>
* <br><strong>Method:</strong> <code>T negate(L x);</code>.
- * @see JexlArithmetic#negate
+ * @see JexlArithmetic#negate(Object)
*/
NEGATE("-", "negate", 1),
* Positivize operator.
* <br><strong>Syntax:</strong> <code>+x</code>
* <br><strong>Method:</strong> <code>T positivize(L x);</code>.
- * @see JexlArithmetic#positivize
+ * @see JexlArithmetic#positivize(Object)
*/
POSITIVIZE("+", "positivize", 1),
* Empty operator.
* <br><strong>Syntax:</strong> <code>empty x</code> or <code>empty(x)</code>
* <br><strong>Method:</strong> <code>boolean empty(L x);</code>.
- * @see JexlArithmetic#empty
+ * @see JexlArithmetic#empty(Object)
*/
EMPTY("empty", "empty", 1),
* Size operator.
* <br><strong>Syntax:</strong> <code>size x</code> or <code>size(x)</code>
* <br><strong>Method:</strong> <code>int size(L x);</code>.
- * @see JexlArithmetic#size
+ * @see JexlArithmetic#size(Object)
*/
SIZE("size", "size", 1),
/**
* Increment pseudo-operator.
- * <br>No syntax, used as helper for <code>++</code>.
- * @see JexlArithmetic#increment
+ * <br>No syntax, used as helper for the prefix and postfix versions of <code>++</code>.
+ * @see JexlArithmetic#increment(Object)
*/
INCREMENT("+1", "increment", 1),
/**
* Decrement pseudo-operator.
- * <br>No syntax, used as helper for <code>--</code>.
- * @see JexlArithmetic#decrement
+ * <br>No syntax, used as helper for the prefix and postfix versions of <code>--</code>.
+ * @see JexlArithmetic#decrement(Object)
*/
DECREMENT("-1", "decrement", 1),
* <br><strong>Syntax:</strong> <code>++x</code>
* <br><strong>Method:</strong> <code>T incrementAndGet(L x);</code>.
*/
- INCREMENT_AND_GET("++.", "incrementAndGet", INCREMENT),
+ INCREMENT_AND_GET("++.", "incrementAndGet", INCREMENT, 1),
/**
* Postfix ++, increments and returns the value before incrementing.
* <br><strong>Syntax:</strong> <code>x++</code>
* <br><strong>Method:</strong> <code>T getAndIncrement(L x);</code>.
*/
- GET_AND_INCREMENT(".++", "getAndIncrement", INCREMENT),
+ GET_AND_INCREMENT(".++", "getAndIncrement", INCREMENT, 1),
/**
* Prefix --, decrements and returns the value after decrementing.
* <br><strong>Syntax:</strong> <code>--x</code>
* <br><strong>Method:</strong> <code>T decrementAndGet(L x);</code>.
*/
- DECREMENT_AND_GET("--.", "decrementAndGet", DECREMENT),
+ DECREMENT_AND_GET("--.", "decrementAndGet", DECREMENT, 1),
/**
* Postfix --, decrements and returns the value before decrementing.
* <br><strong>Syntax:</strong> <code>x--</code>
* <br><strong>Method:</strong> <code>T getAndDecrement(L x);</code>.
*/
- GET_AND_DECREMENT(".--", "getAndDecrement", DECREMENT),
+ GET_AND_DECREMENT(".--", "getAndDecrement", DECREMENT, 1),
/**
* Marker for side effect.
* @param argc the number of parameters for the method
*/
JexlOperator(final String o, final String m, final int argc) {
- this.operator = o;
- this.methodName = m;
- this.arity = argc;
- this.base = null;
+ this(o, m, null, argc);
}
/**
- * Creates a side-effect operator.
+ * Creates a side effect operator with arity == 2.
*
* @param o the operator name
* @param m the method name associated to this operator in a JexlArithmetic
* @param b the base operator, ie + for +=
*/
JexlOperator(final String o, final String m, final JexlOperator b) {
+ this(o, m, b, 2);
+ }
+
+ /**
+ * Creates a side effect operator.
+ *
+ * @param o the operator name
+ * @param m the method name associated to this operator in a JexlArithmetic
+ * @param b the base operator, ie + for +=
+ * @param a the operator arity
+ */
+ JexlOperator(final String o, final String m, final JexlOperator b, final int a) {
this.operator = o;
this.methodName = m;
- this.arity = 2;
+ this.arity = a;
this.base = b;
}
protected int depth = Integer.MAX_VALUE;
/** Arrow symbol. */
protected String arrow = "->";
+ /** EOL. */
+ protected String lf = "\n";
/**
* Creates a Debugger.
return this;
}
+ /**
+ * Sets this debugger line-feed string.
+ * @param lf the string used to delineate lines (usually "\" or "")
+ * @return this debugger instance
+ */
+ public Debugger lineFeed(final String lf) {
+ this.lf = lf;
+ return this;
+ }
+
/**
* Checks if a child node is the cause to debug & adds its representation to the rebuilt expression.
* @param node the child node
*/
private static boolean isStatement(JexlNode child) {
return child instanceof ASTJexlScript
- || child instanceof ASTJexlLambda
|| child instanceof ASTBlock
|| child instanceof ASTIfStatement
|| child instanceof ASTForeachStatement
if (!isStatement(child) && !semicolTerminated(builder)) {
builder.append(';');
if (indent > 0) {
- builder.append('\n');
+ builder.append(lf);
} else {
builder.append(' ');
}
builder.append('{');
if (indent > 0) {
indentLevel += 1;
- builder.append('\n');
+ builder.append(lf);
} else {
builder.append(' ');
}
}
}
}
+ if (!Character.isSpaceChar(builder.charAt(builder.length() - 1))) {
+ builder.append(' ');
+ }
builder.append('}');
return data;
}
return check(node, "break", data);
}
+
@Override
protected Object visit(final ASTForeachStatement node, final Object data) {
+ final int form = node.getLoopForm();
builder.append("for(");
- accept(node.jjtGetChild(0), data);
- builder.append(" : ");
- accept(node.jjtGetChild(1), data);
- builder.append(") ");
- if (node.jjtGetNumChildren() > 2) {
- acceptStatement(node.jjtGetChild(2), data);
+ final JexlNode body;
+ if (form == 0) {
+ // for( .. : ...)
+ accept(node.jjtGetChild(0), data);
+ builder.append(" : ");
+ accept(node.jjtGetChild(1), data);
+ builder.append(") ");
+ body = node.jjtGetNumChildren() > 2? node.jjtGetChild(2) : null;
+ } else {
+ // for( .. ; ... ; ..)
+ int nc = 0;
+ // first child is var declaration(s)
+ final JexlNode vars = (form & 1) != 0 ? node.jjtGetChild(nc++) : null;
+ final JexlNode predicate = (form & 2) != 0 ? node.jjtGetChild(nc++) : null;
+ // the loop step
+ final JexlNode step = (form & 4) != 0 ? node.jjtGetChild(nc++) : null;
+ // last child is body
+ body = (form & 8) != 0 ? node.jjtGetChild(nc) : null;
+ if (vars != null) {
+ accept(vars, data);
+ }
+ builder.append("; ");
+ if (predicate != null) {
+ accept(predicate, data);
+ }
+ builder.append("; ");
+ if (step != null) {
+ accept(step, data);
+ }
+ builder.append(") ");
+ }
+ // the body
+ if (body != null) {
+ accept(body, data);
} else {
builder.append(';');
}
return data;
}
+ @Override
+ protected Object visit(final ASTDefineVars node, final Object data) {
+ final int num = node.jjtGetNumChildren();
+ if (num > 0) {
+ // var, let, const
+ accept(node.jjtGetChild(0), data);
+ for (int i = 1; i < num; ++i) {
+ builder.append(", ");
+ JexlNode child = node.jjtGetChild(i);
+ if (child instanceof ASTAssignment) {
+ ASTAssignment assign = (ASTAssignment) child;
+ int nc = assign.jjtGetNumChildren();
+ ASTVar var = (ASTVar) assign.jjtGetChild(0);
+ builder.append(var.getName());
+ if (nc > 1) {
+ builder.append(" = ");
+ accept(assign.jjtGetChild(1), data);
+ }
+ } else if (child instanceof ASTVar) {
+ ASTVar var = (ASTVar) child;
+ builder.append(var.getName());
+ } else {
+ // that's odd
+ accept(child, data);
+ }
+ }
+ }
+ return data;
+ }
+
@Override
protected Object visit(final ASTWhileStatement node, final Object data) {
builder.append("while (");
@Override
protected Object visit(final ASTGetDecrementNode node, final Object data) {
- return postfixChild(node, "++", data);
+ return postfixChild(node, "--", data);
}
@Override
@Override
protected Object visit(final ASTIncrementGetNode node, final Object data) {
- return prefixChild(node, "--", data);
+ return prefixChild(node, "++", data);
}
@Override
*/
private static final class UberspectHolder {
/** The default uberspector that handles all introspection patterns. */
- private static final Uberspect UBERSPECT =
+ static final Uberspect UBERSPECT =
new Uberspect(LogFactory.getLog(JexlEngine.class),
JexlUberspect.JEXL_STRATEGY,
JexlPermissions.parse());
import java.util.Iterator;
import java.util.concurrent.Callable;
+import java.util.function.Consumer;
import org.apache.commons.jexl3.JexlArithmetic;
import org.apache.commons.jexl3.JexlContext;
public Object getAttribute(final Object object, final Object attribute) {
return getAttribute(object, attribute, null);
}
+
/**
* Sets an attribute of an object.
*
return node.getLoopForm() == 0 ? forIterator(node, data) : forLoop(node, data);
}
- protected Object forIterator(final ASTForeachStatement node, final Object data) {
+ private Object forIterator(final ASTForeachStatement node, final Object data) {
Object result = null;
/* first objectNode is the loop variable */
final ASTReference loopReference = (ASTReference) node.jjtGetChild(0);
return result;
}
- protected Object forLoop(final ASTForeachStatement node, final Object data) {
+ private Object forLoop(final ASTForeachStatement node, final Object data) {
Object result = null;
int nc;
int form = node.getLoopForm();
}
// initialize after eventual creation of local lexical frame
init.jjtAccept(this, data);
- nc = 1;
+ // other inits
+ for (JexlNode moreAssignment = node.jjtGetChild(nc);
+ moreAssignment instanceof ASTAssignment;
+ moreAssignment = node.jjtGetChild(++nc)) {
+ moreAssignment.jjtAccept(this, data);
+ }
} else {
locals = null;
nc = 0;
}
- Object forEach = null;
try {
// the loop condition
final JexlNode predicate = (form & 2) != 0? node.jjtGetChild(nc++) : null;
// the loop step
final JexlNode step = (form & 4) != 0? node.jjtGetChild(nc++) : null;
// last child is body
- final JexlNode statement = (form & 8) != 0 ? node.jjtGetChild(nc++) : null;
- // get an iterator for the collection/array etc via the introspector.
- final Iterator<?> itemsIterator = null;
- int cnt = 0;
+ final JexlNode statement = (form & 8) != 0 ? node.jjtGetChild(nc) : null;
+ // while(predicate())...
while (predicate == null || arithmetic.toBoolean(predicate.jjtAccept(this, data))) {
cancelCheck(node);
// the body
}
}
} finally {
- // closeable iterator handling
- closeIfSupported(forEach);
// restore lexical frame
if (locals != null) {
block = block.pop();
JexlNode objectNode = null;
JexlNode ptyNode = null;
StringBuilder ant = null;
- boolean antish = !(parent instanceof ASTReference);
+ boolean antish = !(parent instanceof ASTReference) && options.isAntish();
int v = 1;
main:
for (int c = 0; c < numChildren; c++) {
}
final ASTIdentifier afirst = (ASTIdentifier) first;
ant = new StringBuilder(afirst.getName());
- // skip the else...*
- // *... and continue
- if (!options.isAntish()) {
- antish = false;
- }
continue;
// skip the first node case since it was trialed in jjtAccept above and returned null
}
object = context.get(ant.toString());
} else if (c != numChildren - 1) {
// only the last one may be null
- ptyNode = objectNode;
+ ptyNode = c == 0 && numChildren > 1 ? node.jjtGetChild(1) : objectNode;
break; //
}
}
return executeAssign(node, JexlOperator.INCREMENT_AND_GET, data);
}
- /**
- * Helper for postfix assignment operators.
- * @param operator the operator
- * @return true if operator is a postfix operator (x++, y--)
- */
- private static boolean isPostfix(JexlOperator operator) {
- return operator == JexlOperator.GET_AND_INCREMENT || operator == JexlOperator.GET_AND_DECREMENT;
- }
-
/**
* Executes an assignment with an optional side-effect operator.
* @param node the node
cancelCheck(node);
// left contains the reference to assign to
final JexlNode left = node.jjtGetChild(0);
- ASTIdentifier var = null;
+ final ASTIdentifier var;
Object object = null;
- int symbol = -1;
+ final int symbol;
// check var decl with assign is ok
if (left instanceof ASTIdentifier) {
var = (ASTIdentifier) left;
return undefinedVariable(var, var.getName());
}
}
+ } else {
+ var = null;
+ symbol = -1;
}
boolean antish = options.isAntish();
// 0: determine initial object & property:
final int last = left.jjtGetNumChildren() - 1;
// right is the value expression to assign
- Object right = node.jjtGetNumChildren() < 2? null: node.jjtGetChild(1).jjtAccept(this, data);
- // previous value for postfix assignment operators (x++, x--)
- Object actual = null;
+ final Object right = node.jjtGetNumChildren() < 2? null: node.jjtGetChild(1).jjtAccept(this, data);
+ // actual value to return, right in most cases
+ Object actual = right;
// a (var?) v = ... expression
if (var != null) {
if (symbol >= 0) {
// check we are not assigning a symbol itself
if (last < 0) {
- if (assignop != null) {
- final Object self = actual = getVariable(frame, block, var);
- right = operators.tryAssignOverload(node, assignop, self, right);
- if (right == JexlOperator.ASSIGN) {
- return self;
+ if (assignop == null) {
+ // make the closure accessible to itself, ie capture the currently set variable after frame creation
+ if (right instanceof Closure) {
+ ((Closure) right).setCaptured(symbol, right);
}
+ frame.set(symbol, right);
+ } else {
+ // go through potential overload
+ final Object self = getVariable(frame, block, var);
+ final Consumer<Object> f = r -> frame.set(symbol, r);
+ actual = operators.tryAssignOverload(node, assignop, f, self, right);
}
- frame.set(symbol, right);
- // make the closure accessible to itself, ie capture the currently set variable after frame creation
- if (right instanceof Closure) {
- ((Closure) right).setCaptured(symbol, right);
- }
- return isPostfix(assignop)? actual : right; // 1
+ return actual; // 1
}
object = getVariable(frame, block, var);
// top level is a symbol, can not be an antish var
antish = false;
} else {
// check we are not assigning direct global
+ final String name = var.getName();
if (last < 0) {
- if (assignop != null) {
- final Object self = actual = context.get(var.getName());
- right = operators.tryAssignOverload(node, assignop, self, right);
- if (right == JexlOperator.ASSIGN) {
- return self;
- }
+ if (assignop == null) {
+ setContextVariable(node, name, right);
+ } else {
+ // go through potential overload
+ final Object self = context.get(name);
+ final Consumer<Object> f = r -> setContextVariable(node, name, r);
+ actual = operators.tryAssignOverload(node, assignop, f, self, right);
}
- setContextVariable(node, var.getName(), right);
- return isPostfix(assignop)? actual : right; // 2
+ return actual; // 2
}
- object = context.get(var.getName());
+ object = context.get(name);
// top level accesses object, can not be an antish var
if (object != null) {
antish = false;
if (propertyId != null) {
// deal with creating/assignining antish variable
if (antish && ant != null && object == null && !propertyId.isSafe() && !propertyId.isExpression()) {
- if (last > 0) {
- ant.append('.');
- }
+ ant.append('.');
ant.append(propertyId.getName());
- if (assignop != null) {
+ final String name = ant.toString();
+ if (assignop == null) {
+ setContextVariable(propertyNode, name, right);
+ } else {
final Object self = actual = context.get(ant.toString());
- right = operators.tryAssignOverload(node, assignop, self, right);
- if (right == JexlOperator.ASSIGN) {
- return self;
- }
+ final JexlNode pnode = propertyNode;
+ final Consumer<Object> assign = r -> setContextVariable(pnode, name, r);
+ actual = operators.tryAssignOverload(node, assignop, assign, self, right);
}
- setContextVariable(propertyNode, ant.toString(), right);
- return isPostfix(assignop)? actual : right; // 3
+ return actual; // 3
}
// property of an object ?
property = evalIdentifier(propertyId);
return unsolvableProperty(objectNode, "<null>.<?>", true, null);
}
// 3: one before last, assign
- if (assignop != null) {
- final Object self = actual = getAttribute(object, property, propertyNode);
- right = operators.tryAssignOverload(node, assignop, self, right);
- if (right == JexlOperator.ASSIGN) {
- return self;
- }
+ if (assignop == null) {
+ setAttribute(object, property, right, propertyNode);
+ } else {
+ final Object self = getAttribute(object, property, propertyNode);
+ final Object o = object;
+ final JexlNode n = propertyNode;
+ final Consumer<Object> assign = r -> setAttribute(o, property, r, n);
+ actual = operators.tryAssignOverload(node, assignop, assign, self, right);
}
- setAttribute(object, property, right, propertyNode);
- return isPostfix(assignop)? actual : right; // 4
+ return actual;
+ }
+
+ @Override
+ protected Object visit(final ASTDefineVars node, final Object data) {
+ final int argc = node.jjtGetNumChildren();
+ Object result = null;
+ for (int i = 0; i < argc; i++) {
+ result = node.jjtGetChild(i).jjtAccept(this, data);
+ }
+ return result;
}
@Override
return call.eval(mCALL);
}
// functor is a var, may be method is a global one ?
- if (isavar && target == context) {
+ if (isavar) {
if (call.isContextMethod(methodName, argv)) {
return call.eval(methodName);
}
}
}
boolean narrow = false;
- JexlMethod ctor = null;
Funcall funcall = null;
+ JexlMethod ctor;
while (true) {
// try as stated
ctor = uberspect.getConstructor(target, argv);
*/
package org.apache.commons.jexl3.internal;
-import java.lang.reflect.Method;
import org.apache.commons.jexl3.JexlArithmetic;
import org.apache.commons.jexl3.JexlEngine;
import org.apache.commons.jexl3.JexlException;
import org.apache.commons.jexl3.introspection.JexlUberspect;
import org.apache.commons.jexl3.parser.JexlNode;
+import java.lang.reflect.Method;
+import java.util.function.Consumer;
+
/**
* Helper class to deal with operator overloading and specifics.
* @since 3.0
* @param args the arguments
* @return the result of the operator evaluation or TRY_FAILED
*/
- protected Object tryOverload(final JexlNode node, final JexlOperator operator, final Object... args) {
+ protected Object tryOverload(final JexlNode node, final JexlOperator operator, Object... args) {
if (operators != null && operators.overloads(operator)) {
final JexlArithmetic arithmetic = interpreter.arithmetic;
final boolean cache = interpreter.cache;
final JexlMethod vm = operators.getOperator(operator, args);
if (vm != null && !isArithmetic(vm)) {
final Object result = vm.invoke(arithmetic, args);
- if (cache) {
+ if (cache && !vm.tryFailed(result)) {
node.jjtSetValue(vm);
}
return result;
}
} catch (final Exception xany) {
- return interpreter.operatorError(node, operator, xany);
+ // ignore return if lenient, will return try_failed
+ interpreter.operatorError(node, operator, xany);
}
}
return JexlEngine.TRY_FAILED;
}
+ /**
+ * Helper for postfix assignment operators.
+ * @param operator the operator
+ * @return true if operator is a postfix operator (x++, y--)
+ */
+ private static boolean isPostfix(JexlOperator operator) {
+ return operator == JexlOperator.GET_AND_INCREMENT || operator == JexlOperator.GET_AND_DECREMENT;
+ }
+
+ /**
+ * Tidy arguments based on operator arity.
+ * <p>The interpreter may add a null to the arguments of operator expecting only one parameter.</p>
+ * @param operator the operator
+ * @param args the arguements (as seen by the interpreter)
+ * @return the tidied arguments
+ */
+ private Object[] arguments(JexlOperator operator, Object...args) {
+ return operator.getArity() == 1 && args.length > 1 ? new Object[]{args[0]} : args;
+ }
+
/**
* Evaluates an assign operator.
* <p>
* JexlEngine.TRY_FAILED if no operation was performed,
* the value to use as the side effect argument otherwise
*/
- protected Object tryAssignOverload(final JexlNode node, final JexlOperator operator, final Object...args) {
+ protected Object tryAssignOverload(final JexlNode node,
+ final JexlOperator operator,
+ final Consumer<Object> assignFun,
+ final Object...args) {
final JexlArithmetic arithmetic = interpreter.arithmetic;
if (args.length < operator.getArity()) {
return JexlEngine.TRY_FAILED;
}
- // try to call overload with side effect
- Object result = tryOverload(node, operator, args);
- if (result != JexlEngine.TRY_FAILED) {
- return result;
- }
- // call base operator
- final JexlOperator base = operator.getBaseOperator();
- if (base == null) {
- throw new IllegalArgumentException("must be called with a side-effect operator");
- }
- if (operators != null && operators.overloads(base)) {
- // in case there is an overload on the base operator
- try {
- final JexlMethod vm = operators.getOperator(base, args);
- if (vm != null) {
- result = vm.invoke(arithmetic, args);
- if (result != JexlEngine.TRY_FAILED) {
- return result;
- }
+ Object result;
+ try {
+ // if some overloads exist...
+ if (operators != null) {
+ // try to call overload with side effect; the object is modified
+ result = tryOverload(node, operator, arguments(operator, args));
+ if (result != JexlEngine.TRY_FAILED) {
+ return result; // 1
+ }
+ // try to call base overload (ie + for +=)
+ final JexlOperator base = operator.getBaseOperator();
+ if (base != null && operators.overloads(base)) {
+ result = tryOverload(node, base, arguments(base, args));
+ if (result != JexlEngine.TRY_FAILED) {
+ assignFun.accept(result);
+ return isPostfix(operator) ? args[0] : result; // 2
}
- } catch (final Exception xany) {
- interpreter.operatorError(node, base, xany);
}
}
// base eval
- try {
- switch (operator) {
- case SELF_ADD:
- return arithmetic.add(args[0], args[1]);
- case SELF_SUBTRACT:
- return arithmetic.subtract(args[0], args[1]);
- case SELF_MULTIPLY:
- return arithmetic.multiply(args[0], args[1]);
- case SELF_DIVIDE:
- return arithmetic.divide(args[0], args[1]);
- case SELF_MOD:
- return arithmetic.mod(args[0], args[1]);
- case SELF_AND:
- return arithmetic.and(args[0], args[1]);
- case SELF_OR:
- return arithmetic.or(args[0], args[1]);
- case SELF_XOR:
- return arithmetic.xor(args[0], args[1]);
- case SELF_SHIFTLEFT:
- return arithmetic.shiftLeft(args[0], args[1]);
- case SELF_SHIFTRIGHT:
- return arithmetic.shiftRight(args[0], args[1]);
- case SELF_SHIFTRIGHTU:
- return arithmetic.shiftRightUnsigned(args[0], args[1]);
- case INCREMENT_AND_GET:
- case GET_AND_INCREMENT:
- return arithmetic.increment(args[0]);
- case DECREMENT_AND_GET:
- case GET_AND_DECREMENT:
- return arithmetic.decrement(args[0]);
- default:
- // unexpected, new operator added?
- throw new UnsupportedOperationException(operator.getOperatorSymbol());
+ switch (operator) {
+ case SELF_ADD:
+ result = arithmetic.add(args[0], args[1]);
+ break;
+ case SELF_SUBTRACT:
+ result = arithmetic.subtract(args[0], args[1]);
+ break;
+ case SELF_MULTIPLY:
+ result = arithmetic.multiply(args[0], args[1]);
+ break;
+ case SELF_DIVIDE:
+ result = arithmetic.divide(args[0], args[1]);
+ break;
+ case SELF_MOD:
+ result = arithmetic.mod(args[0], args[1]);
+ break;
+ case SELF_AND:
+ result = arithmetic.and(args[0], args[1]);
+ break;
+ case SELF_OR:
+ result = arithmetic.or(args[0], args[1]);
+ break;
+ case SELF_XOR:
+ result = arithmetic.xor(args[0], args[1]);
+ break;
+ case SELF_SHIFTLEFT:
+ result = arithmetic.shiftLeft(args[0], args[1]);
+ break;
+ case SELF_SHIFTRIGHT:
+ result = arithmetic.shiftRight(args[0], args[1]);
+ break;
+ case SELF_SHIFTRIGHTU:
+ result = arithmetic.shiftRightUnsigned(args[0], args[1]);
+ break;
+ case INCREMENT_AND_GET:
+ result = arithmetic.increment(args[0]);
+ break;
+ case DECREMENT_AND_GET:
+ result = arithmetic.decrement(args[0]);
+ break;
+ case GET_AND_INCREMENT:
+ result = args[0];
+ assignFun.accept(arithmetic.increment(result));
+ return result; // 3
+ case GET_AND_DECREMENT: {
+ result = args[0];
+ assignFun.accept(arithmetic.decrement(result));
+ return result; // 4
+ }
+ default:
+ // unexpected, new operator added?
+ throw new UnsupportedOperationException(operator.getOperatorSymbol());
}
+ assignFun.accept(result);
+ return result; // 5
} catch (final Exception xany) {
- interpreter.operatorError(node, base, xany);
+ interpreter.operatorError(node, operator, xany);
}
return JexlEngine.TRY_FAILED;
}
public Scope(final Scope scope, final String... parameters) {
if (parameters != null) {
parms = parameters.length;
- namedVariables = new LinkedHashMap<String, Integer>();
+ namedVariables = new LinkedHashMap<>();
for (int p = 0; p < parms; ++p) {
namedVariables.put(parameters[p], p);
}
final Integer pr = parent.getSymbol(name, true);
if (pr != null) {
if (capturedVariables == null) {
- capturedVariables = new LinkedHashMap<Integer, Integer>();
+ capturedVariables = new LinkedHashMap<>();
}
if (namedVariables == null) {
- namedVariables = new LinkedHashMap<String, Integer>();
+ namedVariables = new LinkedHashMap<>();
}
register = namedVariables.size();
namedVariables.put(name, register);
*/
public int declareParameter(final String name) {
if (namedVariables == null) {
- namedVariables = new LinkedHashMap<String, Integer>();
+ namedVariables = new LinkedHashMap<>();
} else if (vars > 0) {
throw new IllegalStateException("cant declare parameters after variables");
}
* @param name the variable name
* @return the register index storing this variable
*/
- public int declareVariable(final String name, boolean lexical, boolean constant) {
+ public int declareVariable(final String name) {
if (namedVariables == null) {
- namedVariables = new LinkedHashMap<String, Integer>();
+ namedVariables = new LinkedHashMap<>();
}
Integer register = namedVariables.get(name);
if (register == null) {
final Integer pr = parent.getSymbol(name, true);
if (pr != null) {
if (capturedVariables == null) {
- capturedVariables = new LinkedHashMap<Integer, Integer>();
+ capturedVariables = new LinkedHashMap<>();
}
capturedVariables.put(register, pr);
}
*/
public String[] getCapturedVariables() {
if (capturedVariables != null) {
- final List<String> captured = new ArrayList<String>(vars);
+ final List<String> captured = new ArrayList<>(vars);
for (final Map.Entry<String, Integer> entry : namedVariables.entrySet()) {
final int symnum = entry.getValue();
if (symnum >= parms && capturedVariables.containsKey(symnum)) {
}
}
if (!captured.isEmpty()) {
- return captured.toArray(new String[captured.size()]);
+ return captured.toArray(new String[0]);
}
}
return EMPTY_STRS;
* @param bound number of known bound parameters (curry)
* @return the parameter names
*/
- protected String[] getParameters(final int bound) {
+ String[] getParameters(final int bound) {
final int unbound = parms - bound;
if ((namedVariables == null) || (unbound <= 0)) {
return EMPTY_STRS;
if ((namedVariables == null) || (vars <= 0)) {
return EMPTY_STRS;
}
- final List<String> locals = new ArrayList<String>(vars);
+ final List<String> locals = new ArrayList<>(vars);
for (final Map.Entry<String, Integer> entry : namedVariables.entrySet()) {
final int symnum = entry.getValue();
if (symnum >= parms && (capturedVariables == null || !capturedVariables.containsKey(symnum))) {
locals.add(entry.getKey());
}
}
- return locals.toArray(new String[locals.size()]);
+ return locals.toArray(new String[0]);
}
}
import org.apache.commons.jexl3.parser.*;
/**
- * Fully abstract to avoid public interface exposition.
+ * Concrete visitor base, used for feature and operator controllers.
*/
public class ScriptVisitor extends ParserVisitor {
/**
return visitNode(node, data);
}
+ @Override
+ protected Object visit(final ASTDefineVars node, final Object data) {
+ return visitNode(node, data);
+ }
+
@Override
protected Object visit(final ASTReference node, final Object data) {
return visitNode(node, data);
*/
CompositeExpression(final int[] counters, final List<TemplateExpression> list, final TemplateExpression src) {
super(src);
- this.exprs = list.toArray(new TemplateExpression[list.size()]);
+ this.exprs = list.toArray(new TemplateExpression[0]);
this.meta = (counters[ExpressionType.DEFERRED.getIndex()] > 0 ? 2 : 0)
| (counters[ExpressionType.IMMEDIATE.getIndex()] > 0 ? 1 : 0);
}
jpe += 1;
}
}
- source = blocks.toArray(new Block[blocks.size()]);
- exprs = uexprs.toArray(new TemplateExpression[uexprs.size()]);
+ source = blocks.toArray(new Block[0]);
+ exprs = uexprs.toArray(new TemplateExpression[0]);
}
/**
}
@Override
public Set<Entry<MethodKey, Method>> entrySet() {
- return null;
+ return Collections.emptySet();
}
@Override public Method get(Object name) {
return CACHE_MISS;
* @return the array of field names
*/
String[] getFieldNames() {
- return fieldCache.keySet().toArray(new String[fieldCache.size()]);
+ return fieldCache.keySet().toArray(new String[0]);
}
/**
* @return the array of method names
*/
String[] getMethodNames() {
- return byName.keySet().toArray(new String[byName.size()]);
+ return byName.keySet().toArray(new String[0]);
}
/**
if (methodList != null) {
cacheEntry = methodKey.getMostSpecificMethod(methodList);
}
- if (cacheEntry == null) {
- byKey.put(methodKey, CACHE_MISS);
- } else {
- byKey.put(methodKey, cacheEntry);
- }
+ byKey.put(methodKey, cacheEntry == null? CACHE_MISS : cacheEntry);
} catch (final MethodKey.AmbiguousException ae) {
// that's a miss :-)
byKey.put(methodKey, CACHE_MISS);
*/
private static void create(final ClassMap cache, final JexlPermissions permissions, Class<?> clazz, final Log log) {
//
- // Build a list of all elements in the class hierarchy. This one is bottom-first (i.e. we start
+ // Build a list of all elements in the class hierarchy. This one is bottom-first; we start
// with the actual declaring class and its interfaces and then move up (superclass etc.) until we
// hit java.lang.Object. That is important because it will give us the methods of the declaring class
// which might in turn be abstract further up the tree.
// We also ignore all SecurityExceptions that might happen due to SecurityManager restrictions.
//
for (Class<?> classToReflect = clazz; classToReflect != null; classToReflect = classToReflect.getSuperclass()) {
- if (Modifier.isPublic(classToReflect.getModifiers())) {
+ if (Modifier.isPublic(classToReflect.getModifiers()) && ClassTool.isExported(classToReflect)) {
populateWithClass(cache, permissions, classToReflect, log);
}
final Class<?>[] interfaces = classToReflect.getInterfaces();
}
end += 1;
}
- final Method[] lmn = lm.subList(start, end).toArray(new Method[end - start]);
+ final Method[] lmn = lm.subList(start, end).toArray(new Method[0]);
cache.byName.put(name, lmn);
start = end;
}
--- /dev/null
+/*
+ * 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.commons.jexl3.internal.introspection;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+/**
+ * Utility for Java9+ backport in Java8 of class and module related methods.
+ */
+class ClassTool {
+ /** The Class.getModule() method. */
+ private static final MethodHandle GET_MODULE;
+ /** The Class.getPackageName() method. */
+ private static final MethodHandle GET_PKGNAME;
+ /** The Module.isExported(String packageName) method. */
+ private static final MethodHandle IS_EXPORTED;
+ static {
+ final MethodHandles.Lookup LOOKUP= MethodHandles.lookup();
+ MethodHandle getModule = null;
+ MethodHandle getPackageName = null;
+ MethodHandle isExported = null;
+ try {
+ Class<?> modulec = ClassTool.class.getClassLoader().loadClass("java.lang.Module");
+ if (modulec != null) {
+ getModule = LOOKUP.findVirtual(Class.class, "getModule", MethodType.methodType(modulec));
+ if (getModule != null) {
+ getPackageName = LOOKUP.findVirtual(Class.class, "getPackageName", MethodType.methodType(String.class));
+ if (getPackageName != null) {
+ isExported = LOOKUP.findVirtual(modulec, "isExported", MethodType.methodType(boolean.class, String.class));
+ }
+ }
+ }
+ } catch (Exception e) {
+ // ignore all
+ }
+ GET_MODULE = getModule;
+ GET_PKGNAME = getPackageName;
+ IS_EXPORTED = isExported;
+ }
+
+ /**
+ * Checks whether a class is exported by its module (java 9+).
+ * The code performs the following sequence through reflection (since the same jar can run
+ * on a Java8 or Java9+ runtime and the module features does not exist on 8).
+ * <code>
+ * Module module = declarator.getModule();
+ * return module.isExported(declarator.getPackageName());
+ * </code>
+ * This is required since some classes and methods may not be exported thus not callable through
+ * reflection.
+ * @param declarator the class
+ * @return true if class is exported or no module support exists
+ */
+ static boolean isExported(Class<?> declarator) {
+ if (IS_EXPORTED != null) {
+ try {
+ final Object module = GET_MODULE.invoke(declarator);
+ if (module != null) {
+ final String pkgName = (String) GET_PKGNAME.invoke(declarator);
+ return (Boolean) IS_EXPORTED.invoke(module, pkgName);
+ }
+ } catch (Throwable e) {
+ // ignore
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Gets the package name of a class (class.getPackage() may return null).
+ * @param clz the class
+ * @return the class package name
+ */
+ static String getPackageName(Class<?> clz) {
+ String pkgName = "";
+ if (clz != null) {
+ // use native if we can
+ if (GET_PKGNAME != null) {
+ try {
+ return (String) GET_PKGNAME.invoke(clz);
+ } catch(Throwable xany) {
+ return "";
+ }
+ }
+ // remove array
+ Class<?> clazz = clz;
+ while(clazz.isArray()) {
+ clazz = clazz.getComponentType();
+ }
+ // mimic getPackageName()
+ if (clazz.isPrimitive()) {
+ return "java.lang";
+ }
+ // remove enclosing
+ Class<?> walk = clazz.getEnclosingClass();
+ while(walk != null) {
+ clazz = walk;
+ walk = walk.getEnclosingClass();
+ }
+ Package pkg = clazz.getPackage();
+ // pkg may be null for unobvious reasons
+ if (pkg == null) {
+ String name = clazz.getName();
+ int dot = name.lastIndexOf('.');
+ if (dot > 0) {
+ pkgName = name.substring(0, dot);
+ }
+ } else {
+ pkgName = pkg.getName();
+ }
+ }
+ return pkgName;
+ }
+
+}
}
}
// try to find one
- ctor = key.getMostSpecificConstructor(l.toArray(new Constructor<?>[l.size()]));
+ ctor = key.getMostSpecificConstructor(l.toArray(new Constructor<?>[0]));
if (ctor != null) {
constructorsMap.put(key, ctor);
} else {
package org.apache.commons.jexl3.internal.introspection;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
* not be altered using an instance of permissions using {@link JexlPermissions#parse(String...)}.</p>
*/
public class Permissions implements JexlPermissions {
- /**
- * Java9 introduced Class.getPackageName(), use it if it exists.
- */
- private static final MethodHandle GETPKGNAME = getPackageNameHandle();
- static MethodHandle getPackageNameHandle() {
- MethodHandle mh;
- try {
- Method m = Class.class.getMethod("getPackageName");
- mh = MethodHandles.lookup().unreflect(m);
- } catch(Exception xm) {
- mh = null;
- }
- return mh;
- }
-
/**
* Equivalent of @NoJexl on a class in a package.
*/
return allowed == null? Collections.emptySet() : Collections.unmodifiableSet(allowed);
}
-
- /**
- * Gets the package name of a class (class.getPackage() may return null).
- * @param clz the class
- * @return the class package name
- */
- static String getPackageName(Class<?> clz) {
- String pkgName = "";
- if (clz != null) {
- // use native if we can
- if (GETPKGNAME != null) {
- try {
- return (String) GETPKGNAME.invokeWithArguments(clz);
- } catch(Throwable xany) {
- return "";
- }
- }
- // remove array
- Class<?> clazz = clz;
- while(clazz.isArray()) {
- clazz = clazz.getComponentType();
- }
- // mimic getPackageName()
- if (clazz.isPrimitive()) {
- return "java.lang";
- }
- // remove enclosing
- Class<?> walk = clazz.getEnclosingClass();
- while(walk != null) {
- clazz = walk;
- walk = walk.getEnclosingClass();
- }
- Package pkg = clazz.getPackage();
- // pkg may be null for unobvious reasons
- if (pkg == null) {
- String name = clazz.getName();
- int dot = name.lastIndexOf('.');
- if (dot > 0) {
- pkgName = name.substring(0, dot);
- }
- } else {
- pkgName = pkg.getName();
- }
- }
- return pkgName;
- }
-
/**
* Gets the package constraints.
* @param packageName the package name
* @return the class constraints instance, not-null.
*/
private NoJexlClass getNoJexl(Class<?> clazz) {
- String pkgName = getPackageName(clazz);
+ String pkgName = ClassTool.getPackageName(clazz);
NoJexlPackage njp = getNoJexlPackage(pkgName);
if (njp != null) {
NoJexlClass njc = njp.getNoJexl(clazz);
* @return true if allowed, false otherwise
*/
private boolean wildcardAllow(Class<?> clazz) {
- return wildcardAllow(allowed, getPackageName(clazz));
+ return wildcardAllow(allowed, ClassTool.getPackageName(clazz));
}
/**
if (nojexl != null) {
return true;
}
- NoJexlPackage njp = packages.get(getPackageName(clazz));
+ NoJexlPackage njp = packages.get(ClassTool.getPackageName(clazz));
return njp != null && Objects.equals(NOJEXL_CLASS, njp.getNoJexl(clazz));
}
final Object obj,
final Object identifier) {
if (obj != null) {
+ final Class<?> clazz = obj instanceof Class<?>? (Class<?>) obj : obj.getClass();
if (identifier != null) {
final String property = identifier.toString();
- final String actual = sandbox.read(obj.getClass(), property);
+ final String actual = sandbox.read(clazz, property);
if (actual != null) {
// no transformation, strict equality: use identifier before string conversion
final Object pty = eq(actual, property) ? identifier : actual;
return uberspect.getPropertyGet(resolvers, obj, pty);
}
} else {
- final String actual = sandbox.read(obj.getClass(), null);
+ final String actual = sandbox.read(clazz, null);
if (actual != JexlSandbox.NULL) {
return uberspect.getPropertyGet(resolvers, obj, null);
}
final Object identifier,
final Object arg) {
if (obj != null) {
+ final Class<?> clazz = obj instanceof Class<?>? (Class<?>) obj : obj.getClass();
if (identifier != null) {
final String property = identifier.toString();
- final String actual = sandbox.write(obj.getClass(), property);
+ final String actual = sandbox.write(clazz, property);
if (actual != null) {
// no transformation, strict equality: use identifier before string conversion
final Object pty = eq(actual, property) ? identifier : actual;
return uberspect.getPropertySet(resolvers, obj, pty, arg);
}
} else {
- final String actual = sandbox.write(obj.getClass(), null);
+ final String actual = sandbox.write(clazz, null);
if (actual != JexlSandbox.NULL) {
return uberspect.getPropertySet(resolvers, obj, null, arg);
}
/**
* The map from arithmetic classes to overloaded operator sets.
* <p>
- * This keeps track of which operator methods are overloaded per JexlArithemtic classes
+ * This map keeps track of which operator methods are overloaded per JexlArithmetic classes
* allowing a fail fast test during interpretation by avoiding seeking a method when there is none.
*/
private final Map<Class<? extends JexlArithmetic>, Set<JexlOperator>> operatorMap;
}
/**
- * Gets the lexical unit currently used by this parser.
+ * Gets the lexical unit used by this parser.
* @return the named register map
*/
protected LexicalUnit getUnit() {
if (scope == null) {
scope = new Scope(null);
}
- final int symbol = scope.declareVariable(name, true, true);
+ final int symbol = scope.declareVariable(name);
variable.setSymbol(symbol, name);
variable.setLexical(true);
if (scope.isCapturedSymbol(symbol)) {
if (scope == null) {
scope = new Scope(null);
}
- final int symbol = scope.declareVariable(name, lexical, constant);
+ final int symbol = scope.declareVariable(name);
variable.setSymbol(symbol, name);
variable.setLexical(lexical);
variable.setConstant(constant);
protected void throwParsingException(final Token parsed) {
JexlInfo xinfo = null;
String msg = "unrecoverable state";
- JexlException.Parsing xparse = null;
Token token = parsed;
if (token == null) {
token = this.getToken(0);
<*> TOKEN :
{
< STRING_LITERAL:
- "\"" (~["\"","\\","\n","\r","\u2028","\u2029"] | "\\" ~["\n","\r","\u2028","\u2029"])* "\""
+ "\"" (~["\"","\\","\n","\r","\t","\f","\b","\u2028","\u2029"]
+ | "\\" ~["\n","\r","\t","\f","\b","\u2028","\u2029"])* "\""
|
- "'" (~["'","\\","\n","\r","\u2028","\u2029"] | "\\" ~["\n","\r","\u2028","\u2029"])* "'"
+ "'" (~["'","\\","\n","\r","\t","\f","\b","\u2028","\u2029"]
+ | "\\" ~["\n","\r","\t","\f","\b","\u2028","\u2029"])* "'"
> { popDot(); } /* Revert state to default if was DOT_ID. */
}
<*> TOKEN :
{
< REGEX_LITERAL:
- "~" "/" (~["/","\n","\r","\u2028","\u2029"] | "\\" "/" )* "/"
+ "~" "/" (~["/","\n","\r","\t","\f","\b","\u2028","\u2029"] | "\\" "/" )* "/"
> { popDot(); } /* Revert state to default if was DOT_ID. */
}
void Var() #void : {}
{
- <VAR> DefineVar() (<COMMA> DefineVar())*
+ (<VAR> DefineVar() (<COMMA> DefineVar())*) #DefineVars(>1)
|
- <LET> DefineLet() (<COMMA> DefineLet())*
+ (<LET> DefineLet() (<COMMA> DefineLet())*) #DefineVars(>1)
|
- <CONST> DefineConst() (<COMMA> DefineConst())*
+ (<CONST> DefineConst() (<COMMA> DefineConst())*) #DefineVars(>1)
}
void DefineVar() #void : {}
*/
package org.apache.commons.jexl3.parser;
-import org.apache.commons.jexl3.JexlOperator;
-
/**
* Fully abstract to avoid public interface exposition.
*/
protected abstract Object visit(ASTVar node, Object data);
+ protected abstract Object visit(ASTDefineVars node, Object data);
+
protected abstract Object visit(ASTReference node, Object data);
protected abstract Object visit(ASTTernaryNode node, Object data);
protected abstract Object visit(ASTSetShiftRightUnsignedNode node, final Object data);
- protected abstract Object visit(final ASTGetDecrementNode node, final Object data);
+ protected abstract Object visit(ASTGetDecrementNode node, final Object data);
- protected abstract Object visit(final ASTGetIncrementNode node, final Object data);
+ protected abstract Object visit(ASTGetIncrementNode node, final Object data);
- protected abstract Object visit(final ASTDecrementGetNode node, final Object data);
+ protected abstract Object visit(ASTDecrementGetNode node, final Object data);
- protected abstract Object visit(final ASTIncrementGetNode node, final Object data);
+ protected abstract Object visit(ASTIncrementGetNode node, final Object data);
protected abstract Object visit(ASTJxltLiteral node, Object data);
private FactorySingletonHolder() {}
/** The engine factory singleton instance. */
- private static final JexlScriptEngineFactory DEFAULT_FACTORY = new JexlScriptEngineFactory();
+ static final JexlScriptEngineFactory DEFAULT_FACTORY = new JexlScriptEngineFactory();
}
/**
private EngineSingletonHolder() {}
/** The JEXL engine singleton instance. */
- private static final JexlEngine DEFAULT_ENGINE = new JexlBuilder().logger(LOG).cache(CACHE_SIZE).create();
+ static final JexlEngine DEFAULT_ENGINE = new JexlBuilder().logger(LOG).cache(CACHE_SIZE).create();
}
/**
import java.math.BigInteger;
import java.math.MathContext;
import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
}
}
+ @Test
+ public void testMinusMinusPrefix() throws Exception {
+ asserter.setVariable("aByte", new Byte((byte) 2));
+ asserter.setVariable("aShort", new Short((short) 3));
+ asserter.setVariable("anInteger", new Integer(4));
+ asserter.setVariable("aLong", new Long(5));
+ asserter.setVariable("aFloat", new Float(6.6));
+ asserter.setVariable("aDouble", new Double(7.7));
+ asserter.setVariable("aBigInteger", new BigInteger("8"));
+ asserter.setVariable("aBigDecimal", new BigDecimal("9.9"));
+ asserter.setVariable("aString", "forty-two");
+
+ asserter.assertExpression("--aByte", new Byte((byte) 1));
+ asserter.assertExpression("--aShort", new Short((short) 2));
+ asserter.assertExpression("--anInteger", new Integer(3));
+ asserter.assertExpression("--aLong", new Long(4));
+ asserter.assertExpression("--aFloat", new Float(5.6));
+ asserter.assertExpression("--aDouble", new Double(6.7));
+ asserter.assertExpression("--aBigInteger", new BigInteger("7"));
+ asserter.assertExpression("--aBigDecimal", new BigDecimal("8.9"));
+
+ asserter.failExpression("aString--", "--", String::contains);
+ }
+
+ @Test
+ public void testMinusMinusPostfix() throws Exception {
+ asserter.setVariable("aByte", new Byte((byte) 2));
+ asserter.setVariable("aShort", new Short((short) 3));
+ asserter.setVariable("anInteger", new Integer(4));
+ asserter.setVariable("aLong", new Long(5));
+ asserter.setVariable("aFloat", new Float(6.6));
+ asserter.setVariable("aDouble", new Double(7.7));
+ asserter.setVariable("aBigInteger", new BigInteger("8"));
+ asserter.setVariable("aBigDecimal", new BigDecimal("9.9"));
+ asserter.setVariable("aString", "forty-two");
+
+ asserter.assertExpression("aByte--",new Byte((byte) 2));
+ asserter.assertExpression("aShort--", new Short((short) 3));
+ asserter.assertExpression("anInteger--", new Integer(4));
+ asserter.assertExpression("aLong--", new Long(5));
+ asserter.assertExpression("aFloat--", new Float(6.6));
+ asserter.assertExpression("aDouble--", new Double(7.7));
+ asserter.assertExpression("aBigInteger--", new BigInteger("8"));
+ asserter.assertExpression("aBigDecimal--", new BigDecimal("9.9"));
+
+ asserter.assertExpression("aByte", new Byte((byte) 1));
+ asserter.assertExpression("aShort", new Short((short) 2));
+ asserter.assertExpression("anInteger", new Integer(3));
+ asserter.assertExpression("aLong", new Long(4));
+ asserter.assertExpression("aFloat", new Float(5.6));
+ asserter.assertExpression("aDouble", new Double(6.7));
+ asserter.assertExpression("aBigInteger", new BigInteger("7"));
+ asserter.assertExpression("aBigDecimal", new BigDecimal("8.9"));
+
+ asserter.failExpression("aString--", "--", String::contains);
+ }
+
+ @Test
+ public void testPlusPlusPrefix() throws Exception {
+ asserter.setVariable("aByte", new Byte((byte) 0));
+ asserter.setVariable("aShort", new Short((short) 1));
+ asserter.setVariable("anInteger", new Integer(2));
+ asserter.setVariable("aLong", new Long(3));
+ asserter.setVariable("aFloat", new Float(4.4));
+ asserter.setVariable("aDouble", new Double(5.5));
+ asserter.setVariable("aBigInteger", new BigInteger("6"));
+ asserter.setVariable("aBigDecimal", new BigDecimal("7.7"));
+ asserter.setVariable("aString", "forty-two");
+
+ asserter.assertExpression("++aByte", new Byte((byte) 1));
+ asserter.assertExpression("++aShort", new Short((short) 2));
+ asserter.assertExpression("++anInteger", new Integer(3));
+ asserter.assertExpression("++aLong", new Long(4));
+ asserter.assertExpression("++aFloat", new Float(5.4));
+ asserter.assertExpression("++aDouble", new Double(6.5));
+ asserter.assertExpression("++aBigInteger", new BigInteger("7"));
+ asserter.assertExpression("++aBigDecimal", new BigDecimal("8.7"));
+
+ asserter.failExpression("++aString", "++", String::contains);
+ }
+
+ @Test
+ public void testPlusPlusPostfix() throws Exception {
+ asserter.setVariable("aByte", new Byte((byte) 0));
+ asserter.setVariable("aShort", new Short((short) 1));
+ asserter.setVariable("anInteger", new Integer(2));
+ asserter.setVariable("aLong", new Long(3));
+ asserter.setVariable("aFloat", new Float(4.4));
+ asserter.setVariable("aDouble", new Double(5.5));
+ asserter.setVariable("aBigInteger", new BigInteger("6"));
+ asserter.setVariable("aBigDecimal", new BigDecimal("7.7"));
+ asserter.setVariable("aString", "forty-two");
+
+ asserter.assertExpression("aByte++", new Byte((byte) 0));
+ asserter.assertExpression("aShort++", new Short((short) 1));
+ asserter.assertExpression("anInteger++", new Integer(2));
+ asserter.assertExpression("aLong++", new Long(3));
+ asserter.assertExpression("aFloat++", new Float(4.4));
+ asserter.assertExpression("aDouble++", new Double(5.5));
+ asserter.assertExpression("aBigInteger++", new BigInteger("6"));
+ asserter.assertExpression("aBigDecimal++", new BigDecimal("7.7"));
+
+ asserter.assertExpression("aByte", new Byte((byte) 1));
+ asserter.assertExpression("aShort", new Short((short) 2));
+ asserter.assertExpression("anInteger", new Integer(3));
+ asserter.assertExpression("aLong", new Long(4));
+ asserter.assertExpression("aFloat", new Float(5.4));
+ asserter.assertExpression("aDouble", new Double(6.5));
+ asserter.assertExpression("aBigInteger", new BigInteger("7"));
+ asserter.assertExpression("aBigDecimal", new BigDecimal("8.7"));
+
+ asserter.failExpression("aString++", "++", String::contains);
+ }
+
+ @Test
+ public void testNarrowBig() throws Exception {
+ List<String> ls = Arrays.asList("zero", "one", "two");
+ asserter.setVariable("list",ls);
+ asserter.setVariable("aBigDecimal", new BigDecimal("1"));
+ asserter.setVariable("aBigInteger", new BigDecimal("1"));
+ asserter.assertExpression("list.get(aBigDecimal)", "one");
+ asserter.assertExpression("list.get(aBigInteger)", "one");
+ }
+
+ @Test
+ public void testNarrowBigDecimal() throws Exception {
+ asserter.setVariable("bi420", BigInteger.valueOf(420));
+ asserter.setVariable("bi10", BigInteger.valueOf(10));
+ asserter.setVariable("bd420", new BigDecimal("420"));
+ asserter.setVariable("bd10", new BigDecimal("10"));
+ asserter.assertExpression("420 / bi10", 42);
+ asserter.assertExpression("420l / bi10", 42L);
+ asserter.assertExpression("bi420 / 420", 1);
+ asserter.assertExpression("bi420 / 420l", 1L);
+ asserter.assertExpression("bd420 / 10", new BigDecimal("42"));
+ }
+
/**
* test some simple mathematical calculations
*/
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
+
+import org.apache.commons.jexl3.internal.Debugger;
import org.junit.Assert;
import org.junit.Test;
super("ForEachTest");
}
+ @Test public void testForLoop0b0() {
+ String src = "(l)->{ for(let x = 0, y = 0; x < 4; ++x) l.add(x) }";
+ JexlEngine jexl = new JexlBuilder().safe(false).create();
+ JexlScript script = jexl.createScript(src);
+ List<Integer> l = new ArrayList<>();
+ Object result = script.execute(null, l);
+ Assert.assertNotNull(result);
+ Assert.assertEquals(Arrays.asList(0, 1, 2, 3), l);
+ String resrc = toString(script);
+ Assert.assertEquals(src, resrc);
+ }
+
+ @Test public void testForLoop0a() {
+ String src = "(l)->{ for(let x = 0; x < 4; ++x) { l.add(x); } }";
+ JexlEngine jexl = new JexlBuilder().safe(false).create();
+ JexlScript script = jexl.createScript(src);
+ List<Integer> l = new ArrayList<>();
+ Object result = script.execute(null, l);
+ Assert.assertNotNull(result);
+ Assert.assertEquals(Arrays.asList(0, 1, 2, 3), l);
+ String resrc = toString(script);
+ Assert.assertEquals(src, resrc);
+ }
+
@Test
public void testForEachWithEmptyStatement() throws Exception {
final JexlScript e = JEXL.createScript("for(item : list) ;");
*/
package org.apache.commons.jexl3;
+import org.apache.commons.jexl3.internal.Engine32;
+import org.apache.commons.jexl3.internal.OptionsContext;
+import org.apache.commons.jexl3.introspection.JexlSandbox;
+import org.junit.Assert;
+import org.junit.Test;
+
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
-import org.apache.commons.jexl3.internal.Engine32;
-import org.apache.commons.jexl3.internal.OptionsContext;
-import org.junit.Assert;
import static org.junit.Assert.assertEquals;
-import org.junit.Test;
/**
* Test cases for reported issue between JEXL-300 and JEXL-399.
Assert.assertEquals("phone", xvar.getVariable());
}
}
+
+ public static class TestObject374 {
+ private String name;
+ private TestObject374 nested = null;
+ public String getName() {
+ return name;
+ }
+ public void setName(String pName) {
+ this.name = pName;
+ }
+ public TestObject374 getNested() {
+ return nested;
+ }
+ public void setNested(TestObject374 pNested) {
+ nested = pNested;
+ }
+ }
+
+ @Test
+ public void test374() {
+ JexlEngine engine = new JexlBuilder().cache(512).strict(true).silent(false).antish(false).safe(false).create();
+ // Create expression to evaluate 'name'
+ JexlExpression expr = engine.createExpression("nested.name");
+ // Create an object with getter for name
+ TestObject374 myObject = new TestObject374();
+ myObject.setName("John");
+ JexlContext context = new ObjectContext<TestObject374>(engine, myObject);
+ // Expect an exception because nested is null, so we are doing null.name
+ try {
+ Object result = expr.evaluate(context);
+ Assert.fail("An exception expected, but got: " + result);
+ } catch (JexlException ex) {
+ // Expected
+ //ex.printStackTrace();
+ }
+ }
+
+ @Test
+ public void test375() {
+ JexlSandbox jexlSandbox = new JexlSandbox(false);
+ jexlSandbox.allow(Type375.class.getName());
+ JexlEngine engine = new JexlBuilder().sandbox(jexlSandbox).create();
+
+ JexlContext context = new MapContext();
+ context.set("Type", Type375.class);
+
+ Object result = engine.createScript("Type.valueOf('DOMICILE')").execute(context);
+ Assert.assertEquals(Type375.DOMICILE, result);
+
+ result = engine.createScript("Type.DOMICILE").execute(context);
+ Assert.assertEquals(Type375.DOMICILE, result);
+ }
+
+ public enum Type375 {
+ DELIVERY_ADDRESS,
+ DOMICILE
+ }
+
}
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
+import org.apache.commons.jexl3.internal.Debugger;
import org.apache.commons.jexl3.internal.OptionsContext;
import org.apache.commons.jexl3.internal.Util;
import org.apache.commons.jexl3.internal.introspection.Uberspect;
* Eases the implementation of main methods to debug.
*/
public class JexlTestCase {
- // The default options: all tests where engine must lexicality is
+ // The default options: all tests where engine lexicality is
// important can be identified by the builder calling lexical(...).
static {
JexlOptions.setDefaultFlags("-safe", "+lexical");
protected final JexlEngine JEXL;
public JexlTestCase(final String name) {
- this(name, new JexlBuilder().cache(128).create());
+ this(name, new JexlBuilder().permissions(null).cache(128).create());
}
protected JexlTestCase(final String name, final JexlEngine jexl) {
return arg.trim().replaceAll("\\s+", " ");
}
+ public static String toString(JexlScript script) {
+ Debugger d = new Debugger().lineFeed("").indentation(0);
+ d.debug(script);
+ return d.toString();
+ }
+
/**
* A very secure singleton.
*/
final JexlEngine jexl = new JexlBuilder().strict(true).create();
final JexlScript script = jexl.createScript("let x = 32; (()->{ for(let x : null) { let c = 0; { return x; } } } )(); ");
Assert.assertNotNull(script);
- String dbg = script.getParsedText();
+ String dbg = JexlTestCase.toString(script);
String src = script.getSourceText();
Assert.assertTrue(JexlTestCase.equalsIgnoreWhiteSpace(src, dbg));
}
*/
package org.apache.commons.jexl3;
+import java.io.BufferedReader;
import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
import java.net.URL;
+
import org.junit.Assert;
import org.junit.Test;
* Tests for JexlScript
* @since 1.1
*/
-@SuppressWarnings({"UnnecessaryBoxing", "AssertEqualsBetweenInconvertibleTypes"})
+@SuppressWarnings({"AssertEqualsBetweenInconvertibleTypes"})
public class ScriptTest extends JexlTestCase {
static final String TEST1 = "src/test/scripts/test1.jexl";
static final String TEST_ADD = "src/test/scripts/testAdd.jexl";
+ static final String TEST_JSON = "src/test/scripts/httpPost.jexl";
// test class for testScriptUpdatesContext
// making this class private static will cause the test to fail.
* Test creating a script from spaces.
*/
@Test
- public void testSpacesScript() throws Exception {
+ public void testSpacesScript() {
final String code = " ";
final JexlScript s = JEXL.createScript(code);
Assert.assertNotNull(s);
* Test creating a script from a string.
*/
@Test
- public void testSimpleScript() throws Exception {
+ public void testSimpleScript() {
final String code = "while (x < 10) x = x + 1;";
final JexlScript s = JEXL.createScript(code);
final JexlContext jc = new MapContext();
- jc.set("x", new Integer(1));
+ jc.set("x",1);
final Object o = s.execute(jc);
- Assert.assertEquals("Result is wrong", new Integer(10), o);
+ Assert.assertEquals("Result is wrong", 10, o);
Assert.assertEquals("getText is wrong", code, s.getSourceText());
}
@Test
- public void testScriptFromFile() throws Exception {
+ public void testScriptJsonFromFileJexl() {
+ final File testScript = new File(TEST_JSON);
+ final JexlScript s = JEXL.createScript(testScript);
+ final JexlContext jc = new MapContext();
+ jc.set("httpr", new HttpPostRequest());
+ Object result = s.execute(jc);
+ Assert.assertNotNull(result);
+ Assert.assertEquals("{ \"id\": 101}", result);
+ }
+
+ @Test
+ public void testScriptJsonFromFileJava() {
+ final String testScript ="httpr.execute('https://jsonplaceholder.typicode.com/posts', null)";
+ final JexlScript s = JEXL.createScript(testScript);
+ final JexlContext jc = new MapContext();
+ jc.set("httpr", new HttpPostRequest());
+ Object result = s.execute(jc);
+ Assert.assertNotNull(result);
+ Assert.assertEquals("{ \"id\": 101}", result);
+ }
+
+ /**
+ * An object to call from.
+ */
+ public static class HttpPostRequest {
+ public static String execute(String url, String data) throws IOException {
+ return httpPostRequest(url, data);
+ }
+ }
+
+ /**
+ * HTTP post.
+ * @param sURL the url
+ * @param jsonData some json data
+ * @return the result
+ * @throws IOException
+ */
+ private static String httpPostRequest(String sURL, String jsonData) throws IOException {
+ URL url = new java.net.URL(sURL);
+ HttpURLConnection con = (HttpURLConnection) url.openConnection();
+ con.setRequestMethod("POST");
+ con.setRequestProperty("Accept", "application/json");
+ // send data
+ if ( jsonData != null ) {
+ con.setRequestProperty("Content-Type", "application/json; utf-8");
+ con.setDoOutput(true);
+
+ OutputStream outputStream = con.getOutputStream();
+ byte[] input = jsonData.getBytes("utf-8");
+ outputStream.write(input, 0, input.length);
+ }
+ // read response
+ int responseCode = con.getResponseCode();
+ InputStream inputStream = null;
+ inputStream = con.getInputStream();
+ StringBuffer response = new java.lang.StringBuffer();
+ if (inputStream != null) {
+ try (BufferedReader in = new java.io.BufferedReader(new java.io.InputStreamReader(inputStream))) {
+ String inputLine = "";
+ while ((inputLine = in.readLine()) != null) {
+ response.append(inputLine);
+ }
+ }
+ }
+ return response.toString();
+ }
+
+
+ @Test
+ public void testScriptFromFile() {
final File testScript = new File(TEST1);
final JexlScript s = JEXL.createScript(testScript);
final JexlContext jc = new MapContext();
jc.set("out", System.out);
final Object result = s.execute(jc);
Assert.assertNotNull("No result", result);
- Assert.assertEquals("Wrong result", new Integer(7), result);
+ Assert.assertEquals("Wrong result", 7, result);
}
@Test
- public void testArgScriptFromFile() throws Exception {
+ public void testArgScriptFromFile() {
final File testScript = new File(TEST_ADD);
final JexlScript s = JEXL.createScript(testScript,"x", "y");
final JexlContext jc = new MapContext();
jc.set("out", System.out);
final Object result = s.execute(jc, 13, 29);
Assert.assertNotNull("No result", result);
- Assert.assertEquals("Wrong result", new Integer(42), result);
+ Assert.assertEquals("Wrong result", 42, result);
}
@Test
jc.set("out", System.out);
final Object result = s.execute(jc);
Assert.assertNotNull("No result", result);
- Assert.assertEquals("Wrong result", new Integer(7), result);
+ Assert.assertEquals("Wrong result", 7, result);
}
@Test
jc.set("out", System.out);
final Object result = s.execute(jc, 13, 29);
Assert.assertNotNull("No result", result);
- Assert.assertEquals("Wrong result", new Integer(42), result);
+ Assert.assertEquals("Wrong result", 42, result);
}
@Test
- public void testScriptUpdatesContext() throws Exception {
+ public void testScriptUpdatesContext() {
final String jexlCode = "resultat.setCode('OK')";
final JexlExpression e = JEXL.createExpression(jexlCode);
final JexlScript s = JEXL.createScript(jexlCode);
import java.util.List;
import java.util.Map;
import java.util.Collections;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.commons.jexl3.internal.introspection.NoJexlTest;
+import org.apache.commons.jexl3.jexl342.OptionalArithmetic;
import org.apache.commons.jexl3.junit.Asserter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Assert;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
Object result;
script = jexl.createScript("(x, y)->{ x += y }");
result = script.execute(jc, 3115, 15);
- Assert.assertEquals(3115 + 15, result);
+ Assert.assertEquals(3115 + 15, result);
final Var v0 = new Var(3115);
result = script.execute(jc, v0, new Var(15));
Assert.assertEquals(result, v0);
- Assert.assertEquals(3115 + 15, v0.value);
+ Assert.assertEquals(3115 + 15, v0.value);
script = jexl.createScript("(x, y)->{ x -= y}");
result = script.execute(jc, 3115, 15);
- Assert.assertEquals(3115 - 15, result);
+ Assert.assertEquals(3115 - 15, result);
final Var v1 = new Var(3115);
result = script.execute(jc, v1, new Var(15));
Assert.assertNotEquals(result, v1); // not a real side effect
- Assert.assertEquals(3115 - 15, ((Var) result).value);
+ Assert.assertEquals(3115 - 15, ((Var) result).value);
script = jexl.createScript("(x, y)->{ x *= y }");
result = script.execute(jc, 3115, 15);
- Assert.assertEquals(3115 * 15, result);
+ Assert.assertEquals(3115 * 15, result);
final Var v2 = new Var(3115);
result = script.execute(jc, v2, new Var(15));
Assert.assertEquals(result, v2);
- Assert.assertEquals(3115 * 15, v2.value);
+ Assert.assertEquals(3115 * 15, v2.value);
script = jexl.createScript("(x, y)->{ x /= y }");
result = script.execute(jc, 3115, 15);
- Assert.assertEquals(3115 / 15, result);
+ Assert.assertEquals(3115 / 15, result);
final Var v3 = new Var(3115);
result = script.execute(jc, v3, new Var(15));
Assert.assertEquals(result, v3);
- Assert.assertEquals(3115 / 15, v3.value);
+ Assert.assertEquals(3115 / 15, v3.value);
script = jexl.createScript("(x, y)->{ x %= y }");
result = script.execute(jc, 3115, 15);
- Assert.assertEquals(3115 % 15, result);
+ Assert.assertEquals(3115 % 15, result);
final Var v4 = new Var(3115);
result = script.execute(jc, v4, new Var(15));
Assert.assertEquals(result, v4);
- Assert.assertEquals(3115 % 15, v4.value);
+ Assert.assertEquals(3115 % 15, v4.value);
script = jexl.createScript("(x, y)->{ x &= y }");
result = script.execute(jc, 3115, 15);
- Assert.assertEquals(3115L & 15, result);
+ Assert.assertEquals(3115L & 15, result);
final Var v5 = new Var(3115);
result = script.execute(jc, v5, new Var(15));
Assert.assertEquals(result, v5);
- Assert.assertEquals(3115 & 15, v5.value);
+ Assert.assertEquals(3115 & 15, v5.value);
script = jexl.createScript("(x, y)->{ x |= y }");
result = script.execute(jc, 3115, 15);
- Assert.assertEquals(3115L | 15, result);
+ Assert.assertEquals(3115L | 15, result);
final Var v6 = new Var(3115);
result = script.execute(jc, v6, new Var(15));
Assert.assertEquals(result, v6);
- Assert.assertEquals(3115L | 15, v6.value);
+ Assert.assertEquals(3115L | 15, v6.value);
script = jexl.createScript("(x, y)->{ x ^= y }");
result = script.execute(jc, 3115, 15);
- Assert.assertEquals(3115L ^ 15, result);
+ Assert.assertEquals(3115L ^ 15, result);
final Var v7 = new Var(3115);
result = script.execute(jc, v7, new Var(15));
Assert.assertEquals(result, v7);
- Assert.assertEquals(3115L ^ 15, v7.value);
+ Assert.assertEquals(3115L ^ 15, v7.value);
+
+ script = jexl.createScript("(x, y)->{ x >>>= y }");
+ result = script.execute(jc, 234453115, 5);
+ Assert.assertEquals(234453115L >>> 5, result);
+ final Var v8 = new Var(234453115);
+ result = script.execute(jc, v8, 5);
+ Assert.assertEquals(result, v8);
+ Assert.assertEquals(234453115L >>> 5, v8.value);
+
+ script = jexl.createScript("(x, y)->{ x >>= y }");
+ result = script.execute(jc, 435566788L, 7);
+ Assert.assertEquals(435566788L >> 7, result);
+ final Var v9 = new Var(435566788);
+ result = script.execute(jc, v9, 7);
+ Assert.assertEquals(result, v9);
+ Assert.assertEquals(435566788L >> 7, v9.value);
+
+ script = jexl.createScript("(x, y)->{ x <<= y }");
+ result = script.execute(jc, 3115, 2);
+ Assert.assertEquals(3115L << 2, result);
+ final Var v10 = new Var(3115);
+ result = script.execute(jc, v10, 2);
+ Assert.assertEquals(result, v10);
+ Assert.assertEquals(3115L << 2, v10.value);
}
+ @Test
+ public void testIncrementSelf() throws Exception {
+ final JexlEngine jexl = new JexlBuilder().cache(64).arithmetic(new SelfArithmetic(false)).create();
+ final JexlContext jc = null;
+ runSelfIncrement(jexl, jc);
+ runSelfIncrement(jexl, jc);
+ }
+
+ @Test
+ public void testIncrementSelfNoCache() throws Exception {
+ final JexlEngine jexl = new JexlBuilder().cache(0).arithmetic(new SelfArithmetic(false)).create();
+ final JexlContext jc = null;
+ runSelfIncrement(jexl, jc);
+ }
+
+ protected void runSelfIncrement(final JexlEngine jexl, final JexlContext jc) {
+ JexlScript script = jexl.createScript("x -> [+x, +(x++), +x]");
+ final Var v11 = new Var(3115);
+ final AtomicInteger i11 = new AtomicInteger(3115);
+ for(Object v : Arrays.asList(v11, i11)) {
+ Object result = script.execute(jc, v);
+ Assert.assertTrue(result instanceof int[]);
+ int[] r = (int[]) result;
+ Assert.assertEquals(3115, r[0]);
+ Assert.assertEquals(3115, r[1]);
+ Assert.assertEquals(3116, r[2]);
+ }
+
+ script = jexl.createScript("x -> [+x, +(++x), +x]");
+ final Var v12 = new Var(3189);
+ final AtomicInteger i12 = new AtomicInteger(3189);
+ for(Object v : Arrays.asList(v12, i12)) {
+ Object result = script.execute(jc, v);
+ Assert.assertTrue(result instanceof int[]);
+ int[] r = (int[]) result;
+ Assert.assertEquals(3189, r[0]);
+ Assert.assertEquals(3190, r[1]);
+ Assert.assertEquals(3190, r[2]);
+ }
+
+ script = jexl.createScript("x -> [+x, +(x--), +x]");
+ final Var v13 = new Var(3115);
+ for(Object v : Arrays.asList(v13)) {
+ Object result = script.execute(jc, v13);
+ Assert.assertTrue(result instanceof int[]);
+ int[] r = (int[]) result;
+ Assert.assertEquals(3115, r[0]);
+ Assert.assertEquals(3115, r[1]);
+ Assert.assertEquals(3114, r[2]);
+ }
+
+ script = jexl.createScript("x -> [+x, +(--x), +x]");
+ final Var v14 = new Var(3189);
+ for(Object v : Arrays.asList(v14)) {
+ Object result = script.execute(jc, v);
+ Assert.assertTrue(result instanceof int[]);
+ int[] r = (int[]) result;
+ Assert.assertEquals(3189, r[0]);
+ Assert.assertEquals(3188, r[1]);
+ Assert.assertEquals(3188, r[2]);
+ }
+ }
+
@Test
public void testOverrideGetSet() throws Exception {
final JexlEngine jexl = new JexlBuilder().cache(64).arithmetic(new SelfArithmetic(false)).create();
value = v;
}
- @Override
- public String toString() {
+ @Override public String toString() {
return Integer.toString(value);
}
}
// an arithmetic that performs side effects
- public static class SelfArithmetic extends JexlArithmetic {
+ public static class SelfArithmetic extends OptionalArithmetic {
public SelfArithmetic(final boolean strict) {
super(strict);
}
return "VALUE".equals(property)? var.value = v : JexlEngine.TRY_FAILED;
}
- public JexlOperator selfAdd(final Var lhs, final Var rhs) {
+ public Var selfAdd(final Var lhs, final Var rhs) {
lhs.value += rhs.value;
- return JexlOperator.ASSIGN;
+ return lhs;
}
// for kicks, this one does not side effect but overloads nevertheless
return new Var(lhs.value - rhs.value);
}
- public JexlOperator selfDivide(final Var lhs, final Var rhs) {
+ public Var selfDivide(final Var lhs, final Var rhs) {
lhs.value /= rhs.value;
- return JexlOperator.ASSIGN;
+ return lhs;
}
- public JexlOperator selfMultiply(final Var lhs, final Var rhs) {
+ public Var selfMultiply(final Var lhs, final Var rhs) {
lhs.value *= rhs.value;
- return JexlOperator.ASSIGN;
+ return lhs;
}
- public JexlOperator selfMod(final Var lhs, final Var rhs) {
+ public Var selfMod(final Var lhs, final Var rhs) {
lhs.value %= rhs.value;
- return JexlOperator.ASSIGN;
+ return lhs;
}
public Var and(final Var lhs, final Var rhs) {
return new Var(lhs.value & rhs.value);
}
- public JexlOperator selfAnd(final Var lhs, final Var rhs) {
+ public Var selfAnd(final Var lhs, final Var rhs) {
lhs.value &= rhs.value;
- return JexlOperator.ASSIGN;
+ return lhs;
}
public Var or(final Var lhs, final Var rhs) {
return new Var(lhs.value | rhs.value);
}
- public JexlOperator selfOr(final Var lhs, final Var rhs) {
+ public Var selfOr(final Var lhs, final Var rhs) {
lhs.value |= rhs.value;
- return JexlOperator.ASSIGN;
+ return lhs;
}
public Var xor(final Var lhs, final Var rhs) {
return new Var(lhs.value ^ rhs.value);
}
- public JexlOperator selfXor(final Var lhs, final Var rhs) {
+ public Var selfXor(final Var lhs, final Var rhs) {
lhs.value ^= rhs.value;
- return JexlOperator.ASSIGN;
+ return lhs;
+ }
+
+ public Var shiftLeft(final Var lhs, final int rhs) {
+ return new Var(lhs.value << rhs);
+ }
+
+ public Var selfShiftLeft(final Var lhs, final int rhs) {
+ lhs.value <<= rhs;
+ return lhs;
+ }
+
+ public Var shiftRight(final Var lhs, final int rhs) {
+ return new Var(lhs.value >> rhs);
+ }
+
+ public Var selfShiftRight(final Var lhs, final int rhs) {
+ lhs.value >>= rhs;
+ return lhs;
+ }
+
+ public Var shiftRightUnsigned(final Var lhs, final int rhs) {
+ return new Var(lhs.value >>> rhs);
+ }
+
+ public Var selfShiftRightUnsigned(final Var lhs, final int rhs) {
+ lhs.value >>>= rhs;
+ return lhs;
+ }
+
+ public int increment(final Var lhs) {
+ return lhs.value + 1;
+ }
+
+ public int decrement(final Var lhs) {
+ return lhs.value - 1;
+ }
+
+ public int incrementAndGet(AtomicInteger i) {
+ return i.incrementAndGet();
+ }
+
+ public int getAndIncrement(AtomicInteger i) {
+ return i.getAndIncrement();
+ }
+
+ public int positivize(Var n) {
+ return n.value;
+ }
+
+ public int positivize(Number n) {
+ return n.intValue();
}
}
super(astrict);
}
- public JexlOperator selfAdd(final Collection<String> c, final String item) throws IOException {
+ public Object selfAdd(final Collection<String> c, final String item) throws IOException {
c.add(item);
- return JexlOperator.ASSIGN;
+ return c;
}
- public JexlOperator selfAdd(final Appendable c, final String item) throws IOException {
+ public Object selfAdd(final Appendable c, final String item) throws IOException {
c.append(item);
- return JexlOperator.ASSIGN;
+ return c;
}
@Override
}
if (c instanceof Appendable) {
((Appendable) c).append(item);
- return JexlOperator.ASSIGN;
+ return c;
}
return JexlEngine.TRY_FAILED;
}
@Test
public void testGetPackageName() {
final String PKG = "org.apache.commons.jexl3.internal.introspection";
- String pkg = Permissions.getPackageName(Outer.class);
+ String pkg = ClassTool.getPackageName(Outer.class);
Assert.assertEquals(PKG, pkg);
- pkg = Permissions.getPackageName(Outer.Inner.class);
+ pkg = ClassTool.getPackageName(Outer.Inner.class);
Assert.assertEquals(PKG, pkg);
Outer[] oo = new Outer[0];
- pkg = Permissions.getPackageName(oo.getClass());
+ pkg = ClassTool.getPackageName(oo.getClass());
Assert.assertEquals(PKG, pkg);
Outer.Inner[] ii = new Outer.Inner[0];
- pkg = Permissions.getPackageName(ii.getClass());
+ pkg = ClassTool.getPackageName(ii.getClass());
Assert.assertEquals(PKG, pkg);
- pkg = Permissions.getPackageName(Process.class);
+ pkg = ClassTool.getPackageName(Process.class);
Assert.assertEquals("java.lang", pkg);
- pkg = Permissions.getPackageName(Integer.TYPE);
+ pkg = ClassTool.getPackageName(Integer.TYPE);
Assert.assertEquals("java.lang", pkg);
}
}
return narrowed;
}
+
+ @Override
+ public ArrayBuilder arrayBuilder(final int size) {
+ return new org.apache.commons.jexl3.internal.ArrayBuilder(size) {
+ @Override
+ public void add(Object value) {
+ super.add(star(value));
+ }
+ };
+ }
+
+ @Override
+ public SetBuilder setBuilder(final int size) {
+ return new org.apache.commons.jexl3.internal.SetBuilder(size) {
+ @Override
+ public void add(Object value) {
+ super.add(star(value));
+ }
+ };
+ }
+
+ @Override
+ public MapBuilder mapBuilder(final int size) {
+ return new org.apache.commons.jexl3.internal.MapBuilder(size) {
+ @Override
+ public void put(Object key, Object value) {
+ super.put(key, star(value));
+ }
+ };
+ }
}
import java.lang.reflect.Array;
import java.math.BigDecimal;
+import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
+import java.util.function.BiPredicate;
import org.apache.commons.jexl3.JexlEvalContext;
final JexlArithmetic jexla = engine.getArithmetic();
Assert.assertEquals("expression: " + expression, 0,
((BigDecimal) expected).compareTo(jexla.toBigDecimal(value)));
- }
- if (expected != null && value != null) {
+ } else if (expected instanceof BigInteger) {
+ final JexlArithmetic jexla = engine.getArithmetic();
+ Assert.assertEquals("expression: " + expression, 0,
+ ((BigInteger) expected).compareTo(jexla.toBigInteger(value)));
+ } else if (expected != null && value != null) {
if (expected.getClass().isArray() && value.getClass().isArray()) {
final int esz = Array.getLength(expected);
final int vsz = Array.getLength(value);
* @throws Exception if the expression did not fail or the exception did not match the expected pattern
*/
public void failExpression(final String expression, final String matchException) throws Exception {
+ failExpression(expression, matchException, String::matches);
+ }
+ public void failExpression(final String expression, final String matchException, BiPredicate<String,String> predicate) throws Exception {
try {
final JexlScript exp = engine.createScript(expression);
exp.execute(context);
fail("expression: " + expression);
} catch (final JexlException xjexl) {
- if (matchException != null && !xjexl.getMessage().matches(matchException)) {
+ if (matchException != null && !predicate.test(xjexl.getMessage(), matchException)) {
fail("expression: " + expression + ", expected: " + matchException + ", got " + xjexl.getMessage());
}
}
--- /dev/null
+/*
+ * 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.
+ */
+//-------------------------------------------------------------------
+// send a POST Request
+//-------------------------------------------------------------------
+
+var httpPostRequest = (sURL, jsonData) -> {
+ var url = new("java.net.URL", sURL);
+ var con = url.openConnection();
+ con.setRequestMethod("POST");
+ con.setRequestProperty("Accept", "application/json");
+
+ // send data
+ if ( jsonData != null ) {
+ con.setRequestProperty("Content-Type", "application/json; utf-8");
+ con.setDoOutput(true);
+
+ var outputStream = con.getOutputStream();
+ var input = jsonData.getBytes("utf-8");
+ outputStream.write(input, 0, size(input));
+ }
+
+ // read response
+ var responseCode = con.getResponseCode();
+ var inputStream = null;
+ inputStream = con.getInputStream();
+ var response = new("java.lang.StringBuffer");
+ if (inputStream != null) {
+ var in = new("java.io.BufferedReader", new("java.io.InputStreamReader", inputStream));
+ var inputLine = "";
+ while ((inputLine = in.readLine()) != null) {
+ response.append(inputLine);
+ }
+ in.close();
+ }
+ response.toString();
+}
+
+httpPostRequest("https://jsonplaceholder.typicode.com/posts");