KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > bsf > util > ReflectionUtils


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

55
56 package org.apache.bsf.util;
57
58 import java.util.*;
59 import java.io.*;
60 import java.beans.*;
61 import java.lang.reflect.*;
62
63 import org.apache.bsf.util.event.*;
64 import org.apache.bsf.util.type.*;
65
66 /**
67  * This file is a collection of reflection utilities. There are utilities
68  * for creating beans, getting bean infos, setting/getting properties,
69  * and binding events.
70  *
71  * @author Sanjiva Weerawarana
72  * @author Joseph Kesselman
73  */

74 public class ReflectionUtils {
75
76   //////////////////////////////////////////////////////////////////////////
77

78   /**
79    * Add an event processor as a listener to some event coming out of an
80    * object.
81    *
82    * @param source event source
83    * @param eventSetName name of event set from event src to bind to
84    * @param processor event processor the event should be delegated to
85    * when it occurs; either via processEvent or
86    * processExceptionableEvent.
87    *
88    * @exception IntrospectionException if unable to introspect
89    * @exception IllegalArgumentException if event set is unknown
90    * @exception IllegalAccessException if the event adapter class or
91    * initializer is not accessible.
92    * @exception InstantiationException if event adapter instantiation fails
93    * @exception InvocationTargetException if something goes wrong while
94    * running add event listener method
95    */

96   public static void addEventListener (Object JavaDoc source, String JavaDoc eventSetName,
97                                        EventProcessor processor)
98        throws IntrospectionException, IllegalArgumentException JavaDoc,
99               IllegalAccessException JavaDoc, InstantiationException JavaDoc,
100               InvocationTargetException {
101     // find the event set descriptor for this event
102
BeanInfo bi = Introspector.getBeanInfo (source.getClass ());
103     EventSetDescriptor esd = (EventSetDescriptor)
104       findFeatureByName ("event", eventSetName, bi.getEventSetDescriptors ());
105     if (esd == null) {
106       throw new IllegalArgumentException JavaDoc ("event set '" + eventSetName +
107                                           "' unknown for source type '" +
108                                           source.getClass () + "'");
109     }
110
111     // get the class object for the event
112
Class JavaDoc listenerType = esd.getListenerType ();
113
114     // find an event adapter class of the right type
115
Class JavaDoc adapterClass = EventAdapterRegistry.lookup (listenerType);
116     if (adapterClass == null) {
117       throw new IllegalArgumentException JavaDoc ("event adapter for listner type " +
118                                           "'" + listenerType + "' (eventset " +
119                                           "'" + eventSetName + "') unknown");
120     }
121
122     // create the event adapter and give it the event processor
123
EventAdapter adapter = (EventAdapter) adapterClass.newInstance ();
124     adapter.setEventProcessor (processor);
125
126     // bind the adapter to the source bean
127
Method addListenerMethod;
128     Object JavaDoc[] args;
129     if (eventSetName.equals ("propertyChange") ||
130         eventSetName.equals ("vetoableChange")) {
131       // In Java 1.2, beans may have direct listener adding methods
132
// for property and vetoable change events which take the
133
// property name as a filter to be applied at the event source.
134
// The filter property of the event processor should be used
135
// in this case to support the source-side filtering.
136
//
137
// ** TBD **: the following two lines need to change appropriately
138
addListenerMethod = esd.getAddListenerMethod ();
139       args = new Object JavaDoc[] {adapter};
140     } else {
141       addListenerMethod = esd.getAddListenerMethod ();
142       args = new Object JavaDoc[] {adapter};
143     }
144     addListenerMethod.invoke (source, args);
145   }
146   //////////////////////////////////////////////////////////////////////////
147

148   /**
149    * Create a bean using given class loader and using the appropriate
150    * constructor for the given args of the given arg types.
151
152    * @param cl the class loader to use. If null, Class.forName is used.
153    * @param className name of class to instantiate
154    * @param argTypes array of argument types
155    * @param args array of arguments
156    *
157    * @return the newly created bean
158    *
159    * @exception ClassNotFoundException if class is not loaded
160    * @exception NoSuchMethodException if constructor can't be found
161    * @exception InstantiationException if class can't be instantiated
162    * @exception IllegalAccessException if class is not accessible
163    * @exception IllegalArgumentException if argument problem
164    * @exception InvocationTargetException if constructor excepted
165    * @exception IOException if I/O error in beans.instantiate
166    */

167   public static Bean createBean (ClassLoader JavaDoc cld, String JavaDoc className,
168                                  Class JavaDoc[] argTypes, Object JavaDoc[] args)
169        throws ClassNotFoundException JavaDoc, NoSuchMethodException JavaDoc,
170               InstantiationException JavaDoc, IllegalAccessException JavaDoc,
171               IllegalArgumentException JavaDoc, InvocationTargetException,
172               IOException {
173     if (argTypes != null) {
174       // find the right constructor and use that to create bean
175
Class JavaDoc cl = (cld != null) ? cld.loadClass (className)
176                                : Class.forName (className);
177       Constructor c = MethodUtils.getConstructor (cl, argTypes);
178       return new Bean (cl, c.newInstance (args));
179     } else {
180       // create the bean with no args constructor
181
Object JavaDoc obj = Beans.instantiate (cld, className);
182       return new Bean (obj.getClass (), obj);
183     }
184   }
185   //////////////////////////////////////////////////////////////////////////
186

187   /**
188    * Create a bean using given class loader and using the appropriate
189    * constructor for the given args. Figures out the arg types and
190    * calls above.
191
192    * @param cl the class loader to use. If null, Class.forName is used.
193    * @param className name of class to instantiate
194    * @param args array of arguments
195    *
196    * @return the newly created bean
197    *
198    * @exception ClassNotFoundException if class is not loaded
199    * @exception NoSuchMethodException if constructor can't be found
200    * @exception InstantiationException if class can't be instantiated
201    * @exception IllegalAccessException if class is not accessible
202    * @exception IllegalArgumentException if argument problem
203    * @exception InvocationTargetException if constructor excepted
204    * @exception IOException if I/O error in beans.instantiate
205    */

206   public static Bean createBean (ClassLoader JavaDoc cld, String JavaDoc className,
207                                  Object JavaDoc[] args)
208        throws ClassNotFoundException JavaDoc, NoSuchMethodException JavaDoc,
209               InstantiationException JavaDoc, IllegalAccessException JavaDoc,
210               IllegalArgumentException JavaDoc, InvocationTargetException,
211               IOException {
212     Class JavaDoc[] argTypes = null;
213     if (args != null) {
214       argTypes = new Class JavaDoc[args.length];
215       for (int i = 0; i < args.length; i++) {
216         argTypes[i] = (args[i] != null) ? args[i].getClass () : null;
217       }
218     }
219     return createBean (cld, className, argTypes, args);
220   }
221   //////////////////////////////////////////////////////////////////////////
222

223   /**
224    * locate the item in the fds array whose name is as given. returns
225    * null if not found.
226    */

227   private static
228   FeatureDescriptor findFeatureByName (String JavaDoc featureType, String JavaDoc name,
229                                        FeatureDescriptor[] fds) {
230     for (int i = 0; i < fds.length; i++) {
231       if (name.equals (fds[i].getName())) {
232         return fds[i];
233       }
234     }
235     return null;
236   }
237   public static Bean getField (Object JavaDoc target, String JavaDoc fieldName)
238       throws IllegalArgumentException JavaDoc, IllegalAccessException JavaDoc {
239     // This is to handle how we do static fields.
240
Class JavaDoc targetClass = (target instanceof Class JavaDoc)
241                         ? (Class JavaDoc) target
242                         : target.getClass ();
243
244     try {
245       Field f = targetClass.getField (fieldName);
246       Class JavaDoc fieldType = f.getType ();
247   
248       // Get the value and return it.
249
Object JavaDoc value = f.get (target);
250       return new Bean (fieldType, value);
251     } catch (NoSuchFieldException JavaDoc e) {
252       throw new IllegalArgumentException JavaDoc ("field '" + fieldName + "' is " +
253                                           "unknown for '" + target + "'");
254     }
255   }
256   //////////////////////////////////////////////////////////////////////////
257

258   /**
259    * Get a property of a bean.
260    *
261    * @param target the object whose prop is to be gotten
262    * @param propName name of the property to set
263    * @param index index to get (if property is indexed)
264    *
265    * @exception IntrospectionException if unable to introspect
266    * @exception IllegalArgumentException if problems with args: if the
267    * property is unknown, or if the property is given an index
268    * when its not, or if the property is not writeable, or if
269    * the given value cannot be assigned to the it (type mismatch).
270    * @exception IllegalAccessException if read method is not accessible
271    * @exception InvocationTargetException if read method excepts
272    */

273   public static Bean getProperty (Object JavaDoc target, String JavaDoc propName,
274                                   Integer JavaDoc index)
275        throws IntrospectionException, IllegalArgumentException JavaDoc,
276               IllegalAccessException JavaDoc, InvocationTargetException {
277     // find the property descriptor
278
BeanInfo bi = Introspector.getBeanInfo (target.getClass ());
279     PropertyDescriptor pd = (PropertyDescriptor)
280       findFeatureByName ("property", propName, bi.getPropertyDescriptors ());
281     if (pd == null) {
282       throw new IllegalArgumentException JavaDoc ("property '" + propName + "' is " +
283                                           "unknown for '" + target + "'");
284     }
285
286     // get read method and type of property
287
Method rm;
288     Class JavaDoc propType;
289     if (index != null) {
290       // if index != null, then property is indexed - pd better be so too
291
if (!(pd instanceof IndexedPropertyDescriptor)) {
292         throw new IllegalArgumentException JavaDoc ("attempt to get non-indexed " +
293                                             "property '" + propName +
294                                             "' as being indexed");
295       }
296       IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;
297       rm = ipd.getIndexedReadMethod ();
298       propType = ipd.getIndexedPropertyType ();
299     } else {
300       rm = pd.getReadMethod ();
301       propType = pd.getPropertyType ();
302     }
303
304     if (rm == null) {
305       throw new IllegalArgumentException JavaDoc ("property '" + propName +
306                                           "' is not readable");
307     }
308
309     // now get the value
310
Object JavaDoc propVal = null;
311     if (index != null) {
312       propVal = rm.invoke (target, new Object JavaDoc[] {index});
313     } else {
314       propVal = rm.invoke (target, null);
315     }
316     return new Bean (propType, propVal);
317   }
318   public static void setField (Object JavaDoc target, String JavaDoc fieldName, Bean value,
319                                TypeConvertorRegistry tcr)
320       throws IllegalArgumentException JavaDoc, IllegalAccessException JavaDoc {
321     // This is to handle how we do static fields.
322
Class JavaDoc targetClass = (target instanceof Class JavaDoc)
323                         ? (Class JavaDoc) target
324                         : target.getClass ();
325
326     try {
327       Field f = targetClass.getField (fieldName);
328       Class JavaDoc fieldType = f.getType ();
329
330       // type convert the value if necessary
331
Object JavaDoc fieldVal = null;
332       boolean okeydokey = true;
333       if (fieldType.isAssignableFrom (value.type)) {
334         fieldVal = value.value;
335       } else if (tcr != null) {
336         TypeConvertor cvtor = tcr.lookup (value.type, fieldType);
337         if (cvtor != null) {
338           fieldVal = cvtor.convert (value.type, fieldType, value.value);
339         } else {
340           okeydokey = false;
341         }
342       } else {
343         okeydokey = false;
344       }
345       if (!okeydokey) {
346         throw new IllegalArgumentException JavaDoc ("unable to assign '" + value.value +
347                                             "' to field '" + fieldName + "'");
348       }
349
350       // now set the value
351
f.set (target, fieldVal);
352     } catch (NoSuchFieldException JavaDoc e) {
353       throw new IllegalArgumentException JavaDoc ("field '" + fieldName + "' is " +
354                                           "unknown for '" + target + "'");
355     }
356   }
357   //////////////////////////////////////////////////////////////////////////
358

359   /**
360    * Set a property of a bean to a given value.
361    *
362    * @param target the object whose prop is to be set
363    * @param propName name of the property to set
364    * @param index index to set (if property is indexed)
365    * @param value the property value
366    * @param valueType the type of the above (needed when its null)
367    * @param tcr type convertor registry to use to convert value type to
368    * property type if necessary
369    *
370    * @exception IntrospectionException if unable to introspect
371    * @exception IllegalArgumentException if problems with args: if the
372    * property is unknown, or if the property is given an index
373    * when its not, or if the property is not writeable, or if
374    * the given value cannot be assigned to the it (type mismatch).
375    * @exception IllegalAccessException if write method is not accessible
376    * @exception InvocationTargetException if write method excepts
377    */

378   public static void setProperty (Object JavaDoc target, String JavaDoc propName,
379                                   Integer JavaDoc index, Object JavaDoc value,
380                                   Class JavaDoc valueType, TypeConvertorRegistry tcr)
381        throws IntrospectionException, IllegalArgumentException JavaDoc,
382               IllegalAccessException JavaDoc, InvocationTargetException {
383     // find the property descriptor
384
BeanInfo bi = Introspector.getBeanInfo (target.getClass ());
385     PropertyDescriptor pd = (PropertyDescriptor)
386       findFeatureByName ("property", propName, bi.getPropertyDescriptors ());
387     if (pd == null) {
388       throw new IllegalArgumentException JavaDoc ("property '" + propName + "' is " +
389                                           "unknown for '" + target + "'");
390     }
391
392     // get write method and type of property
393
Method wm;
394     Class JavaDoc propType;
395     Object JavaDoc[] args;
396     if (index != null) {
397       // if index != null, then property is indexed - pd better be so too
398
if (!(pd instanceof IndexedPropertyDescriptor)) {
399         throw new IllegalArgumentException JavaDoc ("attempt to set non-indexed " +
400                                             "property '" + propName +
401                                             "' as being indexed");
402       }
403       IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;
404       wm = ipd.getIndexedWriteMethod ();
405       propType = ipd.getIndexedPropertyType ();
406     } else {
407       wm = pd.getWriteMethod ();
408       propType = pd.getPropertyType ();
409     }
410
411     if (wm == null) {
412       throw new IllegalArgumentException JavaDoc ("property '" + propName +
413                                           "' is not writeable");
414     }
415
416     // type convert the value if necessary
417
Object JavaDoc propVal = null;
418     boolean okeydokey = true;
419     if (propType.isAssignableFrom (valueType)) {
420       propVal = value;
421     } else if (tcr != null) {
422       TypeConvertor cvtor = tcr.lookup (valueType, propType);
423       if (cvtor != null) {
424         propVal = cvtor.convert (valueType, propType, value);
425       } else {
426         okeydokey = false;
427       }
428     } else {
429       okeydokey = false;
430     }
431     if (!okeydokey) {
432       throw new IllegalArgumentException JavaDoc ("unable to assign '" + value +
433                                           "' to property '" + propName + "'");
434     }
435
436     // now set the value
437
if (index != null) {
438       wm.invoke (target, new Object JavaDoc[] {index, propVal});
439     } else {
440       wm.invoke (target, new Object JavaDoc[] {propVal});
441     }
442   }
443 }
444
Popular Tags