You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@struts.apache.org by mr...@apache.org on 2005/02/21 03:13:40 UTC

svn commit: r154604 - in struts/flow/trunk/src/java/org/apache/struts/flow: core/ sugar/

Author: mrdon
Date: Sun Feb 20 18:13:38 2005
New Revision: 154604

URL: http://svn.apache.org/viewcvs?view=rev&rev=154604
Log:
 * Adding new sugar package to hold all JVM function extensions
 * Adding List and File function extensions
 * Adding a system to support runtime registration of function 
   extensions


Added:
    struts/flow/trunk/src/java/org/apache/struts/flow/sugar/
    struts/flow/trunk/src/java/org/apache/struts/flow/sugar/ExtensionFunction.java   (with props)
    struts/flow/trunk/src/java/org/apache/struts/flow/sugar/FileExtensions.java   (with props)
    struts/flow/trunk/src/java/org/apache/struts/flow/sugar/JavaObjectWrapper.java   (with props)
    struts/flow/trunk/src/java/org/apache/struts/flow/sugar/ListExtensions.java   (with props)
    struts/flow/trunk/src/java/org/apache/struts/flow/sugar/ScriptableList.java
      - copied, changed from r154003, struts/flow/trunk/src/java/org/apache/struts/flow/core/ScriptableList.java
    struts/flow/trunk/src/java/org/apache/struts/flow/sugar/ScriptableMap.java
      - copied, changed from r154003, struts/flow/trunk/src/java/org/apache/struts/flow/core/ScriptableMap.java
    struts/flow/trunk/src/java/org/apache/struts/flow/sugar/SugarWrapFactory.java
      - copied, changed from r154003, struts/flow/trunk/src/java/org/apache/struts/flow/core/SugarWrapFactory.java
Removed:
    struts/flow/trunk/src/java/org/apache/struts/flow/core/ScriptableList.java
    struts/flow/trunk/src/java/org/apache/struts/flow/core/ScriptableMap.java
    struts/flow/trunk/src/java/org/apache/struts/flow/core/SugarWrapFactory.java

Added: struts/flow/trunk/src/java/org/apache/struts/flow/sugar/ExtensionFunction.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/sugar/ExtensionFunction.java?view=auto&rev=154604
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/sugar/ExtensionFunction.java (added)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/sugar/ExtensionFunction.java Sun Feb 20 18:13:38 2005
@@ -0,0 +1,57 @@
+/*
+ *  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.struts.flow.sugar;
+
+import org.mozilla.javascript.*;
+import java.util.*;
+import java.io.Serializable;
+
+/**
+ *  Base class for function extensions.  A function extension is a function
+ *  is added to an existing Java object at the Rhino level.
+ */
+public abstract class ExtensionFunction extends ScriptableObject implements Function {
+        
+    protected Object target;    
+    protected Scriptable wrapper;
+        
+    public Object call(Context cx, Scriptable scope, Scriptable thisObj, java.lang.Object[] args) {
+        try {
+            return execute(cx, scope, thisObj, args);
+        } catch (Exception ex) {
+            throw Context.throwAsScriptRuntimeEx(ex);
+        }
+    }
+    
+    public abstract Object execute(Context cx, Scriptable scope, Scriptable thisObj, java.lang.Object[] args) throws Exception;
+     
+    public Scriptable construct(Context cx, Scriptable scope, java.lang.Object[] args) {
+        return null;
+    }
+    
+    public String getClassName() {
+        return getClass().getName();
+    }
+    
+    public void setTarget(Object target) {
+        this.target = target;   
+    }
+    
+    public void setWrapper(Scriptable wrapper) {
+        this.wrapper = wrapper;   
+    }
+}

Propchange: struts/flow/trunk/src/java/org/apache/struts/flow/sugar/ExtensionFunction.java
------------------------------------------------------------------------------
    svn:executable = *

