KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > opensymphony > workflow > designer > beanutils > MappedPropertyDescriptor


1 /*
2  * $Header: /cvs/osworkflow/src/designer/com/opensymphony/workflow/designer/beanutils/MappedPropertyDescriptor.java,v 1.1 2003/12/06 18:05:58 hani Exp $
3  * $Revision: 1.1 $
4  * $Date: 2003/12/06 18:05:58 $
5  *
6  * ====================================================================
7  *
8  * The Apache Software License, Version 1.1
9  *
10  * Copyright (c) 2001-2003 The Apache Software Foundation. All rights
11  * reserved.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  *
17  * 1. Redistributions of source code must retain the above copyright
18  * notice, this list of conditions and the following disclaimer.
19  *
20  * 2. Redistributions in binary form must reproduce the above copyright
21  * notice, this list of conditions and the following disclaimer in
22  * the documentation and/or other materials provided with the
23  * distribution.
24  *
25  * 3. The end-user documentation included with the redistribution,
26  * if any, must include the following acknowledgement:
27  * "This product includes software developed by the
28  * Apache Software Foundation (http://www.apache.org/)."
29  * Alternately, this acknowledgement may appear in the software itself,
30  * if and wherever such third-party acknowledgements normally appear.
31  *
32  * 4. The names "Apache", "The Jakarta Project", "Commons", and "Apache Software
33  * Foundation" must not be used to endorse or promote products derived
34  * from this software without prior written permission. For written
35  * permission, please contact apache@apache.org.
36  *
37  * 5. Products derived from this software may not be called "Apache",
38  * "Apache" nor may "Apache" appear in their names without prior
39  * written permission of the Apache Software Foundation.
40  *
41  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
42  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
43  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
44  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
45  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
46  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
47  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
48  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
49  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
50  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
51  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52  * SUCH DAMAGE.
53  * ====================================================================
54  *
55  * This software consists of voluntary contributions made by many
56  * individuals on behalf of the Apache Software Foundation. For more
57  * information on the Apache Software Foundation, please see
58  * <http://www.apache.org/>.
59  *
60  */

61
62 package com.opensymphony.workflow.designer.beanutils;
63
64 import java.beans.IntrospectionException JavaDoc;
65 import java.beans.PropertyDescriptor JavaDoc;
66 import java.lang.reflect.Method JavaDoc;
67 import java.lang.reflect.Modifier JavaDoc;
68 import java.security.AccessController JavaDoc;
69 import java.security.PrivilegedAction JavaDoc;
70
71 /**
72  * A MappedPropertyDescriptor describes one mapped property.
73  * Mapped properties are multivalued properties like indexed properties
74  * but that are accessed with a String key instead of an index.
75  * Such property values are typically stored in a Map collection.
76  * For this class to work properly, a mapped value must have
77  * getter and setter methods of the form
78  * <p><code>get<strong>Property</strong>(String key)<code> and
79  * <p><code>set&ltProperty&gt(String key, Object value)<code>,
80  * <p>where <code><strong>Property</strong></code> must be replaced
81  * by the name of the property.
82  *
83  * @author Rey François
84  * @author Gregor Raıman
85  * @version $Revision: 1.1 $ $Date: 2003/12/06 18:05:58 $
86  * @see java.beans.PropertyDescriptor
87  */

