KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javolution > lang > Reflection


1 /*
2  * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
3  * Copyright (C) 2005 - Javolution (http://javolution.org/)
4  * All rights reserved.
5  *
6  * Permission to use, copy, modify, and distribute this software is
7  * freely granted, provided that this notice is preserved.
8  */

9 package javolution.lang;
10
11 import javolution.JavolutionError;
12 import javolution.util.FastMap;
13 import j2me.lang.CharSequence;
14
15 /**
16  * <p> This utility class greatly facilitates the use of reflection to invoke
17  * constructors or methods which may or may not exist at runtime.</p>
18  *
19  * <p> The constructors/methods are identified through their signatures
20  * represented as a {@link String}. When the constructor/method does
21  * not exist (e.g. class not found) or when the platform does not support
22  * reflection, the constructor/method is <code>null</code>
23  * (no exception raised). Here is an example of timer taking advantage
24  * of the new (JRE1.5+) high resolution time when available:[code]
25  * public static long microTime() {
26  * if (NANO_TIME_METHOD != null) { // JRE 1.5+
27  * Long time = (Long) NANO_TIME_METHOD.invoke(null); // Static method.
28  * return time.longValue() / 1000;
29  * } else { // Use the less accurate time in milliseconds.
30  * return System.currentTimeMillis() * 1000;
31  * }
32  * }
33  * private static final Reflection.Method NANO_TIME_METHOD
34  * = Reflection.getMethod("j2me.lang.System.nanoTime()");[/code]</p>
35  *
36  * <p> Arrays and primitive types are supported. For example:[code]
37  * Reflection.Constructor sbc = Reflection.getConstructor("j2me.lang.StringBuilder(int)");
38  * if (sbc != null) { // JDK 1.5+
39  * Object sb = sbc.newInstance(new Integer(32));
40  * Reflection.Method append = Reflection.getMethod("j2me.lang.StringBuilder.append(char[], int, int)");
41  * append.invoke(sb, new char[] { 'h', 'i' }, new Integer(0), new Integer(2));
42  * System.out.println(sb);
43  * }
44  *
45  * > hi[/code]</p>
46  *
47  * @author <a HREF="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
48  * @version 4.0, September 1, 2006
49  */

