You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by he...@apache.org on 2017/06/28 06:50:26 UTC
svn commit: r1800127 - in /commons/proper/jexl/trunk/src:
main/java/org/apache/commons/jexl3/internal/
main/java/org/apache/commons/jexl3/parser/ site/xdoc/ site/xdoc/reference/
test/java/org/apache/commons/jexl3/
Author: henrib
Date: Wed Jun 28 06:50:25 2017
New Revision: 1800127
URL: http://svn.apache.org/viewvc?rev=1800127&view=rev
Log:
JEXL-226: add ?? operator
Modified:
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Debugger.java
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java
commons/proper/jexl/trunk/src/site/xdoc/changes.xml
commons/proper/jexl/trunk/src/site/xdoc/reference/syntax.xml
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IfTest.java
Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Debugger.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Debugger.java?rev=1800127&r1=1800126&r2=1800127&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Debugger.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Debugger.java Wed Jun 28 06:50:25 2017
@@ -90,12 +90,14 @@ import org.apache.commons.jexl3.parser.A
import org.apache.commons.jexl3.parser.ASTUnaryMinusNode;
import org.apache.commons.jexl3.parser.ASTVar;
import org.apache.commons.jexl3.parser.ASTWhileStatement;
+import org.apache.commons.jexl3.parser.ASTAnnotatedStatement;
+import org.apache.commons.jexl3.parser.ASTAnnotation;
+import org.apache.commons.jexl3.parser.ASTNullpNode;
+
import org.apache.commons.jexl3.parser.JexlNode;
import org.apache.commons.jexl3.parser.ParserVisitor;
import java.util.regex.Pattern;
-import org.apache.commons.jexl3.parser.ASTAnnotatedStatement;
-import org.apache.commons.jexl3.parser.ASTAnnotation;
/**
* Helps pinpoint the cause of problems in expressions that fail during evaluation.
@@ -578,13 +580,13 @@ public class Debugger extends ParserVisi
protected Object visit(ASTGTNode node, Object data) {
return infixChildren(node, " > ", false, data);
}
-
+
/** Checks identifiers that contain spaces or punctuation
* (but underscore, at-sign, sharp-sign and dollar).
*/
- protected static final Pattern QUOTED_IDENTIFIER =
+ protected static final Pattern QUOTED_IDENTIFIER =
Pattern.compile("[\\s]|[\\p{Punct}&&[^@#\\$_]]");
-
+
/**
* Checks whether an identifier should be quoted or not.
* @param str the identifier
@@ -900,6 +902,14 @@ public class Debugger extends ParserVisi
return data;
}
+ @Override
+ protected Object visit(ASTNullpNode node, Object data) {
+ accept(node.jjtGetChild(0), data);
+ builder.append("??");
+ accept(node.jjtGetChild(1), data);
+ return data;
+ }
+
@Override
protected Object visit(ASTTrueNode node, Object data) {
check(node, "true", data);
Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java?rev=1800127&r1=1800126&r2=1800127&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java Wed Jun 28 06:50:25 2017
@@ -76,6 +76,7 @@ import org.apache.commons.jexl3.parser.A
import org.apache.commons.jexl3.parser.ASTNSWNode;
import org.apache.commons.jexl3.parser.ASTNotNode;
import org.apache.commons.jexl3.parser.ASTNullLiteral;
+import org.apache.commons.jexl3.parser.ASTNullpNode;
import org.apache.commons.jexl3.parser.ASTNumberLiteral;
import org.apache.commons.jexl3.parser.ASTOrNode;
import org.apache.commons.jexl3.parser.ASTRangeNode;
@@ -849,6 +850,12 @@ public class Interpreter extends Interpr
}
@Override
+ protected Object visit(ASTNullpNode node, Object data) {
+ Object lhs = node.jjtGetChild(0).jjtAccept(this, data);
+ return lhs != null? lhs : node.jjtGetChild(1).jjtAccept(this, data);
+ }
+
+ @Override
protected Object visit(ASTSizeFunction node, Object data) {
try {
Object val = node.jjtGetChild(0).jjtAccept(this, data);
@@ -956,7 +963,7 @@ public class Interpreter extends Interpr
* Check if a null evaluated expression is protected by a ternary expression.
* <p>
* The rationale is that the ternary / elvis expressions are meant for the user to explictly take control
- * over the error generation; ie, ternaries can return null even if the engine in isStrict mode
+ * over the error generation; ie, ternaries can return null even if the engine in strict mode
* would normally throw an exception.
* </p>
* @param node the expression node
@@ -966,7 +973,11 @@ public class Interpreter extends Interpr
for (JexlNode walk = node.jjtGetParent(); walk != null; walk = walk.jjtGetParent()) {
if (walk instanceof ASTTernaryNode) {
return true;
- } else if (!(walk instanceof ASTReference || walk instanceof ASTArrayAccess)) {
+ }
+ if (walk instanceof ASTNullpNode) {
+ return true;
+ }
+ if (!(walk instanceof ASTReference || walk instanceof ASTArrayAccess)) {
break;
}
}
Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt?rev=1800127&r1=1800126&r2=1800127&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt Wed Jun 28 06:50:25 2017
@@ -149,6 +149,7 @@ TOKEN_MGR_DECLS : {
<*> TOKEN : { /* CONDITIONALS */
< QMARK : "?" >
| < ELVIS : "?:" >
+ | < NULLP : "??" >
| < AND : "&&" | "and" >
| < OR : "||" | "or" >
}
@@ -452,6 +453,8 @@ void ConditionalExpression() #void : {}
<QMARK> Expression() <COLON> Expression() #TernaryNode(3)
|
<ELVIS> Expression() #TernaryNode(2)
+ |
+ <NULLP> Expression() #NullpNode(2)
)?
}
Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java?rev=1800127&r1=1800126&r2=1800127&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java Wed Jun 28 06:50:25 2017
@@ -64,6 +64,8 @@ public abstract class ParserVisitor {
protected abstract Object visit(ASTTernaryNode node, Object data);
+ protected abstract Object visit(ASTNullpNode node, Object data);
+
protected abstract Object visit(ASTOrNode node, Object data);
protected abstract Object visit(ASTAndNode node, Object data);
Modified: commons/proper/jexl/trunk/src/site/xdoc/changes.xml
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/site/xdoc/changes.xml?rev=1800127&r1=1800126&r2=1800127&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/site/xdoc/changes.xml (original)
+++ commons/proper/jexl/trunk/src/site/xdoc/changes.xml Wed Jun 28 06:50:25 2017
@@ -25,7 +25,13 @@
<author email="dev@commons.apache.org">Commons Developers</author>
</properties>
<body>
- <release version="3.2" date="unreleased">
+ <release version="3.1.1" date="unreleased">
+ <action dev="henrib" type="add" issue="JEXL-226" due-to="Min Wei">
+ add ?? operator support
+ </action>
+ <action dev="henrib" type="fix" issue="JEXL-230" due-to="Dmitri Blinov">
+ List literal is not mentioned in docs
+ </action>
</release>
<release version="3.1" date="2017-04-14">
<action dev="henrib" type="add" issue="JEXL-222" due-to="Dmitri Blinov">
Modified: commons/proper/jexl/trunk/src/site/xdoc/reference/syntax.xml
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/site/xdoc/reference/syntax.xml?rev=1800127&r1=1800126&r2=1800127&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/site/xdoc/reference/syntax.xml (original)
+++ commons/proper/jexl/trunk/src/site/xdoc/reference/syntax.xml Wed Jun 28 06:50:25 2017
@@ -501,6 +501,26 @@
</td>
</tr>
<tr>
+ <td>Null coalescing operator <code>??</code> </td>
+ <td>
+ The null coalescing operator returns the result of its first operand if it is defined and is not null.
+ <p>When <code>x</code>and<code>y</code>are null or undefined,
+ <code>x ?? 'unknown or null x'</code> evaluates as <code>'unknown or null x'</code>
+ <code>y ?? "default"</code> evaluates as <code>"default"</code>.
+ </p>
+ <p>
+ When <code>var x = 42</code> and <code>var y = "forty-two"</code>,<code>x??"other"</code>
+ evaluates as <code>42</code> and <code>y??"other"</code> evaluates as <code>"forty-two"</code>.
+ </p>
+ <p>
+ <strong>NOTE:</strong> this operator does not behave like the ternary conditional since it
+ does not coerce the first argument to a boolean to evaluate the condition.
+ When <code>var x = false</code> and <code>var y = 0</code>,<code>x??true</code>
+ evaluates as <code>false</code> and <code>y??1</code> evaluates as <code>0</code>.
+ </p>
+ </td>
+ </tr>
+ <tr>
<td>Equality</td>
<td>
The usual <code>==</code> operator can be used as well as the abbreviation <code>eq</code>.
Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IfTest.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IfTest.java?rev=1800127&r1=1800126&r2=1800127&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IfTest.java (original)
+++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IfTest.java Wed Jun 28 06:50:25 2017
@@ -222,13 +222,14 @@ public class IfTest extends JexlTestCase
/**
* Ternary operator condition undefined or null evaluates to false
- * independantly of engine flags.
+ * independently of engine flags; same for null coalescing operator.
* @throws Exception
*/
@Test
public void testTernaryShorthand() throws Exception {
JexlEvalContext jc = new JexlEvalContext();
JexlExpression e = JEXL.createExpression("x.y.z = foo?:'quux'");
+ JexlExpression f = JEXL.createExpression("foo??'quux'");
Object o;
// undefined foo
@@ -239,6 +240,8 @@ public class IfTest extends JexlTestCase
Assert.assertEquals("Should be quux", "quux", o);
o = jc.get("x.y.z");
Assert.assertEquals("Should be quux", "quux", o);
+ o = f.evaluate(jc);
+ Assert.assertEquals("Should be quux", "quux", o);
}
jc.set("foo", null);
@@ -250,6 +253,8 @@ public class IfTest extends JexlTestCase
Assert.assertEquals("Should be quux", "quux", o);
o = jc.get("x.y.z");
Assert.assertEquals("Should be quux", "quux", o);
+ o = f.evaluate(jc);
+ Assert.assertEquals("Should be quux", "quux", o);
}
jc.set("foo", Boolean.FALSE);
@@ -261,6 +266,8 @@ public class IfTest extends JexlTestCase
Assert.assertEquals("Should be quux", "quux", o);
o = jc.get("x.y.z");
Assert.assertEquals("Should be quux", "quux", o);
+ o = f.evaluate(jc);
+ Assert.assertEquals("Should be false", false, o);
}
jc.set("foo", Double.NaN);
@@ -272,6 +279,8 @@ public class IfTest extends JexlTestCase
Assert.assertEquals("Should be quux", "quux", o);
o = jc.get("x.y.z");
Assert.assertEquals("Should be quux", "quux", o);
+ o = f.evaluate(jc);
+ Assert.assertTrue("Should be NaN", Double.isNaN((Double) o));
}
jc.set("foo", "");
@@ -283,6 +292,8 @@ public class IfTest extends JexlTestCase
Assert.assertEquals("Should be quux", "quux", o);
o = jc.get("x.y.z");
Assert.assertEquals("Should be quux", "quux", o);
+ o = f.evaluate(jc);
+ Assert.assertEquals("Should be empty string", "", o);
}
jc.set("foo", "false");
@@ -294,6 +305,8 @@ public class IfTest extends JexlTestCase
Assert.assertEquals("Should be quux", "quux", o);
o = jc.get("x.y.z");
Assert.assertEquals("Should be quux", "quux", o);
+ o = f.evaluate(jc);
+ Assert.assertEquals("Should be 'false'", "false", o);
}
jc.set("foo", 0d);
@@ -305,6 +318,8 @@ public class IfTest extends JexlTestCase
Assert.assertEquals("Should be quux", "quux", o);
o = jc.get("x.y.z");
Assert.assertEquals("Should be quux", "quux", o);
+ o = f.evaluate(jc);
+ Assert.assertEquals("Should be 0", 0.d, o);
}
jc.set("foo", 0);
@@ -316,6 +331,8 @@ public class IfTest extends JexlTestCase
Assert.assertEquals("Should be quux", "quux", o);
o = jc.get("x.y.z");
Assert.assertEquals("Should be quux", "quux", o);
+ o = f.evaluate(jc);
+ Assert.assertEquals("Should be 0", 0, o);
}
jc.set("foo", "bar");
@@ -332,4 +349,22 @@ public class IfTest extends JexlTestCase
debuggerCheck(JEXL);
}
+ @Test
+ public void testNullCoaelescing() throws Exception {
+ Object o;
+ JexlEvalContext jc = new JexlEvalContext();
+ JexlExpression xtrue = JEXL.createExpression("x??true");
+ o = xtrue.evaluate(jc);
+ Assert.assertEquals("Should be true", true, o);
+ jc.set("x", false);
+ o = xtrue.evaluate(jc);
+ Assert.assertEquals("Should be false", false, o);
+ JexlExpression yone = JEXL.createExpression("y??1");
+ o = yone.evaluate(jc);
+ Assert.assertEquals("Should be 1", 1, o);
+ jc.set("y", 0);
+ o = yone.evaluate(jc);
+ Assert.assertEquals("Should be 0", 0, o);
+ debuggerCheck(JEXL);
+ }
}