Author: danielf
Date: Mon Jan  3 13:38:37 2005
New Revision: 124027

URL: http://svn.apache.org/viewcvs?view=rev&rev=124027
Log:
Iterator support for Jexl
Added:
   
cocoon/trunk/src/blocks/template/java/org/apache/cocoon/components/expression/jexl/JSIntrospector.java
Modified:
   
cocoon/trunk/src/blocks/template/java/org/apache/cocoon/components/expression/jexl/JexlExpression.java
   
cocoon/trunk/src/blocks/template/test/org/apache/cocoon/components/expression/jexl/JexlTestCase.java

Added: 
cocoon/trunk/src/blocks/template/java/org/apache/cocoon/components/expression/jexl/JSIntrospector.java
Url: 
http://svn.apache.org/viewcvs/cocoon/trunk/src/blocks/template/java/org/apache/cocoon/components/expression/jexl/JSIntrospector.java?view=auto&rev=124027
==============================================================================
--- (empty file)
+++ 
cocoon/trunk/src/blocks/template/java/org/apache/cocoon/components/expression/jexl/JSIntrospector.java
      Mon Jan  3 13:38:37 2005
@@ -0,0 +1,325 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ * 
+ * Licensed 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.cocoon.components.expression.jexl;
+
+import java.util.Enumeration;
+import java.util.Iterator;
+
+import org.apache.commons.jexl.util.introspection.Info;
+import org.apache.commons.jexl.util.introspection.UberspectImpl;
+import org.apache.commons.jexl.util.introspection.VelMethod;
+import org.apache.commons.jexl.util.introspection.VelPropertyGet;
+import org.apache.commons.jexl.util.introspection.VelPropertySet;
+import org.apache.commons.lang.StringUtils;
+import org.mozilla.javascript.*;
+
+
+/**
+ * Jexl Introspector that supports Rhino JavaScript objects
+ * as well as Java Objects
+ */
+public class JSIntrospector extends UberspectImpl {
+
+    static class JSMethod implements VelMethod {
+
+        Scriptable scope;
+        String name;
+
+        public JSMethod(Scriptable scope, String name) {
+            this.scope = scope;
+            this.name = name;
+        }
+
+        public Object invoke(Object thisArg, Object[] args) throws Exception {
+            Context cx = Context.enter();
+            try {
+                Object result;
+                Scriptable thisObj = !(thisArg instanceof Scriptable) ?
+                        Context.toObject(thisArg, scope) : (Scriptable)thisArg;
+                result = ScriptableObject.getProperty(thisObj, name);
+                Object[] newArgs = null;
+                if (args != null) {
+                    newArgs = new Object[args.length];
+                    int len = args.length;
+                    for (int i = 0; i < len; i++) {
+                        newArgs[i] = args[i];
+                        if (args[i] != null &&
+                            !(args[i] instanceof Number) &&
+                            !(args[i] instanceof Boolean) &&
+                            !(args[i] instanceof String) &&
+                            !(args[i] instanceof Scriptable)) {
+                            newArgs[i] = Context.toObject(args[i], scope);
+                        }
+                    }
+                }
+                result = ScriptRuntime.call(cx, result, thisObj, newArgs, 
scope);
+                if (result == Undefined.instance || result == 
Scriptable.NOT_FOUND) {
+                    result = null;
+                } else if (!(result instanceof NativeJavaClass)) {
+                    while (result instanceof Wrapper) {
+                        result = ((Wrapper)result).unwrap();
+                    }
+                }
+                return result;
+            } catch (JavaScriptException e) {
+                throw new java.lang.reflect.InvocationTargetException(e);
+            } finally {
+                Context.exit();
+            }
+        }
+
+        public boolean isCacheable() {
+            return false;
+        }
+
+        public String getMethodName() {
+            return name;
+        }
+
+        public Class getReturnType() {
+            return Object.class;
+        }
+
+    }
+
+    static class JSPropertyGet implements VelPropertyGet {
+
+        Scriptable scope;
+        String name;
+
+        public JSPropertyGet(Scriptable scope, String name) {
+            this.scope = scope;
+            this.name = name;
+        }
+
+        public Object invoke(Object thisArg) throws Exception {
+            Context cx = Context.enter();
+            try {
+                Scriptable thisObj = !(thisArg instanceof Scriptable) ?
+                        Context.toObject(thisArg, scope) : (Scriptable)thisArg;
+                Object result = ScriptableObject.getProperty(thisObj, name);
+                if (result == Scriptable.NOT_FOUND) {
+                    result = ScriptableObject.getProperty(thisObj, "get" + 
StringUtils.capitalize(name));
+                    if (result != Scriptable.NOT_FOUND && result instanceof 
Function) {
+                        try {
+                            result = ((Function)result).call(
+                                    cx, 
ScriptableObject.getTopLevelScope(thisObj), thisObj, new Object[] {});
+                        } catch (JavaScriptException exc) {
+                            exc.printStackTrace();
+                            result = null;
+                        }
+                    }
+                }
+                if (result == Scriptable.NOT_FOUND || result == 
Undefined.instance) {
+                    result = null;
+                } else if (result instanceof Wrapper && !(result instanceof 
NativeJavaClass)) {
+                    result = ((Wrapper)result).unwrap();
+                }
+                return result;
+            } finally {
+                Context.exit();
+            }
+        }
+
+        public boolean isCacheable() {
+            return false;
+        }
+
+        public String getMethodName() {
+            return name;
+        }
+    }
+
+    static class JSPropertySet implements VelPropertySet {
+
+        Scriptable scope;
+        String name;
+
+        public JSPropertySet(Scriptable scope, String name) {
+            this.scope = scope;
+            this.name = name;
+        }
+
+        public Object invoke(Object thisArg, Object rhs) throws Exception {
+            Context.enter();
+            try {
+                Scriptable thisObj;
+                Object arg = rhs;
+                if (!(thisArg instanceof Scriptable)) {
+                    thisObj = Context.toObject(thisArg, scope);
+                } else {
+                    thisObj = (Scriptable)thisArg;
+                }
+                if (arg != null &&
+                    !(arg instanceof Number) &&
+                    !(arg instanceof Boolean) &&
+                    !(arg instanceof String) &&
+                    !(arg instanceof Scriptable)) {
+                    arg = Context.toObject(arg, scope);
+                }
+                ScriptableObject.putProperty(thisObj, name, arg);
+                return rhs;
+            } finally {
+                Context.exit();
+            }
+        }
+
+        public boolean isCacheable() {
+            return false;
+        }
+
+        public String getMethodName() {
+            return name;
+        }
+    }
+
+    public static class NativeArrayIterator implements Iterator {
+
+        NativeArray arr;
+        int index;
+
+        public NativeArrayIterator(NativeArray arr) {
+            this.arr = arr;
+            this.index = 0;
+        }
+
+        public boolean hasNext() {
+            return index < (int)arr.jsGet_length();
+        }
+
+        public Object next() {
+            Context.enter();
+            try {
+                Object result = arr.get(index++, arr);
+                if (result == Undefined.instance ||
+                    result == Scriptable.NOT_FOUND) {
+                    result = null;
+                } else {
+                    if (!(result instanceof NativeJavaClass)) {
+                        while (result instanceof Wrapper) {
+                            result = ((Wrapper)result).unwrap();
+                        }
+                    }
+                }
+                return result;
+            } finally {
+                Context.exit();
+            }
+        }
+
+        public void remove() {
+            arr.delete(index);
+        }
+    }
+
+    static class ScriptableIterator implements Iterator {
+
+        Scriptable scope;
+        Object[] ids;
+        int index;
+
+        public ScriptableIterator(Scriptable scope) {
+            this.scope = scope;
+            this.ids = scope.getIds();
+            this.index = 0;
+        }
+
+        public boolean hasNext() {
+            return index < ids.length;
+        }
+
+        public Object next() {
+            Context.enter();
+            try {
+                Object result = ScriptableObject.getProperty(scope, 
ids[index++].toString());
+                if (result == Undefined.instance || result == 
Scriptable.NOT_FOUND) {
+                    result = null;
+                } else if (!(result instanceof NativeJavaClass)) {
+                    while (result instanceof Wrapper) {
+                        result = ((Wrapper)result).unwrap();
+                    }
+                }
+                return result;
+            } finally {
+                Context.exit();
+            }
+        }
+
+        public void remove() {
+            Context.enter();
+            try {
+                scope.delete(ids[index].toString());
+            } finally {
+                Context.exit();
+            }
+        }
+    }
+
+    public Iterator getIterator(Object obj, Info i) throws Exception {
+        if (!(obj instanceof Scriptable)) {
+            // support Enumeration
+            /*
+               Booth Enumeration and Iterator are supported in
+               Uberspect. The only difference is that they emit a
+               rather long warning message to commons logging, telling
+               that Enumerations and Iterator not are resettable and
+               cannot be reused.
+            */
+            if (obj instanceof Enumeration) {
+                final Enumeration e = (Enumeration)obj;
+                return new Iterator() {
+
+                        public boolean hasNext() {
+                            return e.hasMoreElements();
+                        }
+
+                        public Object next() {
+                            return e.nextElement();
+                        }
+
+                        public void remove() {
+                            // no action
+                        }
+
+                    };
+            }
+            if (obj instanceof Iterator) {
+                // support Iterator
+                return (Iterator)obj;
+            }
+            return super.getIterator(obj, i);
+        }
+        if (obj instanceof NativeArray) {
+            return new NativeArrayIterator((NativeArray)obj);
+        }
+        return new ScriptableIterator((Scriptable)obj);
+    }
+
+    public VelMethod getMethod(Object obj, String methodName, Object[] args, 
Info i) throws Exception {
+        return !(obj instanceof Scriptable) ?
+                super.getMethod(obj, methodName, args, i) : new 
JSMethod((Scriptable)obj, methodName);
+    }
+
+    public VelPropertyGet getPropertyGet(Object obj, String identifier, Info 
i) throws Exception {
+        return !(obj instanceof Scriptable) ?
+                super.getPropertyGet(obj, identifier, i) : new 
JSPropertyGet((Scriptable)obj, identifier);
+    }
+
+    public VelPropertySet getPropertySet(Object obj, String identifier, Object 
arg, Info i) throws Exception {
+        return !(obj instanceof Scriptable) ?
+                super.getPropertySet(obj, identifier, arg, i) : new 
JSPropertySet((Scriptable)obj, identifier);
+    }
+}