50 public final class Reflection {
51
52     /**
53      * Default constructor (private to forbid instantiation).
54      */

55     private Reflection() {
56     }
57
58     /**
59      * Returns the class having the specified name.
60      * This method searches a lookup table first, then diverse class loaders
61      * (caller, context, system); the newly found class is then initialized
62      * and added to the lookup table for future reference.
63      *
64      * @param name the name of the class to search for.
65      * @return the corresponding class
66      * @throws ClassNotFoundException if the class is not found.
67      */

68     public static Class JavaDoc getClass(CharSequence JavaDoc name)
69             throws ClassNotFoundException JavaDoc {
70         Class JavaDoc cls = (Class JavaDoc) _NameToClass.get(name);
71         return (cls != null) ? cls : searchClass(name.toString());
72     }
73
74     private static Class JavaDoc searchClass(String JavaDoc name) throws ClassNotFoundException JavaDoc {
75         Class JavaDoc cls = null;
76         try {
77             cls = Class.forName(name); // Caller class loader.
78
} catch (ClassNotFoundException JavaDoc e0) { // Try context class loader.
79
/*@JVM-1.4+@
80              try {
81              ClassLoader cl = Thread.currentThread().getContextClassLoader();
82              cls = Class.forName(name, true, cl);
83              } catch (ClassNotFoundException e1) { // Try system class loader.
84              ClassLoader cl = ClassLoader.getSystemClassLoader();
85              cls = Class.forName(name, true, cl);
86              }
87              /**/

88             if (cls == null)
89                 throw new ClassNotFoundException JavaDoc("Cannot found class " + name);
90         }
91         synchronized (_NameToClass) {
92             _NameToClass.put(name, cls);
93         }
94         return cls;
95     }
96
97     private static final FastMap _NameToClass = new FastMap();
98
99     /**
100      * Equivalent to {@link #getClass(CharSequence)} (for J2ME compatibility).
101      */

102     public static Class JavaDoc getClass(String JavaDoc name) throws ClassNotFoundException JavaDoc {
103         Class JavaDoc cls = (Class JavaDoc) _NameToClass.get(name);
104         return (cls != null) ? cls : searchClass(name);
105     }
106
107     /**
108      * Returns the constructor having the specified signature.
109      *
110      * @param signature the textual representation of the constructor signature.
111      * @return the corresponding constructor or <code>null</code> if none
112      * found.
113      */

114     public static Constructor getConstructor(String JavaDoc signature) {
115         int argStart = signature.indexOf('(') + 1;
116         if (argStart < 0) {
117             throw new IllegalArgumentException JavaDoc("Parenthesis '(' not found");
118         }
119         int argEnd = signature.indexOf(')');
120         if (argEnd < 0) {
121             throw new IllegalArgumentException JavaDoc("Parenthesis ')' not found");
122         }
123         String JavaDoc className = signature.substring(0, argStart - 1);
124         Class JavaDoc theClass;
125         try {
126             theClass = Reflection.getClass(className);
127         } catch (ClassNotFoundException JavaDoc e) {
128             return null;
129         }
130         String JavaDoc args = signature.substring(argStart, argEnd);
131         if (args.length() == 0)
132             return new DefaultConstructor(theClass);
133         /*@JVM-1.4+@
134          Class[] argsTypes;
135          try {
136          argsTypes = classesFor(args);
137          } catch (ClassNotFoundException e) {
138          return null;
139          }
140          try {
141          return new ReflectConstructor(theClass.getConstructor(argsTypes),
142          signature);
143          } catch (NoSuchMethodException e) {
144          }
145          /**/

146         return null;
147     }
148
149     private static class DefaultConstructor extends Constructor {
150         final Class JavaDoc _class;
151
152         DefaultConstructor(Class JavaDoc cl) {
153             _class = cl;
154         }
155
156         public Object JavaDoc allocate(Object JavaDoc[] args) {
157             try {
158                 return _class.newInstance();
159             } catch (InstantiationException JavaDoc e) {
160                 throw new JavolutionError("Instantiation error for "
161                         + _class.getName() + " default constructor", e);
162             } catch (IllegalAccessException JavaDoc e) {
163                 throw new JavolutionError("Illegal access error for "
164                         + _class.getName() + " constructor", e);
165             }
166         }
167
168         public String JavaDoc toString() {
169             return _class + " default constructor";
170         }
171     }
172
173     /*@JVM-1.4+@
174      private static final class ReflectConstructor extends Constructor {
175      private final java.lang.reflect.Constructor _value;
176
177      private final String _signature;
178
179      public ReflectConstructor(java.lang.reflect.Constructor value,
180      String signature) {
181      _value = value;
182      _signature = signature;
183      }
184
185      public Object allocate(Object[] args) {
186      try {
187      return _value.newInstance(args);
188      } catch (IllegalArgumentException e) {
189      throw new JavolutionError("Illegal argument for " + _signature
190      + " constructor", e);
191      } catch (InstantiationException e) {
192      throw new JavolutionError("Instantiation error for "
193      + _signature + " constructor", e);
194      } catch (IllegalAccessException e) {
195      throw new JavolutionError("Illegal access error for "
196      + _signature + " constructor", e);
197      } catch (java.lang.reflect.InvocationTargetException e) {
198      throw new JavolutionError("Invocation exception for "
199      + _signature + " constructor",
200      (java.lang.reflect.InvocationTargetException) e.getTargetException());
201      }
202      }
203
204      public String toString() {
205      return _signature + " constructor";
206      }
207      }
208      /**/

209
210     /**
211      * Returns the method having the specified signature.
212      *
213      * @param signature the textual representation of the method signature.
214      * @return the corresponding constructor or <code>null</code> if none
215      * found.
216      */

217     public static Method getMethod(String JavaDoc signature) {
218         /*@JVM-1.4+@
219          int argStart = signature.indexOf('(') + 1;
220          if (argStart < 0) {
221          throw new IllegalArgumentException("Parenthesis '(' not found");
222          }
223          int argEnd = signature.indexOf(')');
224          if (argEnd < 0) {
225          throw new IllegalArgumentException("Parenthesis ')' not found");
226          }
227          int nameStart = signature.substring(0, argStart).lastIndexOf('.') + 1;
228          try {
229
230          String className = signature.substring(0, nameStart - 1);
231          Class theClass;
232          try {
233          theClass = Reflection.getClass(className);
234          } catch (ClassNotFoundException e) {
235          return null;
236          }
237          String methodName = signature.substring(nameStart, argStart - 1);
238          String args = signature.substring(argStart, argEnd);
239          Class[] argsTypes;
240          try {
241          argsTypes = classesFor(args);
242          } catch (ClassNotFoundException e) {
243          return null;
244          }
245          return new ReflectMethod(theClass.getMethod(methodName, argsTypes),
246          signature);
247          } catch (Throwable t) {
248          }
249          /**/

250         return null;
251     }
252
253     /*@JVM-1.4+@
254      private static final class ReflectMethod extends Method {
255      private final java.lang.reflect.Method _value;
256
257      private final String _signature;
258
259      public ReflectMethod(java.lang.reflect.Method value, String signature) {
260      _value = value;
261      _signature = signature;
262      }
263
264      public Object execute(Object that, Object[] args) {
265      try {
266      return _value.invoke(that, args);
267      } catch (IllegalArgumentException e) {
268      throw new JavolutionError("Illegal argument for " + _signature
269      + " method", e);
270      } catch (IllegalAccessException e) {
271      throw new JavolutionError("Illegal access error for "
272      + _signature + " method", e);
273      } catch (java.lang.reflect.InvocationTargetException e) {
274      throw new JavolutionError("Invocation exception for "
275      + _signature + " method", (java.lang.reflect.InvocationTargetException) e
276      .getTargetException());
277      }
278      }
279
280      public String toString() {
281      return _signature + " method";
282      }
283      }
284      /**/

285
286     /**
287      * Returns the classes for the specified argument.
288      *
289      * @param args the comma separated arguments.
290      * @return the classes or <code>null</code> if one of the class is not found.
291      @JVM-1.4+@
292      private static Class[] classesFor(String args) throws ClassNotFoundException {
293      args = args.trim();
294      if (args.length() == 0) {
295      return new Class[0];
296      }
297      // Counts commas.
298      int commas = 0;
299      for (int i=0;;) {
300      i = args.indexOf(',', i);
301      if (i++ < 0) break;
302      commas++;
303      }
304      Class[] classes = new Class[commas + 1];
305
306      int index = 0;
307      for (int i = 0; i < commas; i++) {
308      int sep = args.indexOf(',', index);
309      classes[i] = classFor(args.substring(index, sep).trim());
310      if (classes[i] == null) return null;
311      index = sep + 1;
312      }
313      classes[commas] = classFor(args.substring(index).trim());
314      if (classes[commas] == null) return null;
315      return classes;
316      }
317
318      private static Class classFor(String className) throws ClassNotFoundException {
319      int arrayIndex = className.indexOf("[]");
320      if (arrayIndex >= 0) {
321      if (className.indexOf("[][]") >= 0) {
322      if (className.indexOf("[][][]") >= 0) {
323      if (className.indexOf("[][][][]") >= 0) {
324      throw new UnsupportedOperationException(
325      "The maximum array dimension is 3");
326      } else { // Dimension three.
327      return Reflection.getClass("[[["
328      + descriptorFor(className.substring(0,
329      arrayIndex)));
330      }
331      } else { // Dimension two.
332      return Reflection.getClass("[["
333      + descriptorFor(className.substring(0, arrayIndex)));
334      }
335      } else { // Dimension one.
336      return Reflection.getClass("["
337      + descriptorFor(className.substring(0, arrayIndex)));
338      }
339      }
340      if (className.equals("boolean")) {
341      return boolean.class;
342      } else if (className.equals("byte")) {
343      return byte.class;
344      } else if (className.equals("char")) {
345      return char.class;
346      } else if (className.equals("short")) {
347      return short.class;
348      } else if (className.equals("int")) {
349      return int.class;
350      } else if (className.equals("long")) {
351      return long.class;
352      } else if (className.equals("float")) {
353      return float.class;
354      } else if (className.equals("double")) {
355      return double.class;
356      } else {
357      return Reflection.getClass(className);
358      }
359      }
360
361      private static String descriptorFor(String className) {
362      if (className.equals("boolean")) {
363      return "Z";
364      } else if (className.equals("byte")) {
365      return "B";
366      } else if (className.equals("char")) {
367      return "C";
368      } else if (className.equals("short")) {
369      return "S";
370      } else if (className.equals("int")) {
371      return "I";
372      } else if (className.equals("long")) {
373      return "J";
374      } else if (className.equals("float")) {
375      return "F";
376      } else if (className.equals("double")) {
377      return "D";
378      } else {
379      return "L" + className + ";";
380      }
381      }
382      /**/

383
384     /**
385      * This class represents a run-time constructor obtained through reflection.
386      *
387      * Here are few examples of utilization:[code]
388      * // Default constructor (fastList = new FastList())
389      * Reflection.Constructor fastListConstructor
390      * = Reflection.getConstructor("javolution.util.FastList()");
391      * Object fastList = fastListConstructor.newInstance();
392      *
393      * // Constructor with arguments (fastMap = new FastMap(64))
394      * Reflection.Constructor fastMapConstructor
395      * = Reflection.getConstructor("javolution.util.FastMap(int)");
396      * Object fastMap = fastMapConstructor.newInstance(new Integer(64));
397      * [/code]
398      */

399     public static abstract class Constructor {
400
401         /**
402          * Default constructor.
403          */

404         public Constructor() {
405         }
406
407         /**
408          * Allocates a new object using this constructor with the specified
409          * arguments.
410          *
411          * @param args the constructor arguments.
412          * @return the object being instantiated.
413          */

414         protected abstract Object JavaDoc allocate(Object JavaDoc[] args);
415
416         /**
417          * Invokes this constructor with no argument (convenience method).
418          *
419          * @return the object being instantiated.
420          */

421         public final Object JavaDoc newInstance() {
422             return allocate(NO_ARG);
423         }
424
425         private static final Object JavaDoc[] NO_ARG = new Object JavaDoc[0];
426
427         /**
428          * Invokes this constructor with the specified single argument.
429          *
430          * @param arg0 the first argument.
431          * @return the object being instantiated.
432          */

433         public final Object JavaDoc newInstance(Object JavaDoc arg0) {
434             synchronized (this) {
435                 array1[0] = arg0;
436                 return allocate(array1);
437             }
438         }
439
440         private final Object JavaDoc[] array1 = new Object JavaDoc[1];
441
442         /**
443          * Invokes this constructor with the specified two arguments.
444          *
445          * @param arg0 the first argument.
446          * @param arg1 the second argument.
447          * @return the object being instantiated.
448          */

449         public final Object JavaDoc newInstance(Object JavaDoc arg0, Object JavaDoc arg1) {
450             synchronized (this) {
451                 array2[0] = arg0;
452                 array2[1] = arg1;
453                 return allocate(array2);
454             }
455         }
456
457         private final Object JavaDoc[] array2 = new Object JavaDoc[2];
458
459         /**
460          * Invokes this constructor with the specified three arguments.
461          *
462          * @param arg0 the first argument.
463          * @param arg1 the second argument.
464          * @param arg2 the third argument.
465          * @return the object being instantiated.
466          */

467         public final Object JavaDoc newInstance(Object JavaDoc arg0, Object JavaDoc arg1, Object JavaDoc arg2) {
468             synchronized (this) {
469                 array3[0] = arg0;
470                 array3[1] = arg1;
471                 array3[2] = arg2;
472                 return allocate(array3);
473             }
474         }
475
476         private final Object JavaDoc[] array3 = new Object JavaDoc[3];
477
478         /**
479          * Invokes this constructor with the specified four arguments.
480          *
481          * @param arg0 the first argument.
482          * @param arg1 the second argument.
483          * @param arg2 the third argument.
484          * @param arg3 the fourth argument.
485          * @return the object being instantiated.
486          */

487         public final Object JavaDoc newInstance(Object JavaDoc arg0, Object JavaDoc arg1, Object JavaDoc arg2,
488                 Object JavaDoc arg3) {
489             synchronized (this) {
490                 array4[0] = arg0;
491                 array4[1] = arg1;
492                 array4[2] = arg2;
493                 array4[3] = arg3;
494                 return allocate(array4);
495             }
496         }
497
498         private final Object JavaDoc[] array4 = new Object JavaDoc[4];
499     }
500
501     /**
502      * This class represents a run-time method obtained through reflection.
503      *
504      * Here are few examples of utilization:[code]
505      * // Non-static method: fastMap.put(myKey, myValue)
506      * Reflection.Method putKeyValue
507      * = Reflection.getMethod(
508      * "javolution.util.FastMap.put(j2me.lang.Object, j2me.lang.Object)");
509      * Object previous = putKeyValue.invoke(fastMap, myKey, myValue);
510      *
511      * // Static method: System.nanoTime() (JRE1.5+)
512      * Reflection.Method nanoTime
513      * = Reflection.getMethod("j2me.lang.System.nanoTime()");
514      * long time = ((Long)nanoTime.invoke(null)).longValue();[/code]
515      */

516     public static abstract class Method {
517
518         /**
519          * Default constructor.
520          */

521         public Method() {
522         }
523
524         /**
525          * Executes this method with the specified arguments.
526          *
527          * @param thisObject the object upon which this method is invoked
528          * or <code>null</code> for static methods.
529          * @param args the method arguments.
530          * @return the result of the execution.
531          */

532         protected abstract Object JavaDoc execute(Object JavaDoc thisObject, Object JavaDoc[] args);
533
534         /**
535          * Invokes this method on the specified object which might be
536          * <code>null</code> if the method is static (convenience method).
537          *
538          * @param thisObject the object upon which this method is invoked
539          * or <code>null</code> for static methods.
540          * @return the result of the invocation.
541          */

542         public final Object JavaDoc invoke(Object JavaDoc thisObject) {
543             return execute(thisObject, NO_ARG);
544         }
545
546         private static final Object JavaDoc[] NO_ARG = new Object JavaDoc[0];
547
548         /**
549          * Invokes this method with the specified single argument
550          * on the specified object which might be <code>null</code>
551          * if the method is static (convenience method).
552          *
553          * @param thisObject the object upon which this method is invoked
554          * or <code>null</code> for static methods.
555          * @param arg0 the single argument.
556          * @return the result of the invocation.
557          */

558         public final Object JavaDoc invoke(Object JavaDoc thisObject, Object JavaDoc arg0) {
559             synchronized (this) {
560                 array1[0] = arg0;
561                 return execute(thisObject, array1);
562             }
563         }
564
565         private final Object JavaDoc[] array1 = new Object JavaDoc[1];
566
567         /**
568          * Invokes this method with the specified two arguments
569          * on the specified object which might be <code>null</code>
570          * if the method is static (convenience method).
571          *
572          * @param thisObject the object upon which this method is invoked
573          * or <code>null</code> for static methods.
574          * @param arg0 the first argument.
575          * @param arg1 the second argument.
576          * @return the result of the invocation.
577          * @throws RuntimeException wrapping any exception raised during
578          * invocation (see <code>Throwable.getCause()</code>).
579          */

580         public final Object JavaDoc invoke(Object JavaDoc thisObject, Object JavaDoc arg0, Object JavaDoc arg1) {
581             synchronized (this) {
582                 array2[0] = arg0;
583                 array2[1] = arg1;
584                 return execute(thisObject, array2);
585             }
586         }
587
588         private final Object JavaDoc[] array2 = new Object JavaDoc[2];
589
590         /**
591          * Invokes this method with the specified three arguments
592          * on the specified object which might be <code>null</code>
593          * if the method is static.
594          *
595          * @param thisObject the object upon which this method is invoked
596          * or <code>null</code> for static methods.
597          * @param arg0 the first argument (convenience method).
598          * @param arg1 the second argument.
599          * @param arg2 the third argument.
600          * @return the result of the invocation.
601          */

602         public final Object JavaDoc invoke(Object JavaDoc thisObject, Object JavaDoc arg0, Object JavaDoc arg1,
603                 Object JavaDoc arg2) {
604             synchronized (this) {
605                 array3[0] = arg0;
606                 array3[1] = arg1;
607                 array3[2] = arg2;
608                 return execute(thisObject, array3);
609             }
610         }
611
612         private final Object JavaDoc[] array3 = new Object JavaDoc[3];
613
614         /**
615          * Invokes this method with the specified four arguments
616          * on the specified object which might be <code>null</code>
617          * if the method is static (convenience method).
618          *
619          * @param thisObject the object upon which this method is invoked
620          * or <code>null</code> for static methods.
621          * @param arg0 the first argument.
622          * @param arg1 the second argument.
623          * @param arg2 the third argument.
624          * @param arg3 the fourth argument.
625          * @return the result of the invocation.
626          */

627         public final Object JavaDoc invoke(Object JavaDoc thisObject, Object JavaDoc arg0, Object JavaDoc arg1,
628                 Object JavaDoc arg2, Object JavaDoc arg3) {
629             synchronized (this) {
630                 array4[0] = arg0;
631                 array4[1] = arg1;
632                 array4[2] = arg2;
633                 array4[3] = arg3;
634                 return execute(thisObject, array4);
635             }
636         }
637
638         private final Object JavaDoc[] array4 = new Object JavaDoc[4];
639     }
640
641 }
Popular Tags