001: package com.xoetrope.data.pojo;
002:
003: import java.beans.BeanInfo;
004: import java.beans.Introspector;
005: import java.beans.MethodDescriptor;
006: import java.lang.reflect.Method;
007: import java.util.ArrayList;
008: import java.util.Collection;
009: import java.util.Hashtable;
010: import java.util.Iterator;
011: import java.util.List;
012: import net.xoetrope.optional.data.pojo.XPojoHelper;
013: import net.xoetrope.optional.resources.XProjectClassLoader;
014: import net.xoetrope.xui.data.XModel;
015:
016: /**
017: * <p>A POJO adapter that extends the basic pojo to support listing and querying of
018: * all possible properties.
019: * </p>
020: * <p> Copyright (c) Xoetrope Ltd., 2001-2006, This software is licensed under
021: * the GNU Public License (GPL), please see license.txt for more details. If
022: * you make commercial use of this software you must purchase a commercial
023: * license from Xoetrope.</p>
024: */
025:
026: public class XPojoAdapterEx {
027: protected Class clazz;
028: protected XPojoDataSourceEx dataSource;
029: protected XPojoProperties properties;
030: protected Hashtable getters;
031: protected Hashtable setters;
032:
033: /**
034: * Creates a new instance of <code>XPojoAdapterEx</code>
035: * @param cl the class to be adapted
036: * @param ds the data source object.
037: */
038: public XPojoAdapterEx(Class cl, XPojoDataSourceEx ds) {
039: clazz = cl;
040: dataSource = ds;
041: findAccessors();
042: findProperties();
043: }
044:
045: /**
046: * Gets the type adapted by this object.
047: * @return class object
048: */
049: public Class getAdaptedClass() {
050: return clazz;
051: }
052:
053: /**
054: * Finds and saves all properties of the adapted class.
055: */
056: protected void findProperties() {
057: properties = new XPojoProperties();
058: try {
059: BeanInfo bi = Introspector.getBeanInfo(clazz);
060: MethodDescriptor mds[] = bi.getMethodDescriptors();
061: // iterate over the methods
062: for (int i = 0; i < mds.length; i++) {
063: Method method = mds[i].getMethod();
064: // check whether the method is a getter
065: if (XPojoDataSourceEx.getterSupported(method, true)) {
066: String propertyName = getGetterPropertyName(method);
067: if ((propertyName != null)
068: && (propertyName.length() > 0))
069: properties.setPropertyGetter(propertyName,
070: method);
071: }
072: // check whether the method is a setter
073: else if (XPojoDataSourceEx
074: .setterSupported(method, true)) {
075: String propertyName = getSetterPropertyName(method);
076: if ((propertyName != null)
077: && (propertyName.length() > 0))
078: properties.setPropertySetter(propertyName,
079: method);
080: }
081: }
082: } catch (Exception ex) {
083: ex.printStackTrace();
084: }
085: }
086:
087: protected void findAccessors() {
088: getters = new Hashtable();
089: setters = new Hashtable();
090:
091: try {
092: BeanInfo bi = Introspector.getBeanInfo(clazz);
093: MethodDescriptor mds[] = bi.getMethodDescriptors();
094:
095: for (int i = 0; i < mds.length; i++) {
096: Method method = mds[i].getMethod();
097:
098: if (XPojoDataSourceEx.getterSupported(method, false)) {
099:
100: Integer sigKey = XPojoHelper.getSignatureKey(method
101: .getParameterTypes());
102: ArrayList gettersBySig = (ArrayList) getters
103: .get(sigKey);
104: if (gettersBySig == null)
105: getters.put(sigKey,
106: gettersBySig = new ArrayList());
107: gettersBySig.add(method);
108:
109: } else if (XPojoDataSourceEx.setterSupported(method,
110: false)) {
111:
112: Integer sigKey = XPojoHelper.getSignatureKey(method
113: .getParameterTypes());
114: ArrayList settersBySig = (ArrayList) setters
115: .get(sigKey);
116: if (settersBySig == null)
117: setters.put(sigKey,
118: settersBySig = new ArrayList());
119: settersBySig.add(method);
120: }
121:
122: }
123:
124: } catch (Exception ex) {
125: ex.printStackTrace();
126: }
127: }
128:
129: public String[] getGettersBySig(Class[] parameterTypes) {
130: Integer sigKey = XPojoHelper.getSignatureKey(parameterTypes);
131: ArrayList list = (ArrayList) getters.get(sigKey);
132: String[] gettersBySig = null;
133:
134: if (list != null) {
135: gettersBySig = new String[list.size()];
136: Iterator listIter = list.iterator();
137:
138: for (int i = 0; listIter.hasNext(); i++) {
139: Method getter = (Method) listIter.next();
140: gettersBySig[i] = (getter.getName() + getSignature(getter));
141: }
142: }
143: return gettersBySig;
144: }
145:
146: public String[] getSettersBySig(Class[] parameterTypes) {
147: Integer sigKey = XPojoHelper.getSignatureKey(parameterTypes);
148: ArrayList list = (ArrayList) setters.get(sigKey);
149: String[] settersBySig = null;
150:
151: if (list != null) {
152: settersBySig = new String[list.size()];
153: Iterator listIter = list.iterator();
154:
155: for (int i = 0; listIter.hasNext(); i++) {
156: Method setter = (Method) listIter.next();
157: settersBySig[i] = (setter.getName() + getSignature(setter));
158: }
159: }
160: return settersBySig;
161: }
162:
163: public String[] getSetters() {
164: String[] setterNames = new String[] { "none" };
165: Collection setterSigs = setters.values();
166: if (setterSigs != null) {
167:
168: int numSetters = 0;
169: Iterator setterSigsIter = setterSigs.iterator();
170: while (setterSigsIter.hasNext())
171: numSetters += ((List) setterSigsIter.next()).size();
172:
173: setterNames = new String[numSetters + 1];
174: setterNames[0] = "none";
175:
176: int i = 1;
177: setterSigsIter = setterSigs.iterator();
178: while (setterSigsIter.hasNext()) {
179: Iterator settersIter = ((List) setterSigsIter.next())
180: .iterator();
181: while (settersIter.hasNext()) {
182: Method setter = (Method) settersIter.next();
183: setterNames[i++] = (setter.getName() + getSignature(setter));
184: }
185: }
186: }
187: return setterNames;
188: }
189:
190: /*
191: public String[] getSetters()
192: {
193: Collection setters = this.setters.values();
194: if ( setters == null )
195: return null;
196:
197: Iterator iter = setters.iterator();
198: String[] setterNames = new String[ setters.size() ];
199: for ( int i = 0; iter.hasNext(); i++ ) {
200: Method setter = (Method)iter.next();
201: setterNames[ i ] = ( setter.getName() + getSignature( setter ));
202: }
203: return setterNames;
204: }
205: */
206:
207: /*
208: public ArrayList getGettersBySig( Class[] parameterTypes )
209: {
210: Integer sigKey = XPojoHelper.getSignatureKey( parameterTypes );
211: return (ArrayList)getters.get( sigKey );
212: }
213:
214: public ArrayList getSettersBySig( Class[] parameterTypes )
215: {
216: Integer sigKey = XPojoHelper.getSignatureKey( parameterTypes );
217: return (ArrayList)setters.get( sigKey );
218: }
219: */
220:
221: /**
222: * Determines whether specified method arguments match the passed method signature
223: * @param method the method to be queried
224: * @param methodSignature method's signature to be checked
225: * @return true if passed arguments match, false otherwise
226: */
227: private boolean argumentTypesMatch(Method method,
228: String methodSignature) {
229: if ((methodSignature == null)
230: || (methodSignature.length() == 0))
231: methodSignature = "()";
232:
233: int idx1 = methodSignature.indexOf("(");
234: int idx2 = methodSignature.indexOf(")");
235: if ((idx1 != 0) || (idx2 != methodSignature.length() - 1))
236: return false;
237:
238: methodSignature = methodSignature.substring(1, methodSignature
239: .length() - 1);
240: String[] typeNames = new String[0];
241:
242: if (methodSignature.length() > 0)
243: typeNames = methodSignature.split(",");
244: Class[] typesClasses = method.getParameterTypes();
245: if (typeNames.length != typesClasses.length)
246: return false;
247:
248: boolean match = true;
249: for (int i = 0; (i < typeNames.length) && match; i++) {
250: String typeName = typesClasses[i].getName();
251: typeName = typeName
252: .substring(typeName.lastIndexOf(".") + 1);
253: match = typeName.equals(typeNames[i]);
254: }
255: return match;
256: }
257:
258: /**
259: * Retrieves a method (by reflection) described by the passed arguments
260: * @param wanted method's name
261: * @param wanted mathod's signature
262: * @return method matching specifed signature or null if the method
263: * was not found
264: */
265: private Method getMethod(String methodName, String methodSignature) {
266: Method method = null;
267: Method[] methods = clazz.getMethods();
268: // check whether the method exists
269: for (int i = 0; (i < methods.length) && (method == null); i++) {
270: if (!methods[i].getName().equals(methodName))
271: continue;
272: if (!argumentTypesMatch(methods[i], methodSignature))
273: continue;
274: method = methods[i];
275: }
276: return method;
277: }
278:
279: /**
280: * Gets the method described by the passed String.
281: * @param String describing the method
282: * @return requested method
283: */
284: protected Method getMethod(String method) {
285: String methodName = getMethodName(method);
286: String methodSig = getSignature(method);
287: return getMethod(methodName, methodSig);
288: }
289:
290: /**
291: * Loads and returns the specified class.
292: * @param className the name of the class to be loaded
293: * @return requested class or Object class if the s
294: * specified class could not been loaded
295: */
296: private Class getClass(String className) {
297: XProjectClassLoader classLoader = dataSource
298: .getProjectClassLoader();
299: // try to load the specified class
300: Class clazz = null;
301: if (className != null) {
302: try {
303: if (classLoader != null)
304: clazz = Class.forName(className, true, classLoader);
305: else
306: clazz = Class.forName(className);
307: } catch (Exception ex) {
308: clazz = null;
309: }
310: }
311: return (clazz != null ? clazz : Object.class);
312: }
313:
314: /**
315: * Marks that the specified collection is intended to store the elements
316: * of the specified type
317: * @param propertyName the name of the property representing the collection
318: * @param className the type of the collection elements
319: */
320: protected void addCollectionType(String propertyName,
321: String className) {
322: properties.setCollectionType(propertyName, getClass(className));
323: }
324:
325: /**
326: * Marks that the specified transient collection is intended to store elements
327: * of the specified type.
328: * @propertyName the name of the transient property representing the collection
329: * @param className the type of the collection elements.
330: */
331: protected void addTransientCollectionType(String propertyName,
332: String className) {
333: properties.setTransientCollectionType(propertyName,
334: getClass(className));
335: }
336:
337: /**
338: * Adds the transient property of the POJO
339: * @param propertyName the name of the transient property
340: * @param className the name of the class whose instance will be "stored" by
341: * this property
342: */
343: protected void addTransientProperty(String propertyName,
344: String className) {
345: properties.setTransientPropertyType(propertyName,
346: getClass(className));
347: }
348:
349: /**
350: * Adapt a property by replacing the getter and setter names
351: * determined by reflection
352: * @param propName the property being customized
353: * @param propGetter the getter method
354: * @param propSetter the setter method
355: * @todo customize the specified setter
356: */
357: protected void customizePojoProperty(String propertyName,
358: String propGetter, String propSetter) {
359: // getter
360: String methodName = getMethodName(propGetter);
361: if (methodName != null) {
362: String signature = getSignature(propGetter);
363: // try to obtain the specified method
364: Method method = getMethod(methodName, signature);
365: if (method != null)
366: properties.setPropertyGetter(propertyName + signature,
367: method);
368: }
369:
370: // setter
371: methodName = getMethodName(propSetter);
372: if (methodName != null) {
373: String signature = getSignature(propGetter);
374: // try to obtain the specified method
375: Method method = getMethod(methodName, signature);
376: if (method != null)
377: properties.setPropertySetter(propertyName + signature,
378: method);
379: }
380: }
381:
382: /**
383: * Gets the method signature
384: * @param method the name of the method + signature
385: * @return signature
386: */
387: protected static String getSignature(String method) {
388: String sig = "";
389: if (method != null) {
390: int idx1 = method.indexOf("(");
391: int idx2 = method.indexOf(")");
392: if ((idx1 >= 0) && (idx2 >= 0) && (idx1 < idx2 - 1))
393: sig = method.substring(idx1, idx2 + 1);
394: }
395: return sig;
396: }
397:
398: /**
399: * Gets the String describing the signature of the
400: * specified method.
401: * @param method the method whose signature is to be
402: * obtained
403: * @return the method signature
404: */
405: protected static String getSignature(Method method) {
406: String sig = "(";
407: Class[] types = method.getParameterTypes();
408: for (int i = 0; i < types.length; i++)
409: sig += (types[i].getSimpleName() + ", ");
410: if (types.length > 0)
411: sig = sig.substring(0, sig.length() - 2);
412: sig += ")";
413: return sig;
414: }
415:
416: /**
417: * Gets the method name
418: * @param method the name of the method + signature
419: * @return signature
420: */
421: private static String getMethodName(String method) {
422: if (method != null) {
423: int idx1 = method.indexOf("(");
424: int idx2 = method.indexOf(")");
425: if ((idx1 >= 0) && (idx2 >= 0) && (idx1 < idx2))
426: method = method.substring(0, idx1);
427: }
428: return method;
429: }
430:
431: /**
432: * Retrieves the property name from the specified getter method.
433: * The returned name will appear in the data visualiser tree.
434: * @param method the Method from which the property name
435: * is to be retrieved
436: * @return the property name
437: */
438: private static String getGetterPropertyName(Method method) {
439: String methodName = method.getName();
440: int idx = 0;
441: if (methodName.startsWith("get"))
442: idx = 3;
443: else if (methodName.startsWith("find"))
444: idx = 4;
445: else if (methodName.startsWith("is"))
446: idx = 2;
447: String propertyName = methodName.substring(idx);
448: propertyName = propertyName.substring(0, 1).toLowerCase()
449: + propertyName.substring(1);
450:
451: // if the method takes arguments then add them
452: Class[] argumentTypes = method.getParameterTypes();
453: if (argumentTypes.length > 0) {
454: propertyName += "(";
455: for (int i = 0; i < argumentTypes.length; i++) {
456: String argTypeName = argumentTypes[i].getName();
457: propertyName += argTypeName.substring(argTypeName
458: .lastIndexOf(".") + 1);
459: propertyName += ",";
460: }
461: propertyName = propertyName.substring(0, propertyName
462: .length() - 1);
463: propertyName += ")";
464: }
465:
466: return propertyName;
467: }
468:
469: /**
470: * Retrieves the property name from the specified setter method
471: * @param method the method from which the property name is to
472: * retrieved
473: * @return the name of the property
474: */
475: private static String getSetterPropertyName(Method method) {
476: String methodName = method.getName();
477: if (!methodName.startsWith("set"))
478: return null;
479: String propertyName = methodName.substring(3);
480: propertyName = propertyName.substring(0, 1).toLowerCase()
481: + propertyName.substring(1);
482: return propertyName;
483: }
484:
485: /**
486: * Gets a model node wrapping property value of the passed object.
487: * @param obj the object whose property is to be retrieved
488: * @param propertyName the name of the property
489: * @param parentNode the parent model node for the returned object
490: * @return XModel instance wrapping property value of the <code>obj</code>
491: */
492: public XModel getProperty(String propertyName, XModel parentNode) {
493: XModel property = null;
494: // look for the pojo property
495: Method getter = properties.getPropertyGetter(propertyName);
496: Method setter = properties.getPropertySetter(propertyName);
497: if (getter != null)
498: property = dataSource.adaptPojo(getter, setter,
499: propertyName, parentNode);
500:
501: if (property == null) {
502: Class propertyClass = properties
503: .getTransientPropertyType(propertyName);
504: if (propertyClass != null)
505: property = dataSource.adaptPojo(propertyClass,
506: propertyName, parentNode);
507: }
508:
509: return property;
510: }
511:
512: /**
513: * Gets the properties of the Class being
514: * adapted by this object.
515: */
516: protected XPojoProperties getProperties() {
517: return properties;
518: }
519:
520: /**
521: * Gets the <code>XPojoDataSourceEx</code> object used to construct
522: * POJO's model.
523: * @return the <code>XPojoDataSourceEx</code> object.
524: */
525: public XPojoDataSourceEx getPojoDataSourceEx() {
526: return (XPojoDataSourceEx) dataSource;
527: }
528:
529: /**
530: * Gets the number of properties
531: * @return number of properties
532: */
533: public int getNumChildren() {
534: return (properties.getNumProperties());
535: }
536:
537: }
|