Added: struts/flow/trunk/src/java/org/apache/struts/flow/sugar/FileExtensions.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/sugar/FileExtensions.java?view=auto&rev=154604
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/sugar/FileExtensions.java (added)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/sugar/FileExtensions.java Sun Feb 20 18:13:38 2005
@@ -0,0 +1,80 @@
+/*
+ * 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.struts.flow.sugar;
+
+import org.mozilla.javascript.*;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * Adds various functions to java.io.File
+ */
+public class FileExtensions {
+
+    public static ExtensionFunction append(final File file) {
+        return new ExtensionFunction() {    
+            public Object execute(Context cx, Scriptable scope, Scriptable thisObj, java.lang.Object[] args) 
+                    throws IOException {
+                String text = args[0].toString();
+                BufferedWriter writer = new BufferedWriter(new FileWriter(file, true));
+                writer.write(text);
+                writer.close();
+                return file;
+            }
+        };
+    }
+    
+    public static ExtensionFunction getText(final File file) {
+        return new ExtensionFunction() {    
+            public Object execute(Context cx, Scriptable scope, Scriptable thisObj, java.lang.Object[] args) 
+                    throws IOException {
+                
+                BufferedReader reader = new BufferedReader(new FileReader(file));
+                StringBuffer answer = new StringBuffer();
+                // reading the content of the file within a char buffer allow to keep the correct line endings
+                char[] charBuffer = new char[4096];
+                int nbCharRead = 0;
+                while ((nbCharRead = reader.read(charBuffer)) != -1) {
+                    // appends buffer
+                    answer.append(charBuffer, 0, nbCharRead);
+                }
+                reader.close();
+                return answer.toString();
+            }
+        };
+    }
+    
+    public static ExtensionFunction eachLine(final File file) {
+        return new ExtensionFunction() {    
+            public Object execute(Context cx, Scriptable scope, Scriptable thisObj, java.lang.Object[] args) 
+                    throws IOException {
+                
+                BufferedReader reader = new BufferedReader(new FileReader(file));
+                Function func = (Function)args[0];
+                Object[] params = new Object[1];
+                String line = null;
+                while ((line = reader.readLine()) != null) {
+                    params[0] = line;
+                    func.call(cx, scope, thisObj, params);
+                }
+                reader.close();
+                return null;
+            }
+        };
+    }
+    
+}

Propchange: struts/flow/trunk/src/java/org/apache/struts/flow/sugar/FileExtensions.java
------------------------------------------------------------------------------
    svn:executable = *

Added: struts/flow/trunk/src/java/org/apache/struts/flow/sugar/JavaObjectWrapper.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/sugar/JavaObjectWrapper.java?view=auto&rev=154604
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/sugar/JavaObjectWrapper.java (added)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/sugar/JavaObjectWrapper.java Sun Feb 20 18:13:38 2005
@@ -0,0 +1,56 @@
+/*
+ * 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.struts.flow.sugar;
+
+import org.mozilla.javascript.*;
+
+import java.util.*;
+import java.io.Serializable;
+import java.lang.reflect.*;
+
+/**
+ * Adds support for extended functions to wrapped Java objects.
+ */
+public class JavaObjectWrapper extends NativeJavaObject implements Scriptable, Wrapper, Serializable {
+
+    private Map functions;
+
+    public JavaObjectWrapper() {
+        super();
+    }
+
+    public JavaObjectWrapper(Scriptable scope, Object javaObject, Class staticType, Map functions) {
+        super(scope, javaObject, staticType);
+        this.functions = functions;
+    }
+
+    public Object get(String name, Scriptable start) {
+        //System.out.println("looking up function "+name);
+        Method func = (Method)functions.get(name);
+        if (func != null) {
+            try {
+                ExtensionFunction f = (ExtensionFunction)func.invoke(null, new Object[]{javaObject});
+                f.setTarget(javaObject);
+                f.setWrapper(this);
+                return f;
+            } catch (Exception ex) {
+                throw new RuntimeException("Unable to create function "+name+" on "+javaObject);
+            }
+        } else {
+            return super.get(name, start);
+        } 
+    }
+}

Propchange: struts/flow/trunk/src/java/org/apache/struts/flow/sugar/JavaObjectWrapper.java
------------------------------------------------------------------------------
    svn:executable = *

