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 2015/07/11 22:41:10 UTC

svn commit: r1690404 - in /commons/proper/jexl/trunk/src: main/java/org/apache/commons/jexl3/ 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: Sat Jul 11 20:41:09 2015
New Revision: 1690404

URL: http://svn.apache.org/r1690404
Log:
JEXL:
JEXL-162 fixed, specific exception for operator error, behavior as lenient as 2.x, added test, clarified doc and updated changes

Modified:
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlException.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/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/ArithmeticTest.java

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlException.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlException.java?rev=1690404&r1=1690403&r2=1690404&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlException.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlException.java Sat Jul 11 20:41:09 2015
@@ -484,6 +484,48 @@ public class JexlException extends Runti
     }
 
     /**
+     * Thrown when an operator fails.
+     * @since 3.0
+     */
+    public static class Operator extends JexlException {
+        /**
+         * Creates a new Operator exception instance.
+         * @param node  the location information
+         * @param symbol  the operator name
+         * @param cause the exception causing the error
+         */
+        public Operator(JexlNode node, String symbol, Throwable cause) {
+            super(node, symbol, cause);
+        }
+
+        /**
+         * @return the method name
+         */
+        public String getSymbol() {
+            return super.detailedMessage();
+        }
+
+        @Override
+        protected String detailedMessage() {
+            return "error calling operator '" + getSymbol() + "'";
+        }
+    }
+
+    /**
+     * Generates a message for an operator error.
+     * @param node the node where the error occurred
+     * @param symbol the operator name
+     * @return the error message
+     */
+    public static String operatorError(JexlNode node, String symbol) {
+        StringBuilder msg = errorAt(node);
+        msg.append("error calling operator '");
+        msg.append(symbol);
+        msg.append('\'');
+        return msg.toString();
+    }
+
+    /**
      * Thrown to return a value.
      * @since 3.0
      */

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=1690404&r1=1690403&r2=1690404&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 Sat Jul 11 20:41:09 2015
@@ -303,6 +303,24 @@ public class Interpreter extends ParserV
     }
 
     /**
+     * Triggered when an operator fails.
+     * @param node   the node where the error originated from
+     * @param operator the method name
+     * @param cause the cause of error (if any)
+     * @throws JexlException if isStrict
+     */
+    protected void operatorError(JexlNode node, JexlArithmetic.Operator operator, Throwable cause) {
+        if (cause != null) {
+            if (strictEngine) {
+                throw new JexlException.Operator(node, operator.getOperatorSymbol(), cause);
+            }
+            if (!silent) {
+                logger.warn(JexlException.operatorError(node, operator.getOperatorSymbol()));
+            }
+        }
+    }
+
+    /**
      * Triggered when method, function or constructor invocation fails.
      * @param xjexl the JexlException wrapping the original error
      * @return throws JexlException if isStrict, null otherwise
@@ -412,7 +430,7 @@ public class Interpreter extends ParserV
                     return result;
                 }
             } catch (Exception xany) {
-                return invocationFailed(new JexlException(node, operator.getMethodName(), xany));
+                operatorError(node,  operator, xany);
             }
         }
         return JexlEngine.TRY_FAILED;
@@ -450,7 +468,7 @@ public class Interpreter extends ParserV
                     return result;
                 }
             } catch (Exception xany) {
-                return invocationFailed(new JexlException(node, operator.getMethodName(), xany));
+                operatorError(node, operator, xany);
             }
         }
         return JexlEngine.TRY_FAILED;
@@ -1237,15 +1255,13 @@ public class Interpreter extends ParserV
         // boolean and if so, just use it
         JexlMethod vm = uberspect.getMethod(object, "isEmpty", EMPTY_PARAMS);
         if (vm != null && vm.getReturnType() == Boolean.TYPE) {
-            Boolean result;
             try {
-                result = (Boolean) vm.invoke(object, EMPTY_PARAMS);
-            } catch (Exception e) {
-                throw new JexlException(node, "empty() : error executing", e);
+                return (Boolean) vm.invoke(object, EMPTY_PARAMS);
+            } catch (Exception xany) {
+                operatorError(node, Operator.EMPTY, xany);
             }
-            return result;
         }
-        throw new JexlException(node, "empty() : unsupported type : " + object.getClass(), null);
+        return Boolean.FALSE;
     }
 
     /**
@@ -1280,15 +1296,13 @@ public class Interpreter extends ParserV
         // integer and if so, just use it
         JexlMethod vm = uberspect.getMethod(object, "size", EMPTY_PARAMS);
         if (vm != null && vm.getReturnType() == Integer.TYPE) {
-            Integer result;
             try {
-                result = (Integer) vm.invoke(object, EMPTY_PARAMS);
-            } catch (Exception e) {
-                throw new JexlException(node, "size() : error executing", e);
+                return (Integer) vm.invoke(object, EMPTY_PARAMS);
+            } catch (Exception xany) {
+                operatorError(node, Operator.SIZE, xany);
             }
-            return result;
         }
-        throw new JexlException(node, "size() : unsupported type : " + object.getClass(), null);
+        return 0;
     }
 
     @Override

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=1690404&r1=1690403&r2=1690404&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 Sat Jul 11 20:41:09 2015
@@ -747,7 +747,7 @@ void MethodCall() #void : {}
 {
     LOOKAHEAD(<DOT> <SIZE>) (<DOT> <SIZE> <LPAREN> <RPAREN>) #SizeMethod(1)
     |
-    LOOKAHEAD(<DOT> <EMPTY>) ( <DOT> <SIZE> <LPAREN> <RPAREN>) #EmptyMethod(1)
+    LOOKAHEAD(<DOT> <EMPTY>) (<DOT> <EMPTY> <LPAREN> <RPAREN>) #EmptyMethod(1)
     |
     (MemberAccess() (LOOKAHEAD(<LPAREN>) Arguments())+) #MethodNode(>1)
 }

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=1690404&r1=1690403&r2=1690404&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/site/xdoc/changes.xml (original)
+++ commons/proper/jexl/trunk/src/site/xdoc/changes.xml Sat Jul 11 20:41:09 2015
@@ -26,6 +26,9 @@
     </properties>
     <body>
         <release version="3.0" date="unreleased">
+            <action dev="henrib" type="fix" issue="JEXL-162" due-to="Dmitri Blinov">
+                empty() function throws an exception : unsupported type
+            </action>
             <action dev="henrib" type="add" >
                 Added a method to get parameters from a template
             </action>

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=1690404&r1=1690403&r2=1690404&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/site/xdoc/reference/syntax.xml (original)
+++ commons/proper/jexl/trunk/src/site/xdoc/reference/syntax.xml Sat Jul 11 20:41:09 2015
@@ -290,28 +290,39 @@
         <tr>
           <td>empty</td>
           <td>
-            Returns true if the expression following is either:
+            Evaluates whether an expression if 'empty'.
+            This is true when the argument is:
             <ol>
               <li><code>null</code></li>
+              <li>An instance of class C and the derived JexlArithmetic overloads a method 'public boolean empty(C arg)'
+                  that returns true when the argument is considered empty</li>
               <li>An empty string</li>
               <li>An array of length zero</li>
               <li>A collection of size zero</li>
               <li>An empty map</li>
+              <li>Defining a method 'public boolean isEmpty()'
+                  that returns true when the instance is considered empty</li>
             </ol>
-            <source>empty(var1)</source>
+            This is false in other cases (besides errors).
+            <source>empty(arg)</source>
           </td>
         </tr>
         <tr>
           <td>size</td>
           <td>
-            Returns the information about the expression:
+            Evaluates the 'size' of an expression.
+            This returns:
             <ol>
+              <li>0 if the argument is null</li>
+              <li>The result of calling a method from a derived JexlArithmetic overload 'public int size(C arg)',
+                  C being the class of the argument</li>
               <li>Length of an array</li>
-              <li>Size of a List</li>
-              <li>Size of a Map</li>
-              <li>Size of a Set</li>
               <li>Length of a string</li>
+              <li>Size of a Collection</li>
+              <li>Size of a Map</li>
+              <li>The result of calling a method 'public int size()' defined by the argument class</li>
             </ol>
+            This returns 0 in other cases (besides errors).
             <source>size("Hello")</source> returns 5.
           </td>
         </tr>

Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java?rev=1690404&r1=1690403&r2=1690404&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java (original)
+++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java Sat Jul 11 20:41:09 2015
@@ -18,14 +18,19 @@ package org.apache.commons.jexl3;
 
 import org.apache.commons.jexl3.junit.Asserter;
 
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
 import java.util.HashMap;
 import java.util.Map;
-
 import java.math.BigDecimal;
 import java.math.BigInteger;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
 
 @SuppressWarnings({"UnnecessaryBoxing", "AssertEqualsBetweenInconvertibleTypes"})
 public class ArithmeticTest extends JexlTestCase {
@@ -767,4 +772,65 @@ public class ArithmeticTest extends Jexl
         evaluate = jexl.createExpression("math:abs(-42)").evaluate(null);
         Assert.assertEquals(42, evaluate);
     }
+
+    private static Document getDocument(String xml) throws Exception {
+        DocumentBuilder xmlBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+        InputStream stringInputStream = new ByteArrayInputStream(xml.getBytes("UTF-8"));
+        return xmlBuilder.parse(stringInputStream);
+    }
+
+    public static class XmlArithmetic extends JexlArithmetic {
+        public XmlArithmetic(boolean lenient) {
+            super(lenient);
+        }
+        public boolean empty(org.w3c.dom.Element elt) {
+            return !elt.hasAttributes() && !elt.hasChildNodes();
+        }
+        public int size(org.w3c.dom.Element elt) {
+            return elt.getChildNodes().getLength();
+        }
+    }
+
+    @Test
+    public void testXmlArithmetic() throws Exception {
+        JexlEngine jexl = new JexlBuilder().arithmetic(new XmlArithmetic(false)).create();
+        JexlScript e0 = jexl.createScript("x.empty()", "x");
+        JexlScript e1 = jexl.createScript("empty(x)", "x");
+        JexlScript s0 = jexl.createScript("x.size()", "x");
+        JexlScript s1 = jexl.createScript("size(x)", "x");
+        Document xml;
+        Node x;
+        Boolean empty;
+        int size;
+        xml = getDocument("<node info='123'/>");
+        x = xml.getLastChild();
+        empty = (Boolean) e0.execute(null, x);
+        Assert.assertFalse(empty);
+        empty = (Boolean) e1.execute(null, x);
+        Assert.assertFalse(empty);
+        size = (Integer) s0.execute(null, x);
+        Assert.assertEquals(0, size);
+        size = (Integer) s1.execute(null, x);
+        Assert.assertEquals(0, size);
+        xml = getDocument("<node><a/><b/></node>");
+        x = xml.getLastChild();
+        empty = (Boolean) e0.execute(null, x);
+        Assert.assertFalse(empty);
+        empty = (Boolean) e1.execute(null, x);
+        Assert.assertFalse(empty);
+        size = (Integer) s0.execute(null, x);
+        Assert.assertEquals(2, size);
+        size = (Integer) s1.execute(null, x);
+        Assert.assertEquals(2, size);
+        xml = getDocument("<node/>");
+        x = xml.getLastChild();
+        empty = (Boolean) e0.execute(null, x);
+        Assert.assertTrue(empty);
+        empty = (Boolean) e1.execute(null, x);
+        Assert.assertTrue(empty);
+        size = (Integer) s0.execute(null, x);
+        Assert.assertEquals(0, size);
+        size = (Integer) s1.execute(null, x);
+        Assert.assertEquals(0, size);
+    }
 }