KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > beanutils > MappedPropertyDescriptor


1 /*
2  * Copyright 2001-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
17 package org.apache.commons.beanutils;
18
19
20 import java.beans.IntrospectionException JavaDoc;
21 import java.beans.PropertyDescriptor JavaDoc;
22 import java.lang.reflect.Method JavaDoc;
23 import java.lang.reflect.Modifier JavaDoc;
24 import java.security.AccessController JavaDoc;
25 import java.security.PrivilegedAction JavaDoc;
26
27
28 /**
29  * A MappedPropertyDescriptor describes one mapped property.
30  * Mapped properties are multivalued properties like indexed properties
31  * but that are accessed with a String key instead of an index.
32  * Such property values are typically stored in a Map collection.
33  * For this class to work properly, a mapped value must have
34  * getter and setter methods of the form
35  * <p><code>get<strong>Property</strong>(String key)<code> and
36  * <p><code>set&ltProperty&gt(String key, Object value)<code>,
37  * <p>where <code><strong>Property</strong></code> must be replaced
38  * by the name of the property.
39  * @see java.beans.PropertyDescriptor
40  *
41  * @author Rey François
42  * @author Gregor Raıman
43  * @version $Revision: 1.18.2.1 $ $Date: 2004/07/27 21:44:26 $
44  */