88
89 public class MappedPropertyDescriptor extends PropertyDescriptor JavaDoc
90 {
91   // ----------------------------------------------------- Instance Variables
92

93   /** The underlying data type of the property we are describing. */
94   private Class JavaDoc mappedPropertyType;
95
96   /** The reader method for this property (if any). */
97   private Method JavaDoc mappedReadMethod;
98
99   /** The writer method for this property (if any). */
100   private Method JavaDoc mappedWriteMethod;
101
102   /** The parameter types array for the reader method signature. */
103   private static final Class JavaDoc[] stringClassArray = new Class JavaDoc[]{String JavaDoc.class};
104
105   // ----------------------------------------------------------- Constructors
106

107   /**
108    * Constructs a MappedPropertyDescriptor for a property that follows
109    * the standard Java convention by having getFoo and setFoo
110    * accessor methods, with the addition of a String parameter (the key).
111    * Thus if the argument name is "fred", it will
112    * assume that the writer method is "setFred" and the reader method
113    * is "getFred". Note that the property name should start with a lower
114    * case character, which will be capitalized in the method names.
115    *
116    * @param propertyName The programmatic name of the property.
117    * @param beanClass The Class object for the target bean. For
118    * example sun.beans.OurButton.class.
119    * @throws IntrospectionException if an exception occurs during
120    * introspection.
121    */

122   public MappedPropertyDescriptor(String JavaDoc propertyName, Class JavaDoc beanClass) throws IntrospectionException JavaDoc
123   {
124
125     super(propertyName, null, null);
126
127     if(propertyName == null || propertyName.length() == 0)
128     {
129       throw new IntrospectionException JavaDoc("bad property name: " + propertyName + " on class: " + beanClass.getClass().getName());
130     }
131
132     setName(propertyName);
133     String JavaDoc base = capitalizePropertyName(propertyName);
134
135     // Look for mapped read method and matching write method
136
try
137     {
138       mappedReadMethod = findMethod(beanClass, "get" + base, 1, stringClassArray);
139       Class JavaDoc params[] = {String JavaDoc.class, mappedReadMethod.getReturnType()};
140       mappedWriteMethod = findMethod(beanClass, "set" + base, 2, params);
141     }
142     catch(IntrospectionException JavaDoc e)
143     {
144       ;
145     }
146
147     // If there's no read method, then look for just a write method
148
if(mappedReadMethod == null)
149     {
150       mappedWriteMethod = findMethod(beanClass, "set" + base, 2);
151     }
152
153     if((mappedReadMethod == null) && (mappedWriteMethod == null))
154     {
155       throw new IntrospectionException JavaDoc("Property '" + propertyName + "' not found on " + beanClass.getName());
156     }
157
158     findMappedPropertyType();
159   }
160
161   /**
162    * This constructor takes the name of a mapped property, and method
163    * names for reading and writing the property.
164    *
165    * @param propertyName The programmatic name of the property.
166    * @param beanClass The Class object for the target bean. For
167    * example sun.beans.OurButton.class.
168    * @param mappedGetterName The name of the method used for
169    * reading one of the property values. May be null if the
170    * property is write-only.
171    * @param mappedSetterName The name of the method used for writing
172    * one of the property values. May be null if the property is
173    * read-only.
174    * @throws IntrospectionException if an exception occurs during
175    * introspection.
176    */

177   public MappedPropertyDescriptor(String JavaDoc propertyName, Class JavaDoc beanClass, String JavaDoc mappedGetterName, String JavaDoc mappedSetterName) throws IntrospectionException JavaDoc
178   {
179
180     super(propertyName, null, null);
181
182     if(propertyName == null || propertyName.length() == 0)
183     {
184       throw new IntrospectionException JavaDoc("bad property name: " + propertyName);
185     }
186     setName(propertyName);
187
188     // search the mapped get and set methods
189
mappedReadMethod = findMethod(beanClass, mappedGetterName, 1, stringClassArray);
190
191     if(mappedReadMethod != null)
192     {
193       Class JavaDoc params[] = {String JavaDoc.class, mappedReadMethod.getReturnType()};
194       mappedWriteMethod = findMethod(beanClass, mappedSetterName, 2, params);
195     }
196     else
197     {
198       mappedWriteMethod = findMethod(beanClass, mappedSetterName, 2);
199     }
200
201     findMappedPropertyType();
202   }
203
204   /**
205    * This constructor takes the name of a mapped property, and Method
206    * objects for reading and writing the property.
207    *
208    * @param propertyName The programmatic name of the property.
209    * @param mappedGetter The method used for reading one of
210    * the property values. May be be null if the property
211    * is write-only.
212    * @param mappedSetter The method used for writing one the
213    * property values. May be null if the property is read-only.
214    * @throws IntrospectionException if an exception occurs during
215    * introspection.
216    */

217   public MappedPropertyDescriptor(String JavaDoc propertyName, Method JavaDoc mappedGetter, Method JavaDoc mappedSetter) throws IntrospectionException JavaDoc
218   {
219
220     super(propertyName, mappedGetter, mappedSetter);
221
222     if(propertyName == null || propertyName.length() == 0)
223     {
224       throw new IntrospectionException JavaDoc("bad property name: " + propertyName);
225     }
226
227     setName(propertyName);
228     mappedReadMethod = mappedGetter;
229     mappedWriteMethod = mappedSetter;
230     findMappedPropertyType();
231   }
232
233   // -------------------------------------------------------- Public Methods
234

235   /**
236    * Gets the Class object for the property values.
237    *
238    * @return The Java type info for the property values. Note that
239    * the "Class" object may describe a built-in Java type such as "int".
240    * The result may be "null" if this is a mapped property that
241    * does not support non-keyed access.
242    * <p/>
243    * This is the type that will be returned by the mappedReadMethod.
244    */

245   public Class JavaDoc getMappedPropertyType()
246   {
247     return mappedPropertyType;
248   }
249
250   /**
251    * Gets the method that should be used to read one of the property value.
252    *
253    * @return The method that should be used to read the property value.
254    * May return null if the property can't be read.
255    */

256   public Method JavaDoc getMappedReadMethod()
257   {
258     return mappedReadMethod;
259   }
260
261   /**
262    * Sets the method that should be used to read one of the property value.
263    *
264    * @param mappedGetter The new getter method.
265    */

266   public void setMappedReadMethod(Method JavaDoc mappedGetter) throws IntrospectionException JavaDoc
267   {
268     mappedReadMethod = mappedGetter;
269     findMappedPropertyType();
270   }
271
272   /**
273    * Gets the method that should be used to write one of the property value.
274    *
275    * @return The method that should be used to write one of the property value.
276    * May return null if the property can't be written.
277    */

278   public Method JavaDoc getMappedWriteMethod()
279   {
280     return mappedWriteMethod;
281   }
282
283   /**
284    * Sets the method that should be used to write the property value.
285    *
286    * @param mappedSetter The new setter method.
287    */

288   public void setMappedWriteMethod(Method JavaDoc mappedSetter) throws IntrospectionException JavaDoc
289   {
290     mappedWriteMethod = mappedSetter;
291     findMappedPropertyType();
292   }
293
294   // ------------------------------------------------------- Private Methods
295

296   /**
297    * Introspect our bean class to identify the corresponding getter
298    * and setter methods.
299    */

300   private void findMappedPropertyType() throws IntrospectionException JavaDoc
301   {
302     try
303     {
304       mappedPropertyType = null;
305       if(mappedReadMethod != null)
306       {
307         if(mappedReadMethod.getParameterTypes().length != 1)
308         {
309           throw new IntrospectionException JavaDoc("bad mapped read method arg count");
310         }
311         mappedPropertyType = mappedReadMethod.getReturnType();
312         if(mappedPropertyType == Void.TYPE)
313         {
314           throw new IntrospectionException JavaDoc("mapped read method " + mappedReadMethod.getName() + " returns void");
315         }
316       }
317
318       if(mappedWriteMethod != null)
319       {
320         Class JavaDoc params[] = mappedWriteMethod.getParameterTypes();
321         if(params.length != 2)
322         {
323           throw new IntrospectionException JavaDoc("bad mapped write method arg count");
324         }
325         if(mappedPropertyType != null && mappedPropertyType != params[1])
326         {
327           throw new IntrospectionException JavaDoc("type mismatch between mapped read and write methods");
328         }
329         mappedPropertyType = params[1];
330       }
331     }
332     catch(IntrospectionException JavaDoc ex)
333     {
334       throw ex;
335     }
336   }
337
338   /**
339    * Return a capitalized version of the specified property name.
340    *
341    * @param s The property name
342    */

343   private static String JavaDoc capitalizePropertyName(String JavaDoc s)
344   {
345     if(s.length() == 0)
346     {
347       return s;
348     }
349
350     char chars[] = s.toCharArray();
351     chars[0] = Character.toUpperCase(chars[0]);
352     return new String JavaDoc(chars);
353   }
354
355   //======================================================================
356
// Package private support methods (copied from java.beans.Introspector).
357
//======================================================================
358

359   // Cache of Class.getDeclaredMethods:
360
private static java.util.Hashtable JavaDoc declaredMethodCache = new java.util.Hashtable JavaDoc();
361
362   /*
363    * Internal method to return *public* methods within a class.
364    */

365   private static synchronized Method JavaDoc[] getPublicDeclaredMethods(Class JavaDoc clz)
366   {
367     // Looking up Class.getDeclaredMethods is relatively expensive,
368
// so we cache the results.
369
final Class JavaDoc fclz = clz;
370     Method JavaDoc[] result = (Method JavaDoc[])declaredMethodCache.get(fclz);
371     if(result != null)
372     {
373       return result;
374     }
375
376     // We have to raise privilege for getDeclaredMethods
377
result = (Method JavaDoc[])AccessController.doPrivileged(new PrivilegedAction JavaDoc()
378     {
379       public Object JavaDoc run()
380       {
381         return fclz.getDeclaredMethods();
382       }
383     });
384
385     // Null out any non-public methods.
386
for(int i = 0; i < result.length; i++)
387     {
388       Method JavaDoc method = result[i];
389       int mods = method.getModifiers();
390       if(!Modifier.isPublic(mods))
391       {
392         result[i] = null;
393       }
394     }
395
396     // Add it to the cache.
397
declaredMethodCache.put(clz, result);
398     return result;
399   }
400
401   /**
402    * Internal support for finding a target methodName on a given class.
403    */

404   private static Method JavaDoc internalFindMethod(Class JavaDoc start, String JavaDoc methodName, int argCount)
405   {
406     // For overridden methods we need to find the most derived version.
407
// So we start with the given class and walk up the superclass chain.
408
for(Class JavaDoc cl = start; cl != null; cl = cl.getSuperclass())
409     {
410       Method JavaDoc methods[] = getPublicDeclaredMethods(cl);
411       for(int i = 0; i < methods.length; i++)
412       {
413         Method JavaDoc method = methods[i];
414         if(method == null)
415         {
416           continue;
417         }
418         // skip static methods.
419
int mods = method.getModifiers();
420         if(Modifier.isStatic(mods))
421         {
422           continue;
423         }
424         if(method.getName().equals(methodName) && method.getParameterTypes().length == argCount)
425         {
426           return method;
427         }
428       }
429     }
430
431     // Now check any inherited interfaces. This is necessary both when
432
// the argument class is itself an interface, and when the argument
433
// class is an abstract class.
434
Class JavaDoc ifcs[] = start.getInterfaces();
435     for(int i = 0; i < ifcs.length; i++)
436     {
437       Method JavaDoc m = internalFindMethod(ifcs[i], methodName, argCount);
438       if(m != null)
439       {
440         return m;
441       }
442     }
443
444     return null;
445   }
446
447   /**
448    * Internal support for finding a target methodName with a given
449    * parameter list on a given class.
450    */

451   private static Method JavaDoc internalFindMethod(Class JavaDoc start, String JavaDoc methodName, int argCount, Class JavaDoc args[])
452   {
453     // For overriden methods we need to find the most derived version.
454
// So we start with the given class and walk up the superclass chain.
455
for(Class JavaDoc cl = start; cl != null; cl = cl.getSuperclass())
456     {
457       Method JavaDoc methods[] = getPublicDeclaredMethods(cl);
458       for(int i = 0; i < methods.length; i++)
459       {
460         Method JavaDoc method = methods[i];
461         if(method == null)
462         {
463           continue;
464         }
465         // skip static methods.
466
int mods = method.getModifiers();
467         if(Modifier.isStatic(mods))
468         {
469           continue;
470         }
471         // make sure method signature matches.
472
Class JavaDoc params[] = method.getParameterTypes();
473         if(method.getName().equals(methodName) && params.length == argCount)
474         {
475           boolean different = false;
476           if(argCount > 0)
477           {
478             for(int j = 0; j < argCount; j++)
479             {
480               if(params[j] != args[j])
481               {
482                 different = true;
483                 continue;
484               }
485             }
486             if(different)
487             {
488               continue;
489             }
490           }
491           return method;
492         }
493       }
494     }
495
496     // Now check any inherited interfaces. This is necessary both when
497
// the argument class is itself an interface, and when the argument
498
// class is an abstract class.
499
Class JavaDoc ifcs[] = start.getInterfaces();
500     for(int i = 0; i < ifcs.length; i++)
501     {
502       Method JavaDoc m = internalFindMethod(ifcs[i], methodName, argCount);
503       if(m != null)
504       {
505         return m;
506       }
507     }
508
509     return null;
510   }
511
512   /**
513    * Find a target methodName on a given class.
514    */

515   static Method JavaDoc findMethod(Class JavaDoc cls, String JavaDoc methodName, int argCount) throws IntrospectionException JavaDoc
516   {
517     if(methodName == null)
518     {
519       return null;
520     }
521
522     Method JavaDoc m = internalFindMethod(cls, methodName, argCount);
523     if(m != null)
524     {
525       return m;
526     }
527
528     // We failed to find a suitable method
529
throw new IntrospectionException JavaDoc("No method \"" + methodName + "\" with " + argCount + " arg(s)");
530   }
531
532   /**
533    * Find a target methodName with specific parameter list on a given class.
534    */

535   static Method JavaDoc findMethod(Class JavaDoc cls, String JavaDoc methodName, int argCount, Class JavaDoc args[]) throws IntrospectionException JavaDoc
536   {
537     if(methodName == null)
538     {
539       return null;
540     }
541
542     Method JavaDoc m = internalFindMethod(cls, methodName, argCount, args);
543     if(m != null)
544     {
545       return m;
546     }
547
548     // We failed to find a suitable method
549
throw new IntrospectionException JavaDoc("No method \"" + methodName + "\" with " + argCount + " arg(s) of matching types.");
550   }
551
552   /**
553    * Return true if class a is either equivalent to class b, or
554    * if class a is a subclass of class b, ie if a either "extends"
555    * or "implements" b.
556    * Note tht either or both "Class" objects may represent interfaces.
557    */

558   static boolean isSubclass(Class JavaDoc a, Class JavaDoc b)
559   {
560     // We rely on the fact that for any given java class or
561
// primtitive type there is a unqiue Class object, so
562
// we can use object equivalence in the comparisons.
563
if(a == b)
564     {
565       return true;
566     }
567
568     if(a == null || b == null)
569     {
570       return false;
571     }
572
573     for(Class JavaDoc x = a; x != null; x = x.getSuperclass())
574     {
575       if(x == b)
576       {
577         return true;
578       }
579
580       if(b.isInterface())
581       {
582         Class JavaDoc interfaces[] = x.getInterfaces();
583         for(int i = 0; i < interfaces.length; i++)
584         {
585           if(isSubclass(interfaces[i], b))
586           {
587             return true;
588           }
589         }
590       }
591     }
592
593     return false;
594   }
595 }
596
Popular Tags