View Javadoc

1   /*
2    * Copyright 1999-2004 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.apache.struts.flow.sugar;
17  
18  import org.mozilla.javascript.*;
19  import java.io.*;
20  import java.util.*;
21  import java.lang.reflect.*;
22  import org.apache.commons.beanutils.DynaBean;
23  
24  /***
25   * Wraps Java objects by adding support for function extensions, which are
26   * functions that extend existing Java objects at the Rhino level.
27   */
28  public class SugarWrapFactory extends WrapFactory {
29      
30      private List functionRegistry = new ArrayList();
31      private Map functionMappings = new HashMap();
32      
33      public SugarWrapFactory() {
34          super();
35          
36          // Add default methods
37          addExtensionFunctions(CollectionExtensions.class);
38          addExtensionFunctions(ListExtensions.class);
39          addExtensionFunctions(FileExtensions.class);
40          addExtensionFunctions(InputStreamExtensions.class);
41      }
42      
43      public void addExtensionFunction(Class cls, String name, Method func) {
44          int modifier = func.getModifiers();
45          if (Modifier.isStatic(modifier) && Modifier.isPublic(modifier)) {
46              ExtensionEntry entry = new ExtensionEntry(cls, name, func);
47              functionRegistry.add(entry);
48          } else {
49              throw new IllegalArgumentException("Method "+func+" must be static and public");
50          }
51      }
52      
53      public void addExtensionFunctions(Class holder) {
54          Method[] methods = holder.getDeclaredMethods();
55          for (int x=0; x<methods.length; x++) {
56              int modifier = methods[x].getModifiers();
57              if (Modifier.isStatic(modifier) && Modifier.isPublic(modifier)) {
58                  String name = methods[x].getName();
59                  Class target = methods[x].getParameterTypes()[0];
60                  ExtensionEntry entry = new ExtensionEntry(target, name, methods[x]);
61                  functionRegistry.add(entry);
62              }
63          }
64          
65      }
66  
67      /***
68       * Wrap Java object as Scriptable instance to allow full access to its
69       * methods and fields from JavaScript.
70       * <p>
71       * {@link #wrap(Context, Scriptable, Object, Class)} and
72       * {@link #wrapNewObject(Context, Scriptable, Object)} call this method
73       * when they can not convert <tt>javaObject</tt> to JavaScript primitive
74       * value or JavaScript array.
75       * @param cx the current Context for this thread
76       * @param scope the scope of the executing script
77       * @param javaObject the object to be wrapped
78       * @param staticType type hint. If security restrictions prevent to wrap
79                  object based on its class, staticType will be used instead.
80       * @return the wrapped value which shall not be null
81       */
82      public Scriptable wrapAsJavaObject(Context cx, Scriptable scope,
83          Object javaObject, Class staticType) {
84              
85          Map map = getExtensionFunctions(javaObject.getClass());
86          Scriptable wrap = null;
87          if (javaObject instanceof Map) {
88              wrap = new ScriptableMap(scope, javaObject, staticType, map);
89          } else if (javaObject instanceof DynaBean) {
90              wrap = new ScriptableDynaBean(scope, javaObject, staticType, map);
91          } else if (javaObject instanceof List) {
92              wrap = new ScriptableList(scope, javaObject, staticType, map);
93          } else {
94              wrap = new JavaObjectWrapper(scope, javaObject, staticType, map);
95          }
96          return wrap;
97      }
98      
99      private Map getExtensionFunctions(Class cls) {
100         Map map = (Map)functionMappings.get(cls);
101         ExtensionEntry entry;
102         if (map == null) {
103             map = new HashMap();
104             for (Iterator i = functionRegistry.iterator(); i.hasNext(); ) {
105                 entry = (ExtensionEntry)i.next();
106                 if (entry.clazz.isAssignableFrom(cls)) {
107                     map.put(entry.name, entry.function);
108                 }
109             }
110         }
111         return map;
112     }
113     
114     /***
115      *  Test driver.  Also used for generating javascript javadocs.
116      */
117     public static final void main(String[] args) throws Exception {
118         Context cx = Context.enter();
119         try {
120             cx.setWrapFactory(new SugarWrapFactory());
121             // Initialize the standard objects (Object, Function, etc.)
122             // This must be done before scripts can be executed. Returns
123             // a scope object that we use in later calls.
124             //Scriptable scope = cx.initStandardObjects();
125             Scriptable scope = new ImporterTopLevel(cx);
126 
127         
128             FileReader reader = new FileReader(args[0]);
129             
130             // Set up "arguments" in the global scope to contain the command
131             // line arguments after the name of the script to execute
132             Object[] array = args;
133             if (args.length > 0) {
134                 int length = args.length - 1;
135                 array = new Object[length];
136                 System.arraycopy(args, 1, array, 0, length);
137             }
138             Scriptable argsObj = cx.newArray(scope, array);
139             scope.put("arguments", scope, argsObj);
140             Object result = cx.evaluateReader(scope, reader, args[0], 1, null);
141         
142             // Convert the result to a string and print it.
143             System.err.println(cx.toString(result));
144         } finally {
145             // Exit from the context.
146             Context.exit();
147         }     
148     }
149     
150     class ExtensionEntry {
151         public Class clazz;
152         public String name;
153         public Method function;
154         
155         public ExtensionEntry(Class cls, String name, Method func) {
156             this.clazz = cls;
157             this.name = name;
158             this.function = func;
159         }
160     }
161 
162 }