001: /*
002: * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: * Free SoftwareFoundation, Inc.
023: * 59 Temple Place, Suite 330
024: * Boston, MA 02111-1307 USA
025: *
026: * @author Scott Ferguson
027: */
028:
029: package com.caucho.es.wrapper;
030:
031: import java.beans.IntrospectionException;
032: import java.beans.MethodDescriptor;
033: import java.beans.PropertyDescriptor;
034: import java.lang.reflect.Constructor;
035: import java.lang.reflect.Field;
036: import java.lang.reflect.Method;
037: import java.lang.reflect.Modifier;
038: import java.util.ArrayList;
039: import java.util.HashMap;
040: import java.util.Iterator;
041: import java.util.Map;
042:
043: /**
044: * Full analyzed information on the class as a JavaScript object.
045: */
046: public class ESBeanInfo {
047: static String BAD = "bad";
048:
049: Class cl;
050: HashMap<String, ArrayList<Method>> _methodMap;
051: HashMap<String, ArrayList<Method>> _staticMethodMap;
052: HashMap propMap;
053: ArrayList nonPkgClasses = new ArrayList();
054: PropertyDescriptor[] propertyDescriptors;
055: ESMethodDescriptor iterator;
056:
057: ESBeanInfo(Class cl) {
058: this .cl = cl;
059: _methodMap = new HashMap<String, ArrayList<Method>>();
060: _staticMethodMap = new HashMap<String, ArrayList<Method>>();
061: propMap = new HashMap();
062: }
063:
064: void addNonPkgClass(String name) {
065: if (name.indexOf('$') >= 0)
066: return;
067:
068: if (!nonPkgClasses.contains(name))
069: nonPkgClasses.add(name);
070: }
071:
072: ArrayList getNonPkgClasses() {
073: return nonPkgClasses;
074: }
075:
076: /**
077: * Return the property descriptors for the bean.
078: */
079: public PropertyDescriptor[] getPropertyDescriptors() {
080: if (propertyDescriptors == null)
081: propertyDescriptors = propMapToArray(propMap);
082:
083: return propertyDescriptors;
084: }
085:
086: private static PropertyDescriptor[] propMapToArray(HashMap props) {
087: int count = 0;
088: Iterator i = props.keySet().iterator();
089: while (i.hasNext()) {
090: String key = (String) i.next();
091: Object value = props.get(key);
092:
093: if (value != BAD)
094: count++;
095: }
096:
097: PropertyDescriptor[] descriptors = new PropertyDescriptor[count];
098:
099: count = 0;
100: i = props.keySet().iterator();
101: while (i.hasNext()) {
102: String key = (String) i.next();
103: Object value = props.get(key);
104:
105: if (value != BAD) {
106: descriptors[count] = (PropertyDescriptor) value;
107: count++;
108: }
109: }
110:
111: return descriptors;
112: }
113:
114: void addProp(String name, Field field, ESMethodDescriptor getter,
115: ESMethodDescriptor setter, boolean overwrite)
116: throws IntrospectionException {
117: Object value = propMap.get(name);
118:
119: if (value instanceof ESPropertyDescriptor
120: && !(value instanceof ESIndexedPropertyDescriptor)
121: && !(value instanceof NamedPropertyDescriptor)) {
122: ESPropertyDescriptor prop = (ESPropertyDescriptor) value;
123:
124: if (field != null)
125: prop.field = field;
126: if (getter != null)
127: prop.getter = getter;
128: if (setter != null)
129: prop.setter = setter;
130:
131: propMap.put(name, prop);
132: } else if (value == null || overwrite) {
133: propMap.put(name, new ESPropertyDescriptor(name, field,
134: getter, setter));
135: } else {
136: propMap.put(name, BAD);
137: }
138: }
139:
140: void addProp(String name, Field field, ESMethodDescriptor getter,
141: ESMethodDescriptor setter) throws IntrospectionException {
142: addProp(name, field, getter, setter, false);
143: }
144:
145: void addIndexedProp(String name, ESMethodDescriptor getter,
146: ESMethodDescriptor setter, ESMethodDescriptor size,
147: boolean overwrite) throws IntrospectionException {
148: Object value = propMap.get(name);
149:
150: if (value instanceof ESIndexedPropertyDescriptor) {
151: ESIndexedPropertyDescriptor prop = (ESIndexedPropertyDescriptor) value;
152:
153: if (getter != null)
154: prop.getter = getter;
155: if (setter != null)
156: prop.setter = setter;
157: if (size != null)
158: prop.size = size;
159:
160: propMap.put(name, prop);
161: } else if (value == null || overwrite) {
162: propMap.put(name, new ESIndexedPropertyDescriptor(name,
163: getter, setter, size));
164: } else
165: propMap.put(name, BAD);
166: }
167:
168: void addIndexedProp(String name, ESMethodDescriptor getter,
169: ESMethodDescriptor setter, ESMethodDescriptor size)
170: throws IntrospectionException {
171: addIndexedProp(name, getter, setter, size, false);
172: }
173:
174: void addNamedProp(String name, ESMethodDescriptor getter,
175: ESMethodDescriptor setter, ESMethodDescriptor remover,
176: ESMethodDescriptor iterator, boolean overwrite)
177: throws IntrospectionException {
178: Object value = propMap.get(name);
179:
180: if (value instanceof NamedPropertyDescriptor) {
181: NamedPropertyDescriptor prop = (NamedPropertyDescriptor) value;
182:
183: if (getter != null)
184: prop.namedGetter = getter;
185: if (setter != null)
186: prop.namedSetter = setter;
187: if (remover != null)
188: prop.namedRemover = remover;
189: if (iterator != null)
190: prop.namedIterator = iterator;
191: } else if (value == null || overwrite) {
192: try {
193: propMap.put(name, new NamedPropertyDescriptor(name,
194: null, null, getter, setter, remover, iterator));
195: } catch (Exception e) {
196: propMap.put(name, BAD);
197: }
198: } else
199: propMap.put(name, BAD);
200: }
201:
202: void addNamedProp(String name, ESMethodDescriptor getter,
203: ESMethodDescriptor setter, ESMethodDescriptor remover,
204: ESMethodDescriptor iterator) throws IntrospectionException {
205: addNamedProp(name, getter, setter, remover, iterator, false);
206: }
207:
208: /**
209: * Returns the methods matching the given name.
210: */
211: public ArrayList<Method> getMethods(String name) {
212: return _methodMap.get(name);
213: }
214:
215: /**
216: * Returns the static methods matching the given name.
217: */
218: public ArrayList<Method> getStaticMethods(String name) {
219: return _staticMethodMap.get(name);
220: }
221:
222: public ArrayList getConstructors() {
223: ArrayList overload = new ArrayList();
224: if (!Modifier.isPublic(cl.getModifiers()))
225: return overload;
226:
227: // non-static inner classes have no constructor
228: if (cl.getDeclaringClass() != null
229: && !Modifier.isStatic(cl.getModifiers()))
230: return overload;
231:
232: Constructor[] constructors = cl.getConstructors();
233: for (int i = 0; i < constructors.length; i++) {
234: if (!Modifier.isPublic(constructors[i].getModifiers()))
235: continue;
236:
237: if (Modifier.isPublic(constructors[i].getModifiers()))
238: addConstructor(overload, constructors[i]);
239: }
240:
241: return overload;
242: }
243:
244: private void addConstructor(ArrayList overload,
245: Constructor constructor) {
246: int modifiers = constructor.getModifiers();
247:
248: if (!Modifier.isPublic(modifiers))
249: return;
250:
251: Class[] params = constructor.getParameterTypes();
252: for (int i = 0; i < params.length; i++) {
253: if (!Modifier.isPublic(params[i].getModifiers()))
254: return;
255: if (!params[i].isPrimitive() && !params[i].isArray()
256: && params[i].getName().indexOf('.') < 0)
257: addNonPkgClass(params[i].getName());
258: }
259: int length = params.length;
260:
261: Object oldConstructor;
262: if (length < overload.size()
263: && (oldConstructor = overload.get(length)) != null) {
264: overload.set(length, BAD);
265: } else {
266: while (overload.size() <= length)
267: overload.add(null);
268: overload.set(length, constructor);
269: }
270: }
271:
272: /**
273: * Create a new method descriptor.
274: */
275: ESMethodDescriptor createMethodDescriptor(Method method,
276: boolean overwrite) throws IntrospectionException {
277: boolean staticVirtual = isStaticVirtual(cl, method);
278:
279: return new ESMethodDescriptor(method, overwrite, staticVirtual);
280: }
281:
282: /**
283: * Returns true if this is a static virtual method.
284: * Static virtual methods are used by FooEcmaWrap classes add new methods
285: * to a class.
286: *
287: * <p>The method is always static and the first argument has the class
288: * of the overwriting class.
289: */
290: private static boolean isStaticVirtual(Class cl, Method method) {
291: int modifiers = method.getModifiers();
292:
293: if (!Modifier.isStatic(modifiers))
294: return false;
295:
296: if (method.getName().equals("<init>"))
297: return false;
298:
299: if (!method.getDeclaringClass().getName().endsWith("EcmaWrap"))
300: return false;
301:
302: Class[] params = method.getParameterTypes();
303:
304: boolean result = params.length > 0 && params[0].equals(cl);
305:
306: return result;
307: }
308:
309: /**
310: * Adds a new method to the bean info, changing the overwrite property.
311: *
312: * @param oldMd the old method descriptor.
313: * @param boolean true if overwritable.
314: */
315: void addMethod(MethodDescriptor oldMd, boolean overwrite)
316: throws IntrospectionException {
317: ESMethodDescriptor md = createMethodDescriptor(oldMd
318: .getMethod(), overwrite);
319: md.setName(oldMd.getName());
320: addMethod(_methodMap, md, false);
321: addMethod(_staticMethodMap, md, true);
322: }
323:
324: /**
325: * Adds a new method to the bean info.
326: *
327: * @param me the method descriptor.
328: */
329: void addMethod(ESMethodDescriptor md) throws IntrospectionException {
330: addMethod(_methodMap, md, false);
331: addMethod(_staticMethodMap, md, true);
332: }
333:
334: void addMethods(ESBeanInfo info) throws IntrospectionException {
335: if (info.iterator != null) {
336: iterator = info.iterator;
337: }
338: addMap(_methodMap, info._methodMap, false);
339: addMap(_staticMethodMap, info._staticMethodMap, true);
340: }
341:
342: /**
343: * Merges two method maps together to produce a new map.
344: */
345: private void addMap(HashMap newMap, HashMap oldMap, boolean isStatic) {
346: Iterator i = oldMap.entrySet().iterator();
347:
348: while (i.hasNext()) {
349: Map.Entry entry = (Map.Entry) i.next();
350: ArrayList overload = (ArrayList) entry.getValue();
351:
352: for (int j = 0; j < overload.size(); j++) {
353: ESMethodDescriptor[] mds = (ESMethodDescriptor[]) overload
354: .get(j);
355:
356: if (mds != null) {
357: for (int k = 0; k < mds.length; k++)
358: addMethod(newMap, mds[k], isStatic);
359: }
360: }
361: }
362: }
363:
364: /**
365: * Adds a method to the method map.
366: *
367: * @param map the method map to modify.
368: * @param md the method descriptor of the new method.
369: * @param isStatic true if this is a static method.
370: */
371: private void addMethod(HashMap map, ESMethodDescriptor md,
372: boolean isStatic) {
373: Method method = md.getMethod();
374: int modifiers = method.getModifiers();
375: boolean staticVirtual = md.isStaticVirtual();
376: boolean overwrite = md.isOverwrite();
377:
378: if (!Modifier.isPublic(modifiers))
379: return;
380: if (isStatic && !md.isStatic())
381: return;
382:
383: Class[] params = md.getParameterTypes();
384: for (int i = 0; i < params.length; i++) {
385: if (!Modifier.isPublic(params[i].getModifiers()))
386: return;
387: if (!params[i].isPrimitive() && !params[i].isArray()
388: && params[i].getName().indexOf('.') < 0)
389: addNonPkgClass(params[i].getName());
390: }
391:
392: ArrayList overload = (ArrayList) map.get(md.getName());
393: if (overload == null) {
394: overload = new ArrayList();
395: map.put(md.getName(), overload);
396: }
397:
398: int length = params.length;
399:
400: if (length >= overload.size()) {
401: while (overload.size() <= length)
402: overload.add(null);
403: }
404:
405: ESMethodDescriptor[] oldMethods;
406: oldMethods = (ESMethodDescriptor[]) overload.get(length);
407:
408: if (oldMethods == null) {
409: overload.set(length, new ESMethodDescriptor[] { md });
410: return;
411: }
412:
413: for (int i = 0; i < oldMethods.length; i++) {
414: ESMethodDescriptor testMd = oldMethods[i];
415:
416: if (testMd.overwrites(md))
417: return;
418: else if (md.overwrites(testMd)) {
419: oldMethods[i] = md;
420: return;
421: }
422:
423: Class[] oldParams = testMd.getParameterTypes();
424:
425: int j = 0;
426: for (; j < length; j++) {
427: if (!params[j].equals(oldParams[j]))
428: break;
429: }
430:
431: // duplicates another method
432: if (j == length && md.isOverwrite()) {
433: oldMethods[i] = md;
434: return;
435: } else if (j == length) {
436: return;
437: }
438: }
439:
440: ESMethodDescriptor[] newMethods;
441: newMethods = new ESMethodDescriptor[oldMethods.length + 1];
442: System.arraycopy(oldMethods, 0, newMethods, 0,
443: oldMethods.length);
444: newMethods[oldMethods.length] = md;
445: overload.set(length, newMethods);
446: }
447:
448: private void addPropMap(HashMap oldMap)
449: throws IntrospectionException {
450: Iterator i = oldMap.entrySet().iterator();
451:
452: while (i.hasNext()) {
453: Map.Entry entry = (Map.Entry) i.next();
454: Object value = entry.getValue();
455:
456: if (value == BAD) {
457: // XXX: else add bad?
458: continue;
459: }
460:
461: if (value instanceof NamedPropertyDescriptor) {
462: NamedPropertyDescriptor pd = (NamedPropertyDescriptor) value;
463:
464: addNamedProp(pd.getName(), pd.namedGetter,
465: pd.namedSetter, pd.namedRemover,
466: pd.namedIterator);
467: } else if (value instanceof ESIndexedPropertyDescriptor) {
468: ESIndexedPropertyDescriptor pd = (ESIndexedPropertyDescriptor) value;
469:
470: addIndexedProp(pd.getName(), pd.getESReadMethod(), pd
471: .getESWriteMethod(), pd.getESSizeMethod());
472: } else if (value instanceof ESPropertyDescriptor) {
473: ESPropertyDescriptor pd = (ESPropertyDescriptor) value;
474:
475: addProp(pd.getName(), pd.field, pd.getter, pd.setter);
476: }
477: // XXX: else add bad?
478: }
479: }
480:
481: void addField(Field field) throws IntrospectionException {
482: int modifiers = field.getModifiers();
483: if (!Modifier.isPublic(modifiers))
484: return;
485:
486: addProp(field.getName(), field, null, null);
487: }
488:
489: void addProps(ESBeanInfo info) throws IntrospectionException {
490: addPropMap(info.propMap);
491: }
492: }
|