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 2009/12/14 18:38:45 UTC

svn commit: r890409 - in /commons/proper/jexl/trunk/src: main/java/org/apache/commons/jexl2/ main/java/org/apache/commons/jexl2/internal/introspection/ test/java/org/apache/commons/jexl2/

Author: henrib
Date: Mon Dec 14 17:38:45 2009
New Revision: 890409

URL: http://svn.apache.org/viewvc?rev=890409&view=rev
Log:
fixed JEXL-94; modified test accordingly. Also fixed a bug in MethodKey#isApplicable that surfaced through test.

Modified:
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/Interpreter.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlEngine.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/internal/introspection/MethodKey.java
    commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/MethodTest.java

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/Interpreter.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/Interpreter.java?rev=890409&r1=890408&r2=890409&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/Interpreter.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/Interpreter.java Mon Dec 14 17:38:45 2009
@@ -97,6 +97,8 @@
     protected final JexlArithmetic arithmetic;
     /** The map of registered functions. */
     protected final Map<String, Object> functions;
+    /** The map of registered functions. */
+    protected Map<String, Object> functors;
     /** The context to store/retrieve variables. */
     protected final JexlContext context;
     /** Strict interpreter flag. */
@@ -124,6 +126,7 @@
         this.silent = jexl.silent;
         this.cache = jexl.cache != null;
         this.context = aContext;
+        this.functors = null;
     }
 
     /**
@@ -231,6 +234,46 @@
         return null;
     }
 
+    /**
+     * Resolves a namespace, eventually allocating an instance using context as constructor argument.
+     * The lifetime of such instances span the current expression or script evaluation.
+     *
+     * @param prefix the prefix name (may be null for global namespace)
+     * @param node the AST node
+     * @return the namespace instance
+     */
+    protected Object resolveNamespace(String prefix, JexlNode node) {
+        Object namespace;
+        // check whether this namespace is a functor
+        if (functors != null) {
+            namespace = functors.get(prefix);
+            if (namespace != null) {
+                return namespace;
+            }
+        }
+        namespace = functions.get(prefix);
+        if (namespace == null) {
+            throw new JexlException(node, "no such function namespace " + prefix);
+        }
+        // allow namespace to be instantiated as functor with context
+        if (namespace instanceof Class<?>) {
+            Object[] args = new Object[]{context};
+            Constructor<?> ctor = uberspect.getConstructor(namespace,args, node);
+            if (ctor != null) {
+                try {
+                    namespace = ctor.newInstance(args);
+                    if (functors == null) {
+                        functors = new HashMap<String, Object>();
+                    }
+                    functors.put(prefix, namespace);
+                } catch (Exception xinst) {
+                    throw new JexlException(node, "unable to instantiate namespace " + prefix, xinst);
+                }
+            }
+        }
+        return namespace;
+    }
+
     /** {@inheritDoc} */
     public Object visit(ASTAdditiveNode node, Object data) {
         /**
@@ -741,7 +784,7 @@
             // if the first child of the (ASTReference) parent,
             // it is considered as calling a 'top level' function
             if (node.jjtGetParent().jjtGetChild(0) == node) {
-                data = functions.get(null);
+                data = resolveNamespace(null, node);
                 if (data == null) {
                     throw new JexlException(node, "no default function namespace");
                 }
@@ -837,10 +880,7 @@
     public Object visit(ASTFunctionNode node, Object data) {
         // objectNode 0 is the prefix
         String prefix = ((ASTIdentifier) node.jjtGetChild(0)).image;
-        Object namespace = functions.get(prefix);
-        if (namespace == null) {
-            throw new JexlException(node, "no such function namespace " + prefix);
-        }
+        Object namespace = resolveNamespace(prefix, node);
         // objectNode 1 is the identifier , the others are parameters.
         String function = ((ASTIdentifier) node.jjtGetChild(1)).image;
 

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlEngine.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlEngine.java?rev=890409&r1=890408&r2=890409&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlEngine.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlEngine.java Mon Dec 14 17:38:45 2009
@@ -313,14 +313,16 @@
      * If all methods are static, you may use the bean class instead of an instance as value.
      * </p>
      * <p>
+     * If the entry value is a class that has one contructor taking a JexlContext as argument, an instance
+     * of the namespace will be created at evaluation time. It might be a good idea to derive a JexlContext
+     * to carry the information used by the namespace to avoid variable space pollution and strongly type
+     * the constructor with this specialized JexlContext.
+     * </p>
+     * <p>
      * The key or prefix allows to retrieve the bean that plays the role of the namespace.
      * If the prefix is null, the namespace is the top-level namespace allowing to define
      * top-level user defined functions ( ie: myfunc(...) )
      * </p>
-     * <p>
-     * Note that you can always use a variable implementing methods & use
-     * the 'var.func(...)' syntax if you need more dynamic constructs.
-     * </p>
      * @param funcs the map of functions that should not mutate after the call; if null
      * is passed, the empty collection is used.
      */

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/internal/introspection/MethodKey.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/internal/introspection/MethodKey.java?rev=890409&r1=890408&r2=890409&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/internal/introspection/MethodKey.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/internal/introspection/MethodKey.java Mon Dec 14 17:38:45 2009
@@ -547,7 +547,8 @@
                 // if there's just one more methodArg than class arg
                 // and the last methodArg is an array, then treat it as a vararg
                 return methodArgs.length == classes.length + 1 && methodArgs[methodArgs.length - 1].isArray();
-            } else if (methodArgs.length == classes.length) {
+            }
+            if (methodArgs.length == classes.length) {
                 // this will properly match when the last methodArg
                 // is an array/varargs and the last class is the type of array
                 // (e.g. String when the method is expecting String...)
@@ -562,8 +563,10 @@
                         return false;
                     }
                 }
