KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > es > wrapper > ESIntrospector


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  * Free SoftwareFoundation, Inc.
23  * 59 Temple Place, Suite 330
24  * Boston, MA 02111-1307 USA
25  *
26  * @author Scott Ferguson
27  */

28
29 package com.caucho.es.wrapper;
30
31 import com.caucho.server.util.CauchoSystem;
32 import com.caucho.util.WeakLruCache;
33
34 import java.beans.BeanInfo JavaDoc;
35 import java.beans.IntrospectionException JavaDoc;
36 import java.beans.Introspector JavaDoc;
37 import java.beans.MethodDescriptor JavaDoc;
38 import java.beans.PropertyDescriptor JavaDoc;
39 import java.lang.ref.SoftReference JavaDoc;
40 import java.lang.reflect.Field JavaDoc;
41 import java.lang.reflect.Method JavaDoc;
42 import java.lang.reflect.Modifier JavaDoc;
43     
44 /**
45  * Analyzes the class from a JavaScript perspective.
46  *
47  * <p>Each class Foo searches for its "FooEcmaWrap" to see if there are
48  * any method changes.
49  */

50 public class ESIntrospector {
51   /**
52    * Cache of analyzed classes to avoid duplication.
53    */

54   static WeakLruCache<Class JavaDoc,SoftReference JavaDoc<ESBeanInfo>> _beanMap =
55     new WeakLruCache<Class JavaDoc,SoftReference JavaDoc<ESBeanInfo>>(256);
56   
57   static Integer JavaDoc NULL = new Integer JavaDoc(0);
58   final static int METHOD = 1;
59   final static int PROPERTY = 2;
60   final static int MASK = 3;
61
62   static String JavaDoc []path = new String JavaDoc[] {"", "com.caucho.eswrap"};
63
64   /**
65    * Analyzes the class, returning the calculated ESBeanInfo.
66    *
67    * @param cl the class to be analyzed.
68    * @return the analyzed bean info.
69    */

70   public static ESBeanInfo getBeanInfo(Class JavaDoc cl)
71     throws IntrospectionException JavaDoc
72   {
73     ESBeanInfo info;
74
75     SoftReference JavaDoc<ESBeanInfo> infoRef = _beanMap.get(cl);
76       
77     info = infoRef != null ? infoRef.get() : null;
78
79     if (info != null)
80       return info;
81
82     info = new ESBeanInfo(cl);
83
84     getMethods(info, cl);
85
86     _beanMap.put(cl, new SoftReference JavaDoc<ESBeanInfo>(info));
87     
88     return info;
89   }
90
91   /**
92    * Analyzes a Java method, converting it to any equivalent JavaScript
93    * properties.
94    *
95    * <pre>
96    * keys() -> for (item in obj) {
97    * getFoo() -> obj.foo
98    * getFoo(int) -> obj.foo[i]
99    * getFoo(String) -> obj.foo["name"]
100    * remove(String) -> delete obj.foo
101    * </pre>
102    *
103    * @param info the bean info to be filled
104    * @param cl the bean's class
105    * @param md the method descriptor
106    * @param overwrite if true, this overwrites any previous definition
107    */

108   private static void
109     analyzeProperty(ESBeanInfo info, Class JavaDoc cl, ESMethodDescriptor md,
110             boolean overwrite)
111     throws IntrospectionException JavaDoc
112   {
113     Method JavaDoc method = md.getMethod();
114     int modifiers = method.getModifiers();
115
116     if (! Modifier.isPublic(modifiers))
117       return;
118
119     String JavaDoc name = method.getName();
120     String JavaDoc propName;
121     Class JavaDoc returnType = method.getReturnType();
122     String JavaDoc returnName = returnType.getName();
123     Class JavaDoc []params = md.getParameterTypes();
124
125     if (name.equals("keys") && params.length == 0 &&
126     (returnName.equals("java.util.Iterator") ||
127      (returnName.equals("java.util.Enumeration")))) {
128       info.iterator = md;
129     }
130     else if (name.equals("iterator") && params.length == 0 &&
131          (returnName.equals("java.util.Iterator") ||
132           (returnName.equals("java.util.Enumeration")))) {
133       // keys has priority over iterator
134
if (info.iterator == null)
135     info.iterator = md;
136     }
137     else if (name.startsWith("get") && ! name.equals("get")) {
138       propName = Introspector.decapitalize(name.substring(3));
139
140       // kill match?
141
if (returnName.equals("void") && params.length < 2) {
142     // XXX: props.put(propName, BAD);
143
return;
144       }
145
146       // name keys
147
if (params.length == 0 &&
148       (propName.endsWith("Keys") && ! propName.equals("Keys") ||
149        propName.endsWith("Names") && ! propName.equals("Names")) &&
150       (returnName.equals("java.util.Iterator") ||
151        (returnName.equals("java.util.Enumeration")))) {
152     if (propName.endsWith("Keys"))
153       info.addNamedProp(propName.substring(0, propName.length() - 4),
154                 null, null, null, md);
155     else
156       info.addNamedProp(propName.substring(0, propName.length() - 5),
157                 null, null, null, md);
158       }
159       // index length
160
else if (params.length == 0 &&
161            (propName.endsWith("Size") && ! propName.equals("Size") ||
162         propName.endsWith("Length") && ! propName.equals("Length")) &&
163            (returnName.equals("int"))) {
164     info.addProp(propName, null, md, null);
165     if (propName.endsWith("Size"))
166       info.addIndexedProp(propName.substring(0, propName.length() - 4),
167                   null, null, md);
168     else
169       info.addIndexedProp(propName.substring(0, propName.length() - 6),
170                   null, null, md);
171       }
172       else if (params.length == 0)
173     info.addProp(propName, null, md, null);
174       else if (params.length == 1 &&
175            params[0].getName().equals("java.lang.String")) {
176     info.addNamedProp(propName, md, null, null, null);
177       } else if (params.length == 1 &&
178            params[0].getName().equals("int")) {
179     info.addIndexedProp(propName, md, null, null);
180       } else if (params.length == 1) {
181     // xxx: add bad?
182
}
183     } else if (name.startsWith("set") && ! name.equals("set")) {
184       propName = Introspector.decapitalize(name.substring(3));
185
186       if (params.length == 0)
187     return;
188
189       // kill match?
190
if (! returnType.getName().equals("void") && params.length < 3) {
191     // XXX: add bad? props.put(propName, NULL);
192
return;
193       }
194
195       if (params.length == 1)
196     info.addProp(propName, null, null, md);
197       else if (params.length == 2 &&
198            params[0].getName().equals("java.lang.String")) {
199     info.addNamedProp(propName, null, md, null, null);
200       } else if (params.length == 2 &&
201          params[0].getName().equals("int")) {
202     info.addIndexedProp(propName, null, md, null);
203       } else if (params.length == 2) {
204     // XXX: props.put(propName, NULL);
205
}
206     } else if (name.startsWith("remove") && ! name.equals("remove") ||
207            name.startsWith("delete") && ! name.equals("remove")) {
208       propName = Introspector.decapitalize(name.substring(6));
209
210       if (params.length == 0)
211     return;
212
213       // kill match?
214
if (! returnType.getName().equals("void") && params.length < 2) {
215     // XXX: add bad? props.put(propName, NULL);
216
return;
217       }
218
219       //if (params.length == 0)
220
// info.addProp(propName, null, null, md);
221
if (params.length == 1 &&
222       params[0].getName().equals("java.lang.String")) {
223     info.addNamedProp(propName, null, null, md, null);
224       }
225       /*
226       else if (params.length == 2 &&
227          params[0].getName().equals("int")) {
228     info.addIndexedProp(propName, null, md, null);
229       } else if (params.length == 2) {
230     // XXX: props.put(propName, NULL);
231       }
232       */

233     }
234   }
235
236   /**
237    * Add methods from a FooEcmaWrap class.
238    */

239   static void addEcmaMethods(ESBeanInfo info, Class JavaDoc cl, Class JavaDoc wrapCl, int mask)
240     throws IntrospectionException JavaDoc
241   {
242     Method JavaDoc []methods = wrapCl.getDeclaredMethods();
243
244     for (int i = 0; i < methods.length; i++) {
245       int modifiers = methods[i].getModifiers();
246
247       if (! Modifier.isStatic(modifiers))
248     continue;
249
250       ESMethodDescriptor md = info.createMethodDescriptor(methods[i], true);
251
252       if ((mask & METHOD) != 0)
253     info.addMethod(md);
254       if ((mask & PROPERTY) != 0)
255     analyzeProperty(info, cl, md, true);
256     }
257   }
258
259   /**
260    * Search for FooEcmaWrap classes.
261    *
262    * @param info the bean info to be filled
263    * @param cl the bean being analyzed
264    * @param mask the replacement mask.
265    */

266   static void addEcmaWrap(ESBeanInfo info, Class JavaDoc cl, int mask)
267     throws IntrospectionException JavaDoc
268   {
269     String JavaDoc name = cl.getName();
270     int lastDot = name.lastIndexOf('.');
271     String JavaDoc prefix = lastDot == -1 ? "" : name.substring(0, lastDot);
272     String JavaDoc tail = lastDot == -1 ? name : name.substring(lastDot + 1);
273
274     ClassLoader JavaDoc loader = cl.getClassLoader();
275
276     for (int i = 0; i < path.length; i++) {
277       String JavaDoc testName;
278
279       if (path[i].equals(""))
280     testName = name + "EcmaWrap";
281       else
282     testName = path[i] + "." + name + "EcmaWrap";
283
284       try {
285     Class JavaDoc wrapCl = CauchoSystem.loadClass(testName, false, loader);
286
287     if (wrapCl != null) {
288       addEcmaMethods(info, cl, wrapCl, mask);
289       return;
290     }
291       } catch (ClassNotFoundException JavaDoc e) {
292       }
293
294       if (path[i].equals(""))
295     testName = tail + "EcmaWrap";
296       else
297     testName = path[i] + "." + tail + "EcmaWrap";
298
299       try {
300     Class JavaDoc wrapCl = CauchoSystem.loadClass(testName, false, loader);
301
302     if (wrapCl != null) {
303       addEcmaMethods(info, cl, wrapCl, mask);
304       return;
305     }
306       } catch (ClassNotFoundException JavaDoc e) {
307       }
308     }
309   }
310
311   /**
312    * Fills information for a FooBeanInfo class.
313    *
314    * @param info the result information object
315    * @param cl the bean class
316    *
317    * @return a mask
318    */

319   static int getBeanInfo(ESBeanInfo info, Class JavaDoc cl)
320   {
321     try {
322       String JavaDoc name = cl.getName() + "BeanInfo";
323       
324       Class JavaDoc beanClass = CauchoSystem.loadClass(name, false, cl.getClassLoader());
325       if (beanClass == null)
326     return MASK;
327       BeanInfo JavaDoc beanInfo = (BeanInfo JavaDoc) beanClass.newInstance();
328
329       MethodDescriptor JavaDoc []mds = beanInfo.getMethodDescriptors();
330       if (mds == null)
331     return MASK;
332
333       for (int i = 0; i < mds.length; i++) {
334     Method JavaDoc method = mds[i].getMethod();
335     int modifiers = method.getModifiers();
336
337     if (! Modifier.isStatic(modifiers) &&
338         ! method.getDeclaringClass().isAssignableFrom(cl))
339       continue;
340
341     info.addMethod(mds[i], true);
342       }
343
344       return MASK & ~METHOD;
345     } catch (Exception JavaDoc e) {
346       return MASK;
347     }
348   }
349
350   static void getPropBeanInfo(ESBeanInfo info, Class JavaDoc cl)
351   {
352     try {
353       String JavaDoc name = cl.getName() + "BeanInfo";
354       Class JavaDoc beanClass = CauchoSystem.loadClass(name, false, cl.getClassLoader());
355       if (beanClass == null)
356     return;
357       BeanInfo JavaDoc beanInfo = (BeanInfo JavaDoc) beanClass.newInstance();
358
359       PropertyDescriptor JavaDoc []props = beanInfo.getPropertyDescriptors();
360
361       for (int i = 0; props != null && i < props.length; i++) {
362         ESMethodDescriptor read;
363         ESMethodDescriptor write;
364         
365         read = new ESMethodDescriptor(props[i].getReadMethod(), false, false);
366         write = new ESMethodDescriptor(props[i].getWriteMethod(),
367                                        false, false);
368
369         info.addProp(props[i].getName(), null, read, write, true);
370       }
371     } catch (Exception JavaDoc e) {
372     }
373   }
374
375   private static void getMethods(ESBeanInfo info, Class JavaDoc cl)
376     throws IntrospectionException JavaDoc
377   {
378     if (! cl.isPrimitive() && ! cl.isArray() &&
379         cl.getName().indexOf('.') < 0)
380       info.addNonPkgClass(cl.getName());
381
382     getPropBeanInfo(info, cl);
383
384     int mask = getBeanInfo(info, cl);
385
386     if (mask == 0)
387       return;
388
389     Class JavaDoc []interfaces = cl.getInterfaces();
390     for (int i = 0; i < interfaces.length; i++) {
391       ESBeanInfo subInfo = getBeanInfo(interfaces[i]);
392
393       if ((mask & METHOD) != 0)
394     info.addMethods(subInfo);
395       if ((mask & PROPERTY) != 0)
396     info.addProps(subInfo);
397     }
398
399     Class JavaDoc superClass = cl.getSuperclass();
400     if (superClass != null) {
401       ESBeanInfo subInfo = getBeanInfo(superClass);
402
403       if ((mask & METHOD) != 0)
404     info.addMethods(subInfo);
405       if ((mask & PROPERTY) != 0)
406     info.addProps(subInfo);
407     }
408
409     int modifiers = cl.getModifiers();
410     if (Modifier.isPublic(modifiers)) {
411       Method JavaDoc []methods = cl.getDeclaredMethods();
412       int len = methods == null ? 0 : methods.length;
413       for (int i = 0; i < len; i++) {
414     ESMethodDescriptor md = info.createMethodDescriptor(methods[i], false);
415
416     if ((mask & METHOD) != 0)
417       info.addMethod(md);
418     if ((mask & PROPERTY) != 0)
419       analyzeProperty(info, cl, md, false);
420       }
421
422       Field JavaDoc []fields = cl.getDeclaredFields();
423       for (int i = 0; fields != null && i < fields.length; i++) {
424     info.addField(fields[i]);
425       }
426     }
427
428     addEcmaWrap(info, cl, mask);
429   }
430 }
431
Popular Tags