45
46
47 public class MappedPropertyDescriptor extends PropertyDescriptor JavaDoc {
48     // ----------------------------------------------------- Instance Variables
49

50     /**
51      * The underlying data type of the property we are describing.
52      */

53     private Class JavaDoc mappedPropertyType;
54
55     /**
56      * The reader method for this property (if any).
57      */

58     private Method JavaDoc mappedReadMethod;
59
60     /**
61      * The writer method for this property (if any).
62      */

63     private Method JavaDoc mappedWriteMethod;
64
65     /**
66      * The parameter types array for the reader method signature.
67      */

68     private static final Class JavaDoc[] stringClassArray = new Class JavaDoc[]{String JavaDoc.class};
69
70     // ----------------------------------------------------------- Constructors
71

72     /**
73      * Constructs a MappedPropertyDescriptor for a property that follows
74      * the standard Java convention by having getFoo and setFoo
75      * accessor methods, with the addition of a String parameter (the key).
76      * Thus if the argument name is "fred", it will
77      * assume that the writer method is "setFred" and the reader method
78      * is "getFred". Note that the property name should start with a lower
79      * case character, which will be capitalized in the method names.
80      *
81      * @param propertyName The programmatic name of the property.
82      * @param beanClass The Class object for the target bean. For
83      * example sun.beans.OurButton.class.
84      *
85      * @exception IntrospectionException if an exception occurs during
86      * introspection.
87      */

88     public MappedPropertyDescriptor(String JavaDoc propertyName, Class JavaDoc beanClass)
89             throws IntrospectionException JavaDoc {
90
91         super(propertyName, null, null);
92         
93         if (propertyName == null || propertyName.length() == 0) {
94             throw new IntrospectionException JavaDoc("bad property name: " +
95                     propertyName + " on class: " + beanClass.getClass().getName());
96         }
97
98         setName(propertyName);
99         String JavaDoc base = capitalizePropertyName(propertyName);
100         
101         // Look for mapped read method and matching write method
102
try {
103             mappedReadMethod = findMethod(beanClass, "get" + base, 1,
104                     stringClassArray);
105             Class JavaDoc params[] = { String JavaDoc.class, mappedReadMethod.getReturnType() };
106             mappedWriteMethod = findMethod(beanClass, "set" + base, 2, params);
107         } catch (IntrospectionException JavaDoc e) {
108             ;
109         }
110         
111         // If there's no read method, then look for just a write method
112
if (mappedReadMethod == null) {
113             mappedWriteMethod = findMethod(beanClass, "set" + base, 2);
114         }
115
116         if ((mappedReadMethod == null) && (mappedWriteMethod == null)) {
117             throw new IntrospectionException JavaDoc("Property '" + propertyName +
118                     "' not found on " +
119                     beanClass.getName());
120         }
121         
122         findMappedPropertyType();
123     }
124
125
126     /**
127      * This constructor takes the name of a mapped property, and method
128      * names for reading and writing the property.
129      *
130      * @param propertyName The programmatic name of the property.
131      * @param beanClass The Class object for the target bean. For
132      * example sun.beans.OurButton.class.
133      * @param mappedGetterName The name of the method used for
134      * reading one of the property values. May be null if the
135      * property is write-only.
136      * @param mappedSetterName The name of the method used for writing
137      * one of the property values. May be null if the property is
138      * read-only.
139      *
140      * @exception IntrospectionException if an exception occurs during
141      * introspection.
142      */

143     public MappedPropertyDescriptor(String JavaDoc propertyName, Class JavaDoc beanClass,
144                                     String JavaDoc mappedGetterName, String JavaDoc mappedSetterName)
145             throws IntrospectionException JavaDoc {
146
147         super(propertyName, null, null);
148
149         if (propertyName == null || propertyName.length() == 0) {
150             throw new IntrospectionException JavaDoc("bad property name: " +
151                     propertyName);
152         }
153         setName(propertyName);
154
155         // search the mapped get and set methods
156
mappedReadMethod =
157             findMethod(beanClass, mappedGetterName, 1, stringClassArray);
158
159         if (mappedReadMethod != null) {
160             Class JavaDoc params[] = { String JavaDoc.class, mappedReadMethod.getReturnType() };
161             mappedWriteMethod =
162                 findMethod(beanClass, mappedSetterName, 2, params);
163         } else {
164             mappedWriteMethod =
165                 findMethod(beanClass, mappedSetterName, 2);
166         }
167
168         findMappedPropertyType();
169     }
170
171     /**
172      * This constructor takes the name of a mapped property, and Method
173      * objects for reading and writing the property.
174      *
175      * @param propertyName The programmatic name of the property.
176      * @param mappedGetter The method used for reading one of
177      * the property values. May be be null if the property
178      * is write-only.
179      * @param mappedSetter The method used for writing one the
180      * property values. May be null if the property is read-only.
181      *
182      * @exception IntrospectionException if an exception occurs during
183      * introspection.
184      */

185     public MappedPropertyDescriptor(String JavaDoc propertyName,
186                                     Method JavaDoc mappedGetter, Method JavaDoc mappedSetter)
187             throws IntrospectionException JavaDoc {
188
189         super(propertyName, mappedGetter, mappedSetter);
190
191         if (propertyName == null || propertyName.length() == 0) {
192             throw new IntrospectionException JavaDoc("bad property name: " +
193                     propertyName);
194         }
195
196         setName(propertyName);
197         mappedReadMethod = mappedGetter;
198         mappedWriteMethod = mappedSetter;
199         findMappedPropertyType();
200     }
201
202     // -------------------------------------------------------- Public Methods
203

204     /**
205      * Gets the Class object for the property values.
206      *
207      * @return The Java type info for the property values. Note that
208      * the "Class" object may describe a built-in Java type such as "int".
209      * The result may be "null" if this is a mapped property that
210      * does not support non-keyed access.
211      * <p>
212      * This is the type that will be returned by the mappedReadMethod.
213      */

214     public Class JavaDoc getMappedPropertyType() {
215         return mappedPropertyType;
216     }
217
218     /**
219      * Gets the method that should be used to read one of the property value.
220      *
221      * @return The method that should be used to read the property value.
222      * May return null if the property can't be read.
223      */

224     public Method JavaDoc getMappedReadMethod() {
225         return mappedReadMethod;
226     }
227
228     /**
229      * Sets the method that should be used to read one of the property value.
230      *
231      * @param mappedGetter The new getter method.
232      */

233     public void setMappedReadMethod(Method JavaDoc mappedGetter)
234             throws IntrospectionException JavaDoc {
235         mappedReadMethod = mappedGetter;
236         findMappedPropertyType();
237     }
238
239     /**
240      * Gets the method that should be used to write one of the property value.
241      *
242      * @return The method that should be used to write one of the property value.
243      * May return null if the property can't be written.
244      */

245     public Method JavaDoc getMappedWriteMethod() {
246         return mappedWriteMethod;
247     }
248
249     /**
250      * Sets the method that should be used to write the property value.
251      *
252      * @param mappedSetter The new setter method.
253      */

254     public void setMappedWriteMethod(Method JavaDoc mappedSetter)
255             throws IntrospectionException JavaDoc {
256         mappedWriteMethod = mappedSetter;
257         findMappedPropertyType();
258     }
259
260     // ------------------------------------------------------- Private Methods
261

262     /**
263      * Introspect our bean class to identify the corresponding getter
264      * and setter methods.
265      */

266     private void findMappedPropertyType() throws IntrospectionException JavaDoc {
267         try {
268             mappedPropertyType = null;
269             if (mappedReadMethod != null) {
270                 if (mappedReadMethod.getParameterTypes().length != 1) {
271                     throw new IntrospectionException JavaDoc
272                             ("bad mapped read method arg count");
273                 }
274                 mappedPropertyType = mappedReadMethod.getReturnType();
275                 if (mappedPropertyType == Void.TYPE) {
276                     throw new IntrospectionException JavaDoc
277                             ("mapped read method " +
278                             mappedReadMethod.getName() + " returns void");
279                 }
280             }
281
282             if (mappedWriteMethod != null) {
283                 Class JavaDoc params[] = mappedWriteMethod.getParameterTypes();
284                 if (params.length != 2) {
285                     throw new IntrospectionException JavaDoc
286                             ("bad mapped write method arg count");
287                 }
288                 if (mappedPropertyType != null &&
289                         mappedPropertyType != params[1]) {
290                     throw new IntrospectionException JavaDoc
291                             ("type mismatch between mapped read and write methods");
292                 }
293                 mappedPropertyType = params[1];
294             }
295         } catch (IntrospectionException JavaDoc ex) {
296             throw ex;
297         }
298     }
299
300
301     /**
302      * Return a capitalized version of the specified property name.
303      *
304      * @param s The property name
305      */

306     private static String JavaDoc capitalizePropertyName(String JavaDoc s) {
307         if (s.length() == 0) {
308             return s;
309         }
310
311         char chars[] = s.toCharArray();
312         chars[0] = Character.toUpperCase(chars[0]);
313         return new String JavaDoc(chars);
314     }
315
316     //======================================================================
317
// Package private support methods (copied from java.beans.Introspector).
318
//======================================================================
319

320     // Cache of Class.getDeclaredMethods:
321
private static java.util.Hashtable JavaDoc
322         declaredMethodCache = new java.util.Hashtable JavaDoc();
323
324     /*
325      * Internal method to return *public* methods within a class.
326      */

327     private static synchronized Method JavaDoc[] getPublicDeclaredMethods(Class JavaDoc clz) {
328         // Looking up Class.getDeclaredMethods is relatively expensive,
329
// so we cache the results.
330
final Class JavaDoc fclz = clz;
331         Method JavaDoc[] result = (Method JavaDoc[]) declaredMethodCache.get(fclz);
332         if (result != null) {
333             return result;
334         }
335
336         // We have to raise privilege for getDeclaredMethods
337
result = (Method JavaDoc[])
338                 AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
339                     public Object JavaDoc run() {
340                         try{
341                         
342                             return fclz.getDeclaredMethods();
343                             
344                         } catch (SecurityException JavaDoc ex) {
345                             // this means we're in a limited security environment
346
// so let's try going through the public methods
347
// and null those those that are not from the declaring
348
// class
349
Method JavaDoc[] methods = fclz.getMethods();
350                             for (int i = 0, size = methods.length; i < size; i++) {
351                                 Method JavaDoc method = methods[i];
352                                 if (!(fclz.equals(method.getDeclaringClass()))) {
353                                     methods[i] = null;
354                                 }
355                             }
356                             return methods;
357                         }
358                     }
359                 });
360
361         // Null out any non-public methods.
362
for (int i = 0; i < result.length; i++) {
363             Method JavaDoc method = result[i];
364             if (method != null) {
365                 int mods = method.getModifiers();
366                 if (!Modifier.isPublic(mods)) {
367                     result[i] = null;
368                 }
369             }
370         }
371
372         // Add it to the cache.
373
declaredMethodCache.put(clz, result);
374         return result;
375     }
376
377     /**
378      * Internal support for finding a target methodName on a given class.
379      */