Modified: 
cocoon/trunk/src/blocks/template/java/org/apache/cocoon/components/expression/jexl/JexlExpression.java
Url: 
http://svn.apache.org/viewcvs/cocoon/trunk/src/blocks/template/java/org/apache/cocoon/components/expression/jexl/JexlExpression.java?view=diff&rev=124027&p1=cocoon/trunk/src/blocks/template/java/org/apache/cocoon/components/expression/jexl/JexlExpression.java&r1=124026&p2=cocoon/trunk/src/blocks/template/java/org/apache/cocoon/components/expression/jexl/JexlExpression.java&r2=124027
==============================================================================
--- 
cocoon/trunk/src/blocks/template/java/org/apache/cocoon/components/expression/jexl/JexlExpression.java
      (original)
+++ 
cocoon/trunk/src/blocks/template/java/org/apache/cocoon/components/expression/jexl/JexlExpression.java
      Mon Jan  3 13:38:37 2005
@@ -15,10 +15,13 @@
  */
 package org.apache.cocoon.components.expression.jexl;
 
+import java.lang.reflect.Field;
 import java.util.Iterator;
 import java.util.Map;
 
 import org.apache.commons.jexl.JexlContext;
+import org.apache.commons.jexl.util.Introspector;
+import org.apache.commons.jexl.util.introspection.Info;
 import org.apache.cocoon.components.expression.Expression;
 import org.apache.cocoon.components.expression.ExpressionCompiler;
 import org.apache.cocoon.components.expression.ExpressionContext;
