1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
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
122
123
124
125 Scriptable scope = new ImporterTopLevel(cx);
126
127
128 FileReader reader = new FileReader(args[0]);
129
130
131
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
143 System.err.println(cx.toString(result));
144 } finally {
145
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 }