-            } else if (methodArgs.length > 0) // more arguments given than the method accepts; check for varargs
-            {
+                return true;
+            }
+            // more arguments given than the method accepts; check for varargs
+            if (methodArgs.length > 0) {
                 // check that the last methodArg is an array
                 Class<?> lastarg = methodArgs[methodArgs.length - 1];
                 if (!lastarg.isArray()) {
@@ -584,9 +587,10 @@
                         return false;
                     }
                 }
+                return true;
             }
-
-            return true;
+            // no match
+            return false;
         }
 
         /**

Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/MethodTest.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/MethodTest.java?rev=890409&r1=890408&r2=890409&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/MethodTest.java (original)
+++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/MethodTest.java Mon Dec 14 17:38:45 2009
@@ -52,6 +52,21 @@
             return x.getClass();
         }
     }
+    
+    public static class EnhancedContext extends MapContext {
+        int factor = 6;
+    }
+
+    public static class ContextualFunctor {
+        private final EnhancedContext context;
+        public ContextualFunctor(EnhancedContext theContext) {
+            context = theContext;
+        }
+        public int ratio(int n) {
+            context.factor -= 1;
+            return n / context.factor;
+        }
+    }
 
     @Override
     public void setUp() {
@@ -130,9 +145,10 @@
         java.util.Map<String, Object> funcs = new java.util.HashMap<String, Object>();
         funcs.put(null, new Functor());
         funcs.put("math", new MyMath());
+        funcs.put("cx", ContextualFunctor.class);
         JEXL.setFunctions(funcs);
 
-        JexlContext jc = new MapContext();
+        JexlContext jc = new EnhancedContext();
 
         Expression e = JEXL.createExpression("ten()");
         Object o = e.evaluate(jc);
@@ -150,6 +166,10 @@
         e = JEXL.createExpression("math:cos(pi)");
         o = e.evaluate(jc);
         assertEquals(Double.valueOf(-1),o);
+      
+        e = JEXL.createExpression("cx:ratio(10) + cx:ratio(20)");
+        o = e.evaluate(jc);
+        assertEquals(Integer.valueOf(7),o);
     }
 
     public void testNamespaceCall() throws Exception {
@@ -183,5 +203,12 @@
         o = e.evaluate(jc);
         assertEquals("Result is not 40", new Integer(40), o);
     }
-
+    /**
+     * Runs a test.
+     * @param args where args[0] is the test class name and args[1] the test class method
+     * @throws Exception
+     */
+    public static void main(String[] args) throws Exception {
+        runTest("MethodTest", "testNamespaceCall");
+    }
 }