Added: struts/flow/trunk/src/java/org/apache/struts/flow/sugar/ListExtensions.java
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/sugar/ListExtensions.java?view=auto&rev=154604
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/sugar/ListExtensions.java (added)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/sugar/ListExtensions.java Sun Feb 20 18:13:38 2005
@@ -0,0 +1,106 @@
+/*
+ * 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.struts.flow.sugar;
+
+import org.mozilla.javascript.*;
+
+import java.util.*;
+import java.io.Serializable;
+
+/**
+ * Adds various function extensions to java.util.List implementations.
+ */
+public class ListExtensions {
+
+    public static ExtensionFunction each(final List list) {
+        return new ExtensionFunction() {    
+            public Object execute(Context cx, Scriptable scope, Scriptable thisObj, java.lang.Object[] args) {
+                Function func = (Function)args[0];
+                Object[] param = new Object[1];
+                
+                for (Iterator i = list.iterator(); i.hasNext(); ) {
+                    param[0] = i.next();
+                    func.call(cx, scope, thisObj, param);
+                }
+                return null;
+            }
+        };
+    }
+    
+    public static ExtensionFunction asImmutable(final List list) {
+        return new ExtensionFunction() {    
+            public Object execute(Context cx, Scriptable scope, Scriptable thisObj, java.lang.Object[] args) {
+                return Collections.unmodifiableList(list);
+            }
+        };
+    }
+    
+    public static ExtensionFunction asSynchronized(final List list) {
+        return new ExtensionFunction() {    
+            public Object execute(Context cx, Scriptable scope, Scriptable thisObj, java.lang.Object[] args) {
+                return Collections.synchronizedList(list);
+            }
+        };
+    }
+    
+    public static ExtensionFunction pop(final List list) {
+        return new ExtensionFunction() {    
+            public Object execute(Context cx, Scriptable scope, Scriptable thisObj, java.lang.Object[] args) {
+                Object o = null;
+                if (list.size() > 0) {
+                    o = list.get(list.size() - 1);
+                    list.remove(list.size());
+                }
+                return o;
+            }
+        };
+    }
+    
+    public static ExtensionFunction sort(final List list) {
+        return new ExtensionFunction() {    
+            public Object execute(Context cx, Scriptable scope, Scriptable thisObj, java.lang.Object[] args) {
+                Collections.sort(list);
+                return null;
+            }
+        };
+    }
+    
+    public static ExtensionFunction sortEach(final List list) {
+        return new ExtensionFunction() {    
+            public Object execute(final Context cx, final Scriptable scope, final Scriptable thisObj, java.lang.Object[] args) {
+                final Object[] params = new Object[2];
+                final Function func = (Function)args[0];
+                Comparator comp = new Comparator() {
+                    public int compare(Object o1, Object o2) {
+                        params[0] = o1;
+                        params[1] = o2;
+                        Object result = func.call(cx, scope, thisObj,params);
+                        if (result instanceof Number) {
+                            return ((Number)result).intValue();
+                        } else {
+                            throw new RuntimeException("Invalid sorting function - should return a number.  Returned "+result);
+                        }
+                    }
+                    
+                    public boolean equals(Object o) {return false;}
+                };
+                
+                Collections.sort(list, comp);
+                return list;
+            }
+        };
+    }
+}

Propchange: struts/flow/trunk/src/java/org/apache/struts/flow/sugar/ListExtensions.java
------------------------------------------------------------------------------
    svn:executable = *

Copied: struts/flow/trunk/src/java/org/apache/struts/flow/sugar/ScriptableList.java (from r154003, struts/flow/trunk/src/java/org/apache/struts/flow/core/ScriptableList.java)
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/sugar/ScriptableList.java?view=diff&rev=154604&p1=struts/flow/trunk/src/java/org/apache/struts/flow/core/ScriptableList.java&r1=154003&p2=struts/flow/trunk/src/java/org/apache/struts/flow/sugar/ScriptableList.java&r2=154604
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/ScriptableList.java (original)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/sugar/ScriptableList.java Sun Feb 20 18:13:38 2005
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.struts.flow.core;
+package org.apache.struts.flow.sugar;
 
 import org.mozilla.javascript.*;
 
@@ -23,7 +23,7 @@
 /**
  * Wrap a java.util.List for JavaScript.
  */