380     private static Method JavaDoc internalFindMethod(Class JavaDoc start, String JavaDoc methodName,
381                                              int argCount) {
382         // For overridden methods we need to find the most derived version.
383
// So we start with the given class and walk up the superclass chain.
384
for (Class JavaDoc cl = start; cl != null; cl = cl.getSuperclass()) {
385             Method JavaDoc methods[] = getPublicDeclaredMethods(cl);
386             for (int i = 0; i < methods.length; i++) {
387                 Method JavaDoc method = methods[i];
388                 if (method == null) {
389                     continue;
390                 }
391                 // skip static methods.
392
int mods = method.getModifiers();
393                 if (Modifier.isStatic(mods)) {
394                     continue;
395                 }
396                 if (method.getName().equals(methodName) &&
397                         method.getParameterTypes().length == argCount) {
398                     return method;
399                 }
400             }
401         }
402
403         // Now check any inherited interfaces. This is necessary both when
404
// the argument class is itself an interface, and when the argument
405
// class is an abstract class.
406
Class JavaDoc ifcs[] = start.getInterfaces();
407         for (int i = 0; i < ifcs.length; i++) {
408             Method JavaDoc m = internalFindMethod(ifcs[i], methodName, argCount);
409             if (m != null) {
410                 return m;
411             }
412         }
413
414         return null;
415     }
416
417     /**
418      * Internal support for finding a target methodName with a given
419      * parameter list on a given class.
420      */