\ No newline at end of file



Re: svn commit: r890409 - in /commons/proper/jexl/trunk/src: main/java/org/apache/commons/jexl2/ main/java/org/apache/commons/jexl2/internal/introspection/ test/java/org/apache/commons/jexl2/

Posted by henrib <he...@apache.org>.

Removed it, was leftover from debug in NB.
Thanks for pointing it out.


sebb-2-2 wrote:
> 
> 
> However, I'm not sure it's useful to have main() methods in test cases
> as Maven and Eclipse allow individual cases to be run directly anyway.
> ...
>>  +    public static void main(String[] args) throws Exception {
>>  +        runTest("MethodTest", "testNamespaceCall");
>>  +    }
>>   }
> ...
> 

-- 
View this message in context: http://n4.nabble.com/Re-svn-commit-r890409-in-commons-proper-jexl-trunk-src-main-java-org-apache-commons-jexl2-main-java--tp963766p963800.html
Sent from the Commons - Dev mailing list archive at Nabble.com.

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: svn commit: r890409 - in /commons/proper/jexl/trunk/src: main/java/org/apache/commons/jexl2/ main/java/org/apache/commons/jexl2/internal/introspection/ test/java/org/apache/commons/jexl2/

Posted by henrib <he...@apache.org>.
All members in JexlEngine are protected so JexlEngine can be derived easily.

As for the functors map, there is no reason to allocate that map before hand
if the expression does not use any; there will likely be far more exprs
evaluated without namespaces than the opposite. There are only 2 occurrences
of null checking for functors and these are located in the same function.

I dont feel like I'm bending proper coding rules here.

Btw, any news on the bsf3 artefact in the repository ?


sebb-2-2 wrote:
> 
> ...
>>      protected final JexlArithmetic arithmetic;
>>      /** The map of registered functions. */
>>      protected final Map<String, Object> functions;
>>  +    /** The map of registered functions. */
>>  +    protected Map<String, Object> functors;
> 
> Could/should be private.
> 
> Indeed so should all the other instance variables...
> ...
>>  +        this.functors = null;
> ....
> Why not allocate an empty HashMap here?
> 
> It would save a lot of null checking later, and the variable could
> then be final.
> 

-- 
View this message in context: http://n4.nabble.com/Re-svn-commit-r890409-in-commons-proper-jexl-trunk-src-main-java-org-apache-commons-jexl2-main-java--tp963766p963796.html
Sent from the Commons - Dev mailing list archive at Nabble.com.

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: svn commit: r890409 - in /commons/proper/jexl/trunk/src: main/java/org/apache/commons/jexl2/ main/java/org/apache/commons/jexl2/internal/introspection/ test/java/org/apache/commons/jexl2/

Posted by sebb <se...@gmail.com>.
On 14/12/2009, henrib@apache.org <he...@apache.org> wrote:
> Author: henrib
>  Date: Mon Dec 14 17:38:45 2009
>  New Revision: 890409
>
>  URL: http://svn.apache.org/viewvc?rev=890409&view=rev
>  Log:
>  fixed JEXL-94; modified test accordingly. Also fixed a bug in MethodKey#isApplicable that surfaced through test.
>
<...>
Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/MethodTest.java
>  URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/MethodTest.java?rev=890409&r1=890408&r2=890409&view=diff
>  ==============================================================================
>  --- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/MethodTest.java (original)
>  +++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2

<...>

