commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From hen...@apache.org
Subject 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/
Date Mon, 14 Dec 2009 17:38:45 GMT
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



Mime
View raw message