@@ -52,11 +55,32 @@
 
     public Iterator iterate(ExpressionContext context)
         throws ExpressionException {
-        return null;
+        Iterator iter = null;
+        Object result = evaluate(context);
+        if (result != null) {
+            /* The Info object is supposed to contain the script
+               location where the expression is invoked and use that
+               in a warning log message if no iterator can be
+               generated. This info is not available in the expression
+               object and might not be relevant either as it can be
+               used from a non script situation.
+            */
+            try {
+                iter = Introspector.getUberspect().getIterator(result, new 
Info("Unknown", 0, 0));
+            } catch (Exception e) {
+                throw new ExpressionException("Couldn't get an iterator from 
expression " +
+                                              getExpression(), e);
+            }
+        }
+        if (iter == null) {
+            iter = EMPTY_ITER;
+        }
+        return iter;
     }
 
     public void assign(ExpressionContext context, Object value)
         throws ExpressionException {
+        throw new UnsupportedOperationException("Assign is not yet implemented 
for Jexl");
     }
 
     public String getExpression() {
@@ -67,7 +91,7 @@
         return this.language;
     }
 
-    static class ContextAdapter implements JexlContext {
+    private static class ContextAdapter implements JexlContext {
         private final ExpressionContext context;
         public ContextAdapter(ExpressionContext context) {
             this.context = context;
@@ -79,6 +103,31 @@
 
         public void setVars(Map map) {
             this.context.setVars(map);
+        }
+    }
+
+    private static final Iterator EMPTY_ITER = new Iterator() {
+        public boolean hasNext() {
+            return false;
+        }
+
+        public Object next() {
+            return null;
+        }
+
+        public void remove() {
+            // EMPTY
+        }
+    };
+
+    static {
+        // Hack: there's no _nice_ way to add my introspector to Jexl right now
+        try {
+            Field field = Introspector.class.getDeclaredField("uberSpect");
+            field.setAccessible(true);
+            field.set(null, new JSIntrospector());
+        } catch (Exception e) {
+            e.printStackTrace();
         }
     }
 }

Modified: 
cocoon/trunk/src/blocks/template/test/org/apache/cocoon/components/expression/jexl/JexlTestCase.java
Url: 
http://svn.apache.org/viewcvs/cocoon/trunk/src/blocks/template/test/org/apache/cocoon/components/expression/jexl/JexlTestCase.java?view=diff&rev=124027&p1=cocoon/trunk/src/blocks/template/test/org/apache/cocoon/components/expression/jexl/JexlTestCase.java&r1=124026&p2=cocoon/trunk/src/blocks/template/test/org/apache/cocoon/components/expression/jexl/JexlTestCase.java&r2=124027
==============================================================================
--- 
cocoon/trunk/src/blocks/template/test/org/apache/cocoon/components/expression/jexl/JexlTestCase.java
        (original)
+++ 
cocoon/trunk/src/blocks/template/test/org/apache/cocoon/components/expression/jexl/JexlTestCase.java
        Mon Jan  3 13:38:37 2005
@@ -15,6 +15,8 @@
  */
 package org.apache.cocoon.components.expression.jexl;
 
+import java.util.Iterator;
+
 import junit.framework.TestCase;
 import org.apache.cocoon.components.expression.Expression;
 import org.apache.cocoon.components.expression.ExpressionCompiler;
@@ -36,5 +38,17 @@
         context.put("b", new Long(2));
         Expression expression = compiler.compile("jexl", "a+b");
         assertEquals(new Long(3), expression.evaluate(context));
+    }
+
+    public void testIterator() throws ExpressionException {
+        ExpressionCompiler compiler = new JexlCompiler();
+        ExpressionContext context = new ExpressionContext();
+        String[] arr = {"foo"};
+        context.put("arr", arr);
+        Expression expression = compiler.compile("jexl", "arr");
+        Iterator iter = expression.iterate(context);
+        assertTrue("hasNext", iter.hasNext());
+        assertEquals("foo", iter.next());
+        assertFalse("hasNext", iter.hasNext());
     }
 }

Reply via email to