>  +    /**
>  +     * Runs a test.
>  +     * @param args where args[0] is the test class name and args[1] the test class method

Javadoc disagrees with code.

However, I'm not sure it's useful to have main() methods in test cases
as Maven and Eclipse allow individual cases to be run directly anyway.


>  +     * @throws Exception
>  +     */
>  +    public static void main(String[] args) throws Exception {
>  +        runTest("MethodTest", "testNamespaceCall");
>  +    }
>   }
>  \ No newline at end of file
>
>
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: svn commit: r890409 - in /commons/proper/jexl/trunk/src: main/java/org/apache/commons/jexl2/ main/java/org/apache/commons/jexl2/internal/introspection/ test/java/org/apache/commons/jexl2/

Posted by sebb <se...@gmail.com>.
On 14/12/2009, henrib@apache.org <he...@apache.org> wrote:
> Author: henrib
>  Date: Mon Dec 14 17:38:45 2009
>  New Revision: 890409
>
>  URL: http://svn.apache.org/viewvc?rev=890409&view=rev
>  Log:
>  fixed JEXL-94; modified test accordingly. Also fixed a bug in MethodKey#isApplicable that surfaced through test.
>
>  Modified:
>     commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/Interpreter.java
>     commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlEngine.java
>     commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/internal/introspection/MethodKey.java
>     commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/MethodTest.java
>
>  Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/Interpreter.java
>  URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/Interpreter.java?rev=890409&r1=890408&r2=890409&view=diff
>  ==============================================================================
>  --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/Interpreter.java (original)
>  +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/Interpreter.java Mon Dec 14 17:38:45 2009
>  @@ -97,6 +97,8 @@
>      protected final JexlArithmetic arithmetic;
>      /** The map of registered functions. */
>      protected final Map<String, Object> functions;
>  +    /** The map of registered functions. */
>  +    protected Map<String, Object> functors;

Could/should be private.

Indeed so should all the other instance variables...

>      /** The context to store/retrieve variables. */
>      protected final JexlContext context;
>      /** Strict interpreter flag. */
>  @@ -124,6 +126,7 @@
>          this.silent = jexl.silent;
>          this.cache = jexl.cache != null;
>          this.context = aContext;
>  +        this.functors = null;

Why not allocate an empty HashMap here?

It would save a lot of null checking later, and the variable could
then be final.

>      }
>
>      /**
>  @@ -231,6 +234,46 @@
>          return null;
>      }
>
>  +    /**
>  +     * Resolves a namespace, eventually allocating an instance using context as constructor argument.
>  +     * The lifetime of such instances span the current expression or script evaluation.
>  +     *
>  +     * @param prefix the prefix name (may be null for global namespace)
>  +     * @param node the AST node
>  +     * @return the namespace instance
>  +     */
>  +    protected Object resolveNamespace(String prefix, JexlNode node) {
>  +        Object namespace;
>  +        // check whether this namespace is a functor
>  +        if (functors != null) {
>  +            namespace = functors.get(prefix);
>  +            if (namespace != null) {
>  +                return namespace;
>  +            }
>  +        }
>  +        namespace = functions.get(prefix);
>  +        if (namespace == null) {
>  +            throw new JexlException(node, "no such function namespace " + prefix);
>  +        }
>  +        // allow namespace to be instantiated as functor with context
>  +        if (namespace instanceof Class<?>) {
>  +            Object[] args = new Object[]{context};
>  +            Constructor<?> ctor = uberspect.getConstructor(namespace,args, node);
>  +            if (ctor != null) {
>  +                try {
>  +                    namespace = ctor.newInstance(args);
>  +                    if (functors == null) {
>  +                        functors = new HashMap<String, Object>();
>  +                    }
>  +                    functors.put(prefix, namespace);
>  +                } catch (Exception xinst) {
>  +                    throw new JexlException(node, "unable to instantiate namespace " + prefix, xinst);
>  +                }
>  +            }
>  +        }
>  +        return namespace;
>  +    }
>  +

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org