-public class ScriptableList extends NativeJavaObject implements Scriptable, Wrapper, Serializable {
+public class ScriptableList extends JavaObjectWrapper implements Scriptable, Wrapper, Serializable {
 
     private List list;
 
@@ -36,8 +36,8 @@
         this.javaObject = javaObject;
     }
     
-    public ScriptableList(Scriptable scope, Object javaObject, Class staticType) {
-        super(scope, javaObject, staticType);
+    public ScriptableList(Scriptable scope, Object javaObject, Class staticType, Map funcs) {
+        super(scope, javaObject, staticType, funcs);
         if (javaObject instanceof List) {
             this.list = (List)javaObject;
         } else {
@@ -56,14 +56,6 @@
     public Object get(int index, Scriptable start) {
         return list.get(index);
     }
-    
-    public Object get(String name, Scriptable start) {
-        if ("each".equals(name)) {
-            return new EachClosure();
-        } else {
-            return super.get(name, start);
-        } 
-    }
 
     public void put(int index, Scriptable start, Object value) {
         list.add(index, value);
@@ -85,28 +77,6 @@
 
     public Object unwrap() {
         return this.list;
-    }
-    
-    class EachClosure extends ScriptableObject implements Function {
-        
-        public Object call(Context cx, Scriptable scope, Scriptable thisObj, java.lang.Object[] args) {
-            Function func = (Function)args[0];
-            Object[] param = new Object[1];
-            
-            for (Iterator i = list.iterator(); i.hasNext(); ) {
-                param[0] = i.next();
-                func.call(cx, scope, thisObj, param);
-            }
-            return null;
-        }
-         
-        public Scriptable construct(Context cx, Scriptable scope, java.lang.Object[] args) {
-            return null;
-        }
-        
-        public String getClassName() {
-            return "EachClosure";
-        }
     }
 
 }

Copied: struts/flow/trunk/src/java/org/apache/struts/flow/sugar/ScriptableMap.java (from r154003, struts/flow/trunk/src/java/org/apache/struts/flow/core/ScriptableMap.java)
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/sugar/ScriptableMap.java?view=diff&rev=154604&p1=struts/flow/trunk/src/java/org/apache/struts/flow/core/ScriptableMap.java&r1=154003&p2=struts/flow/trunk/src/java/org/apache/struts/flow/sugar/ScriptableMap.java&r2=154604
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/ScriptableMap.java (original)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/sugar/ScriptableMap.java Sun Feb 20 18:13:38 2005
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.struts.flow.core;
+package org.apache.struts.flow.sugar;
 
 import org.mozilla.javascript.NativeJavaObject;
 import org.mozilla.javascript.Scriptable;
@@ -25,7 +25,7 @@
 /**
  * Wrap a java.util.Map for JavaScript.
  */
-public class ScriptableMap extends NativeJavaObject implements Scriptable, Wrapper, Serializable {
+public class ScriptableMap extends JavaObjectWrapper implements Scriptable, Wrapper, Serializable {
 
     private Map map;
     private String functionPrefix = null;
@@ -38,8 +38,8 @@
         this.map = map;
     }
     
-    public ScriptableMap(Scriptable scope, Object javaObject, Class staticType, String functionPrefix) {
-        super(scope, javaObject, staticType);
+    public ScriptableMap(Scriptable scope, Object javaObject, Class staticType, String functionPrefix, Map functions) {
+        super(scope, javaObject, staticType, functions);
         this.functionPrefix = functionPrefix;
         if (javaObject instanceof Map) {
             this.map = (Map)javaObject;

Copied: struts/flow/trunk/src/java/org/apache/struts/flow/sugar/SugarWrapFactory.java (from r154003, struts/flow/trunk/src/java/org/apache/struts/flow/core/SugarWrapFactory.java)
URL: http://svn.apache.org/viewcvs/struts/flow/trunk/src/java/org/apache/struts/flow/sugar/SugarWrapFactory.java?view=diff&rev=154604&p1=struts/flow/trunk/src/java/org/apache/struts/flow/core/SugarWrapFactory.java&r1=154003&p2=struts/flow/trunk/src/java/org/apache/struts/flow/sugar/SugarWrapFactory.java&r2=154604
==============================================================================
--- struts/flow/trunk/src/java/org/apache/struts/flow/core/SugarWrapFactory.java (original)
+++ struts/flow/trunk/src/java/org/apache/struts/flow/sugar/SugarWrapFactory.java Sun Feb 20 18:13:38 2005
@@ -13,22 +13,59 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.struts.flow.core;
+package org.apache.struts.flow.sugar;
 
 import org.mozilla.javascript.*;
 import java.io.*;
 import java.util.*;
+import java.lang.reflect.*;
 
 /**
- * Wrap a java.util.Collection for JavaScript.
+ * Wraps Java objects by adding support for function extensions, which are
+ * functions that extend existing Java objects at the Rhino level.
  */
 public class SugarWrapFactory extends WrapFactory {
     
     private String mapFuncPrefix = "fn_";
     
+    private List functionRegistry = new ArrayList();
+    private Map functionMappings = new HashMap();
+    
+    public SugarWrapFactory() {
+        super();
+        
+        // Add default methods
+        addExtensionFunctions(ListExtensions.class);
+        addExtensionFunctions(FileExtensions.class);
+    }
+    
     public void setMapFunctionPrefix(String prefix) {
         this.mapFuncPrefix = prefix;
     }
+    
+    public void addExtensionFunction(Class cls, String name, Method func) {
+        int modifier = func.getModifiers();
+        if (Modifier.isStatic(modifier) && Modifier.isPublic(modifier)) {
+            ExtensionFunctionEntry entry = new ExtensionFunctionEntry(cls, name, func);
+            functionRegistry.add(entry);
+        } else {
+            throw new IllegalArgumentException("Method "+func+" must be static and public");
+        }
+    }
+    
+    public void addExtensionFunctions(Class holder) {
+        Method[] methods = holder.getDeclaredMethods();
+        for (int x=0; x<methods.length; x++) {
+            int modifier = methods[x].getModifiers();
+            if (Modifier.isStatic(modifier) && Modifier.isPublic(modifier)) {
+                String name = methods[x].getName();
+                Class target = methods[x].getParameterTypes()[0];
+                ExtensionFunctionEntry entry = new ExtensionFunctionEntry(target, name, methods[x]);
+                functionRegistry.add(entry);
+            }
+        }
+        
+    }
 
     /**
      * Wrap Java object as Scriptable instance to allow full access to its
@@ -48,17 +85,34 @@
     public Scriptable wrapAsJavaObject(Context cx, Scriptable scope,
         Object javaObject, Class staticType) {
         
+        Map map = getExtensionFunctions(javaObject.getClass());
+        
         Scriptable wrap = null;
         if (javaObject instanceof Map) {
             wrap = new ScriptableMap(scope, javaObject, staticType, mapFuncPrefix);
         } else if (javaObject instanceof List) {
-            wrap = new ScriptableList(scope, javaObject, staticType);
+            wrap = new ScriptableList(scope, javaObject, staticType, map);
         } else {
-            wrap = super.wrapAsJavaObject(cx, scope, javaObject, staticType);
+            wrap = new JavaObjectWrapper(scope, javaObject, staticType, map);
         }
         return wrap;
     }
     
+    private Map getExtensionFunctions(Class cls) {
+        Map map = (Map)functionMappings.get(cls);
+        ExtensionFunctionEntry entry;
+        if (map == null) {
+            map = new HashMap();
+            for (Iterator i = functionRegistry.iterator(); i.hasNext(); ) {
+                entry = (ExtensionFunctionEntry)i.next();
+                if (entry.clazz.isAssignableFrom(cls)) {
+                    map.put(entry.name, entry.function);
+                }
+            }
+        }
+        return map;
+    }
+    
     // temporary method for testing
     public static final void main(String[] args) throws Exception {
         // Creates and enters a Context. The Context stores information
@@ -88,6 +142,18 @@
             // Exit from the context.
             Context.exit();
         }   
+    }
+    
+    class ExtensionFunctionEntry {
+        public Class clazz;
+        public String name;
+        public Method function;
+        
+        public ExtensionFunctionEntry(Class cls, String name, Method func) {
+            this.clazz = cls;
+            this.name = name;
+            this.function = func;
+        }
     }
 
 }



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