421     private static Method JavaDoc internalFindMethod(Class JavaDoc start, String JavaDoc methodName,
422                                              int argCount, Class JavaDoc args[]) {
423         // For overriden methods we need to find the most derived version.
424
// So we start with the given class and walk up the superclass chain.
425
for (Class JavaDoc cl = start; cl != null; cl = cl.getSuperclass()) {
426             Method JavaDoc methods[] = getPublicDeclaredMethods(cl);
427             for (int i = 0; i < methods.length; i++) {
428                 Method JavaDoc method = methods[i];
429                 if (method == null) {
430                     continue;
431                 }
432                 // skip static methods.
433
int mods = method.getModifiers();
434                 if (Modifier.isStatic(mods)) {
435                     continue;
436                 }
437                 // make sure method signature matches.
438
Class JavaDoc params[] = method.getParameterTypes();
439                 if (method.getName().equals(methodName) &&
440                         params.length == argCount) {
441                     boolean different = false;
442                     if (argCount > 0) {
443                         for (int j = 0; j < argCount; j++) {
444                             if (params[j] != args[j]) {
445                                 different = true;
446                                 continue;
447                             }
448                         }
449                         if (different) {
450                             continue;
451                         }
452                     }
453                     return method;
454                 }
455             }
456         }
457
458         // Now check any inherited interfaces. This is necessary both when
459
// the argument class is itself an interface, and when the argument
460
// class is an abstract class.
461
Class JavaDoc ifcs[] = start.getInterfaces();
462         for (int i = 0; i < ifcs.length; i++) {
463             Method JavaDoc m = internalFindMethod(ifcs[i], methodName, argCount);
464             if (m != null) {
465                 return m;
466             }
467         }
468         
469         return null;
470     }
471
472     /**
473      * Find a target methodName on a given class.
474      */

475     static Method JavaDoc findMethod(Class JavaDoc cls, String JavaDoc methodName, int argCount)
476             throws IntrospectionException JavaDoc {
477         if (methodName == null) {
478             return null;
479         }
480
481         Method JavaDoc m = internalFindMethod(cls, methodName, argCount);
482         if (m != null) {
483             return m;
484         }
485
486         // We failed to find a suitable method
487
throw new IntrospectionException JavaDoc("No method \"" + methodName +
488                 "\" with " + argCount + " arg(s)");
489     }
490
491     /**
492      * Find a target methodName with specific parameter list on a given class.
493      */

494     static Method JavaDoc findMethod(Class JavaDoc cls, String JavaDoc methodName, int argCount,
495                              Class JavaDoc args[]) throws IntrospectionException JavaDoc {
496         if (methodName == null) {
497             return null;
498         }
499
500         Method JavaDoc m = internalFindMethod(cls, methodName, argCount, args);
501         if (m != null) {
502             return m;
503         }
504
505         // We failed to find a suitable method
506
throw new IntrospectionException JavaDoc("No method \"" + methodName +
507                 "\" with " + argCount + " arg(s) of matching types.");
508     }
509
510     /**
511      * Return true if class a is either equivalent to class b, or
512      * if class a is a subclass of class b, ie if a either "extends"
513      * or "implements" b.
514      * Note tht either or both "Class" objects may represent interfaces.
515      */

516     static boolean isSubclass(Class JavaDoc a, Class JavaDoc b) {
517         // We rely on the fact that for any given java class or
518
// primtitive type there is a unqiue Class object, so
519
// we can use object equivalence in the comparisons.
520
if (a == b) {
521             return true;
522         }
523
524         if (a == null || b == null) {
525             return false;
526         }
527
528         for (Class JavaDoc x = a; x != null; x = x.getSuperclass()) {
529             if (x == b) {
530                 return true;
531             }
532
533             if (b.isInterface()) {
534                 Class JavaDoc interfaces[] = x.getInterfaces();
535                 for (int i = 0; i < interfaces.length; i++) {
536                     if (isSubclass(interfaces[i], b)) {
537                         return true;
538                     }
539                 }
540             }
541         }
542
543         return false;
544     }
545
546
547     /**
548      * Return true iff the given method throws the given exception.
549      */

550
551     private boolean throwsException(Method JavaDoc method, Class JavaDoc exception) {
552
553         Class JavaDoc exs[] = method.getExceptionTypes();
554         for (int i = 0; i < exs.length; i++) {
555             if (exs[i] == exception) {
556                 return true;
557             }
558         }
559
560         return false;
561     }
562 }
563
Popular Tags