JAL-3438 spotless for 2.11.2.0
[jalview.git] / test / junit / extensions / PrivilegedAccessor.java
1 /*
2  * Copyright 2004-2012 Sebastian Dietrich (Sebastian.Dietrich@e-movimento.com)
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 package junit.extensions;
17
18 import java.util.Locale;
19
20 import java.lang.reflect.Array;
21 import java.lang.reflect.Constructor;
22 import java.lang.reflect.Field;
23 import java.lang.reflect.InvocationTargetException;
24 import java.lang.reflect.Method;
25 import java.lang.reflect.Modifier;
26 import java.security.InvalidParameterException;
27 import java.util.ArrayList;
28 import java.util.Collection;
29 import java.util.Collections;
30 import java.util.HashMap;
31 import java.util.Map;
32 import java.util.StringTokenizer;
33
34 /**
35  * This class is used to access a method or field of an object no matter what
36  * the access modifier of the method or field. The syntax for accessing fields
37  * and methods is out of the ordinary because this class uses reflection to peel
38  * away protection.
39  * <p>
40  * a.k.a. The "ObjectMolester"
41  * <p>
42  * Here is an example of using this to access a private member: <br>
43  * <code>myObject</code> is an object of type <code>MyClass</code>.
44  * <code>setName(String)</code> is a private method of <code>MyClass</code>.
45  * 
46  * <pre>
47  * PrivilegedAccessor.invokeMethod(myObject, &quot;setName(java.lang.String)&quot;,
48  *         &quot;newName&quot;);
49  * </pre>
50  * 
51  * @author Charlie Hubbard (chubbard@iss.net)
52  * @author Prashant Dhokte (pdhokte@iss.net)
53  * @author Sebastian Dietrich (sebastian.dietrich@e-movimento.com)
54  * 
55  * @deprecated use PA instead. PA improves the functionality of
56  *             PrivilegedAccessor by introducing support for varargs and removal
57  *             of the necessity to catch exceptions.
58  */
59 @Deprecated
60 final class PrivilegedAccessor
61 {
62   /**
63    * Private constructor to make it impossible to instantiate this class.
64    */
65   private PrivilegedAccessor()
66   {
67     assert false : "You mustn't instantiate PrivilegedAccessor, use its methods statically";
68   }
69
70   /**
71    * Returns a string representation of the given object. The string has the
72    * following format: "<classname> {<attributes and values>}" whereas
73    * <attributes and values> is a comma separated list with
74    * <attributeName>=<attributeValue> <atributes and values> includes all
75    * attributes of the objects class followed by the attributes of its
76    * superclass (if any) and so on.
77    * 
78    * @param instanceOrClass
79    *          the object or class to get a string representation of
80    * @return a string representation of the given object
81    */
82   public static String toString(final Object instanceOrClass)
83   {
84     Collection<String> fields = getFieldNames(instanceOrClass);
85
86     if (fields.isEmpty())
87     {
88       return getClass(instanceOrClass).getName();
89     }
90
91     StringBuffer stringBuffer = new StringBuffer();
92
93     stringBuffer.append(getClass(instanceOrClass).getName() + " {");
94
95     for (String fieldName : fields)
96     {
97       try
98       {
99         stringBuffer.append(fieldName + "="
100                 + getValue(instanceOrClass, fieldName) + ", ");
101       } catch (NoSuchFieldException e)
102       {
103         assert false : "It should always be possible to get a field that was just here";
104       }
105     }
106
107     stringBuffer.replace(stringBuffer.lastIndexOf(", "),
108             stringBuffer.length(), "}");
109     return stringBuffer.toString();
110   }
111
112   /**
113    * Gets the name of all fields (public, private, protected, default) of the
114    * given instance or class. This includes as well all fields (public, private,
115    * protected, default) of all its super classes.
116    * 
117    * @param instanceOrClass
118    *          the instance or class to get the fields of
119    * @return the collection of field names of the given instance or class
120    */
121   public static Collection<String> getFieldNames(
122           final Object instanceOrClass)
123   {
124     if (instanceOrClass == null)
125     {
126       return Collections.EMPTY_LIST;
127     }
128
129     Class<?> clazz = getClass(instanceOrClass);
130     Field[] fields = clazz.getDeclaredFields();
131     Collection<String> fieldNames = new ArrayList<String>(fields.length);
132
133     for (Field field : fields)
134     {
135       fieldNames.add(field.getName());
136     }
137     fieldNames.addAll(getFieldNames(clazz.getSuperclass()));
138
139     return fieldNames;
140   }
141
142   /**
143    * Gets the signatures of all methods (public, private, protected, default) of
144    * the given instance or class. This includes as well all methods (public,
145    * private, protected, default) of all its super classes. This does not
146    * include constructors.
147    * 
148    * @param instanceOrClass
149    *          the instance or class to get the method signatures of
150    * @return the collection of method signatures of the given instance or class
151    */
152   public static Collection<String> getMethodSignatures(
153           final Object instanceOrClass)
154   {
155     if (instanceOrClass == null)
156     {
157       return Collections.EMPTY_LIST;
158     }
159
160     Class<?> clazz = getClass(instanceOrClass);
161     Method[] methods = clazz.getDeclaredMethods();
162     Collection<String> methodSignatures = new ArrayList<String>(
163             methods.length + Object.class.getDeclaredMethods().length);
164
165     for (Method method : methods)
166     {
167       methodSignatures.add(method.getName() + "("
168               + getParameterTypesAsString(method.getParameterTypes())
169               + ")");
170     }
171     methodSignatures.addAll(getMethodSignatures(clazz.getSuperclass()));
172
173     return methodSignatures;
174   }
175
176   /**
177    * Gets the value of the named field and returns it as an object. If
178    * instanceOrClass is a class then a static field is returned.
179    * 
180    * @param instanceOrClass
181    *          the instance or class to get the field from
182    * @param fieldName
183    *          the name of the field
184    * @return an object representing the value of the field
185    * @throws NoSuchFieldException
186    *           if the field does not exist
187    */
188   public static Object getValue(final Object instanceOrClass,
189           final String fieldName) throws NoSuchFieldException
190   {
191     Field field = getField(instanceOrClass, fieldName);
192     try
193     {
194       return field.get(instanceOrClass);
195     } catch (IllegalAccessException e)
196     {
197       assert false : "getField() should have setAccessible(true), so an IllegalAccessException should not occur in this place";
198       return null;
199     }
200   }
201
202   /**
203    * Instantiates an object of the given class with the given arguments. If you
204    * want to instantiate a member class, you must provide the object it is a
205    * member of as first argument.
206    * 
207    * @param fromClass
208    *          the class to instantiate an object from
209    * @param args
210    *          the arguments to pass to the constructor
211    * @return an object of the given type
212    * @throws IllegalArgumentException
213    *           if the number of actual and formal parameters differ; if an
214    *           unwrapping conversion for primitive arguments fails; or if, after
215    *           possible unwrapping, a parameter value cannot be converted to the
216    *           corresponding formal parameter type by a method invocation
217    *           conversion.
218    * @throws IllegalAccessException
219    *           if this Constructor object enforces Java language access control
220    *           and the underlying constructor is inaccessible.
221    * @throws InvocationTargetException
222    *           if the underlying constructor throws an exception.
223    * @throws NoSuchMethodException
224    *           if the constructor could not be found
225    * @throws InstantiationException
226    *           if the class that declares the underlying constructor represents
227    *           an abstract class.
228    * 
229    * @see PrivilegedAccessor#instantiate(Class,Class[],Object[])
230    */
231   public static <T> T instantiate(final Class<? extends T> fromClass,
232           final Object[] args) throws IllegalArgumentException,
233           InstantiationException, IllegalAccessException,
234           InvocationTargetException, NoSuchMethodException
235   {
236     return instantiate(fromClass, getParameterTypes(args), args);
237   }
238
239   /**
240    * Instantiates an object of the given class with the given arguments and the
241    * given argument types. If you want to instantiate a member class, you must
242    * provide the object it is a member of as first argument.
243    * 
244    * 
245    * @param fromClass
246    *          the class to instantiate an object from
247    * @param args
248    *          the arguments to pass to the constructor
249    * @param argumentTypes
250    *          the fully qualified types of the arguments of the constructor
251    * @return an object of the given type
252    * @throws IllegalArgumentException
253    *           if the number of actual and formal parameters differ; if an
254    *           unwrapping conversion for primitive arguments fails; or if, after
255    *           possible unwrapping, a parameter value cannot be converted to the
256    *           corresponding formal parameter type by a method invocation
257    *           conversion.
258    * @throws IllegalAccessException
259    *           if this Constructor object enforces Java language access control
260    *           and the underlying constructor is inaccessible.
261    * @throws InvocationTargetException
262    *           if the underlying constructor throws an exception.
263    * @throws NoSuchMethodException
264    *           if the constructor could not be found
265    * @throws InstantiationException
266    *           if the class that declares the underlying constructor represents
267    *           an abstract class.
268    * 
269    * @see PrivilegedAccessor#instantiate(Class,Object[])
270    */
271   public static <T> T instantiate(final Class<? extends T> fromClass,
272           final Class<?>[] argumentTypes, final Object[] args)
273           throws IllegalArgumentException, InstantiationException,
274           IllegalAccessException, InvocationTargetException,
275           NoSuchMethodException
276   {
277     return getConstructor(fromClass, argumentTypes).newInstance(args);
278   }
279
280   /**
281    * Calls a method on the given object instance with the given arguments.
282    * Arguments can be object types or representations for primitives.
283    * 
284    * @param instanceOrClass
285    *          the instance or class to invoke the method on
286    * @param methodSignature
287    *          the name of the method and the parameters <br>
288    *          (e.g. "myMethod(java.lang.String, com.company.project.MyObject)")
289    * @param arguments
290    *          an array of objects to pass as arguments
291    * @return the return value of this method or null if void
292    * @throws IllegalAccessException
293    *           if the method is inaccessible
294    * @throws InvocationTargetException
295    *           if the underlying method throws an exception.
296    * @throws NoSuchMethodException
297    *           if no method with the given <code>methodSignature</code> could be
298    *           found
299    * @throws IllegalArgumentException
300    *           if an argument couldn't be converted to match the expected type
301    */
302   public static Object invokeMethod(final Object instanceOrClass,
303           final String methodSignature, final Object[] arguments)
304           throws IllegalArgumentException, IllegalAccessException,
305           InvocationTargetException, NoSuchMethodException
306   {
307     if ((methodSignature.indexOf('(') == -1) || (methodSignature
308             .indexOf('(') >= methodSignature.indexOf(')')))
309     {
310       throw new NoSuchMethodException(methodSignature);
311     }
312     Class<?>[] parameterTypes = getParameterTypes(methodSignature);
313     return getMethod(instanceOrClass, getMethodName(methodSignature),
314             parameterTypes).invoke(instanceOrClass,
315                     getCorrectedArguments(parameterTypes, arguments));
316   }
317
318   /**
319    * Gets the given arguments corrected to match the given methodSignature.
320    * Correction is necessary for array arguments not to be mistaken by varargs.
321    * 
322    * @param parameterTypes
323    *          the method signatue the given arguments should match
324    * @param arguments
325    *          the arguments that should be corrected
326    * @return the corrected arguments
327    */
328   private static Object[] getCorrectedArguments(Class<?>[] parameterTypes,
329           Object[] arguments)
330   {
331     if (arguments == null)
332     {
333       return arguments;
334     }
335     if (parameterTypes.length > arguments.length)
336     {
337       return arguments;
338     }
339     if (parameterTypes.length < arguments.length)
340     {
341       return getCorrectedArguments(parameterTypes,
342               new Object[]
343               { arguments });
344     }
345
346     Object[] correctedArguments = new Object[arguments.length];
347     int currentArgument = 0;
348     for (Class<?> parameterType : parameterTypes)
349     {
350       correctedArguments[currentArgument] = getCorrectedArgument(
351               parameterType, arguments[currentArgument]);
352       currentArgument++;
353     }
354     return correctedArguments;
355   }
356
357   /**
358    * Gets the given argument corrected to match the given parameterType.
359    * Correction is necessary for array arguments not to be mistaken by varargs.
360    * 
361    * @param parameterType
362    *          the type to match the given argument upon
363    * @param argument
364    *          the argument to match the given parameterType
365    * @return the corrected argument
366    */
367   private static Object getCorrectedArgument(Class<?> parameterType,
368           Object argument)
369   {
370     if (!parameterType.isArray() || (argument == null))
371     {
372       return argument; // normal argument for normal parameterType
373     }
374
375     if (!argument.getClass().isArray())
376     {
377       return new Object[] { argument };
378     }
379
380     if (parameterType.equals(argument.getClass()))
381     {
382       return argument; // no need to cast
383     }
384
385     // (typed) array argument for (object) array parameterType, elements need to
386     // be casted
387     Object correctedArrayArgument = Array.newInstance(
388             parameterType.getComponentType(), Array.getLength(argument));
389     for (int index = 0; index < Array.getLength(argument); index++)
390     {
391       if (parameterType.getComponentType().isPrimitive())
392       { // rely on autoboxing
393         Array.set(correctedArrayArgument, index,
394                 Array.get(argument, index));
395       }
396       else
397       { // cast to expected type
398         try
399         {
400           Array.set(correctedArrayArgument, index, parameterType
401                   .getComponentType().cast(Array.get(argument, index)));
402         } catch (ClassCastException e)
403         {
404           throw new IllegalArgumentException(
405                   "Argument " + argument + " of type " + argument.getClass()
406                           + " does not match expected argument type "
407                           + parameterType + ".");
408         }
409       }
410     }
411     return correctedArrayArgument;
412   }
413
414   /**
415    * Sets the value of the named field. If fieldName denotes a static field,
416    * provide a class, otherwise provide an instance. If the fieldName denotes a
417    * final field, this method could fail with an IllegalAccessException, since
418    * setting the value of final fields at other times than instantiation can
419    * have unpredictable effects.<br/>
420    * <br/>
421    * Example:<br/>
422    * <br/>
423    * <code>
424    * String myString = "Test"; <br/>
425    * <br/>
426    * //setting the private field value<br/>
427    * PrivilegedAccessor.setValue(myString, "value", new char[] {'T', 'e', 's', 't'});<br/>
428    * <br/>
429    * //setting the static final field serialVersionUID - MIGHT FAIL<br/>
430    * PrivilegedAccessor.setValue(myString.getClass(), "serialVersionUID", 1);<br/>
431    * <br/>
432    * </code>
433    * 
434    * @param instanceOrClass
435    *          the instance or class to set the field
436    * @param fieldName
437    *          the name of the field
438    * @param value
439    *          the new value of the field
440    * @throws NoSuchFieldException
441    *           if no field with the given <code>fieldName</code> can be found
442    * @throws IllegalAccessException
443    *           possibly if the field was final
444    */
445   public static void setValue(final Object instanceOrClass,
446           final String fieldName, final Object value)
447           throws NoSuchFieldException, IllegalAccessException
448   {
449     Field field = getField(instanceOrClass, fieldName);
450     if (Modifier.isFinal(field.getModifiers()))
451     {
452       PrivilegedAccessor.setValue(field, "modifiers",
453               field.getModifiers() ^ Modifier.FINAL);
454     }
455     field.set(instanceOrClass, value);
456   }
457
458   /**
459    * Gets the class with the given className.
460    * 
461    * @param className
462    *          the name of the class to get
463    * @return the class for the given className
464    * @throws ClassNotFoundException
465    *           if the class could not be found
466    */
467   private static Class<?> getClassForName(final String className)
468           throws ClassNotFoundException
469   {
470     if (className.indexOf('[') > -1)
471     {
472       Class<?> clazz = getClassForName(
473               className.substring(0, className.indexOf('[')));
474       return Array.newInstance(clazz, 0).getClass();
475     }
476
477     if (className.indexOf("...") > -1)
478     {
479       Class<?> clazz = getClassForName(
480               className.substring(0, className.indexOf("...")));
481       return Array.newInstance(clazz, 0).getClass();
482     }
483
484     try
485     {
486       return Class.forName(className, false,
487               Thread.currentThread().getContextClassLoader());
488     } catch (ClassNotFoundException e)
489     {
490       return getSpecialClassForName(className);
491     }
492   }
493
494   /**
495    * Maps string representation of primitives to their corresponding classes.
496    */
497   private static final Map<String, Class<?>> PRIMITIVE_MAPPER = new HashMap<String, Class<?>>(
498           8);
499
500   /**
501    * Fills the map with all java primitives and their corresponding classes.
502    */
503   static
504   {
505     PRIMITIVE_MAPPER.put("int", Integer.TYPE);
506     PRIMITIVE_MAPPER.put("float", Float.TYPE);
507     PRIMITIVE_MAPPER.put("double", Double.TYPE);
508     PRIMITIVE_MAPPER.put("short", Short.TYPE);
509     PRIMITIVE_MAPPER.put("long", Long.TYPE);
510     PRIMITIVE_MAPPER.put("byte", Byte.TYPE);
511     PRIMITIVE_MAPPER.put("char", Character.TYPE);
512     PRIMITIVE_MAPPER.put("boolean", Boolean.TYPE);
513   }
514
515   /**
516    * Gets special classes for the given className. Special classes are
517    * primitives and "standard" Java types (like String)
518    * 
519    * @param className
520    *          the name of the class to get
521    * @return the class for the given className
522    * @throws ClassNotFoundException
523    *           if the class could not be found
524    */
525   private static Class<?> getSpecialClassForName(final String className)
526           throws ClassNotFoundException
527   {
528     if (PRIMITIVE_MAPPER.containsKey(className))
529     {
530       return PRIMITIVE_MAPPER.get(className);
531     }
532
533     if (missesPackageName(className))
534     {
535       return getStandardClassForName(className);
536     }
537
538     throw new ClassNotFoundException(className);
539   }
540
541   /**
542    * Gets a 'standard' java class for the given className.
543    * 
544    * @param className
545    *          the className
546    * @return the class for the given className (if any)
547    * @throws ClassNotFoundException
548    *           of no 'standard' java class was found for the given className
549    */
550   private static Class<?> getStandardClassForName(String className)
551           throws ClassNotFoundException
552   {
553     try
554     {
555       return Class.forName("java.lang." + className, false,
556               Thread.currentThread().getContextClassLoader());
557     } catch (ClassNotFoundException e)
558     {
559       try
560       {
561         return Class.forName("java.util." + className, false,
562                 Thread.currentThread().getContextClassLoader());
563       } catch (ClassNotFoundException e1)
564       {
565         throw new ClassNotFoundException(className);
566       }
567     }
568   }
569
570   /**
571    * Tests if the given className possibly misses its package name.
572    * 
573    * @param className
574    *          the className
575    * @return true if the className might miss its package name, otherwise false
576    */
577   private static boolean missesPackageName(String className)
578   {
579     if (className.contains("."))
580     {
581       return false;
582     }
583     if (className
584             .startsWith(className.substring(0, 1).toUpperCase(Locale.ROOT)))
585     {
586       return true;
587     }
588     return false;
589   }
590
591   /**
592    * Gets the constructor for a given class with the given parameters.
593    * 
594    * @param type
595    *          the class to instantiate
596    * @param parameterTypes
597    *          the types of the parameters
598    * @return the constructor
599    * @throws NoSuchMethodException
600    *           if the method could not be found
601    */
602   private static <T> Constructor<T> getConstructor(final Class<T> type,
603           final Class<?>[] parameterTypes) throws NoSuchMethodException
604   {
605     Constructor<T> constructor = type
606             .getDeclaredConstructor(parameterTypes);
607     constructor.setAccessible(true);
608     return constructor;
609   }
610
611   /**
612    * Return the named field from the given instance or class. Returns a static
613    * field if instanceOrClass is a class.
614    * 
615    * @param instanceOrClass
616    *          the instance or class to get the field from
617    * @param fieldName
618    *          the name of the field to get
619    * @return the field
620    * @throws NoSuchFieldException
621    *           if no such field can be found
622    * @throws InvalidParameterException
623    *           if instanceOrClass was null
624    */
625   private static Field getField(final Object instanceOrClass,
626           final String fieldName)
627           throws NoSuchFieldException, InvalidParameterException
628   {
629     if (instanceOrClass == null)
630     {
631       throw new InvalidParameterException(
632               "Can't get field on null object/class");
633     }
634
635     Class<?> type = getClass(instanceOrClass);
636
637     try
638     {
639       Field field = type.getDeclaredField(fieldName);
640       field.setAccessible(true);
641       return field;
642     } catch (NoSuchFieldException e)
643     {
644       if (type.getSuperclass() == null)
645       {
646         throw e;
647       }
648       return getField(type.getSuperclass(), fieldName);
649     }
650   }
651
652   /**
653    * Gets the class of the given parameter. If the parameter is a class, it is
654    * returned, if it is an object, its class is returned
655    * 
656    * @param instanceOrClass
657    *          the instance or class to get the class of
658    * @return the class of the given parameter
659    */
660   private static Class<?> getClass(final Object instanceOrClass)
661   {
662     if (instanceOrClass instanceof Class)
663     {
664       return (Class<?>) instanceOrClass;
665     }
666
667     return instanceOrClass.getClass();
668   }
669
670   /**
671    * Return the named method with a method signature matching classTypes from
672    * the given class.
673    * 
674    * @param type
675    *          the class to get the method from
676    * @param methodName
677    *          the name of the method to get
678    * @param parameterTypes
679    *          the parameter-types of the method to get
680    * @return the method
681    * @throws NoSuchMethodException
682    *           if the method could not be found
683    */
684   private static Method getMethod(final Class<?> type,
685           final String methodName, final Class<?>[] parameterTypes)
686           throws NoSuchMethodException
687   {
688     try
689     {
690       return type.getDeclaredMethod(methodName, parameterTypes);
691     } catch (NoSuchMethodException e)
692     {
693       if (type.getSuperclass() == null)
694       {
695         throw e;
696       }
697       return getMethod(type.getSuperclass(), methodName, parameterTypes);
698     }
699   }
700
701   /**
702    * Gets the method with the given name and parameters from the given instance
703    * or class. If instanceOrClass is a class, then we get a static method.
704    * 
705    * @param instanceOrClass
706    *          the instance or class to get the method of
707    * @param methodName
708    *          the name of the method
709    * @param parameterTypes
710    *          the parameter-types of the method to get
711    * @return the method
712    * @throws NoSuchMethodException
713    *           if the method could not be found
714    */
715   private static Method getMethod(final Object instanceOrClass,
716           final String methodName, final Class<?>[] parameterTypes)
717           throws NoSuchMethodException
718   {
719     Class<?> type;
720
721     type = getClass(instanceOrClass);
722
723     Method accessMethod = getMethod(type, methodName, parameterTypes);
724     accessMethod.setAccessible(true);
725     return accessMethod;
726   }
727
728   /**
729    * Gets the name of a method.
730    * 
731    * @param methodSignature
732    *          the signature of the method
733    * @return the name of the method
734    */
735   private static String getMethodName(final String methodSignature)
736   {
737     try
738     {
739       return methodSignature.substring(0, methodSignature.indexOf('('))
740               .trim();
741     } catch (StringIndexOutOfBoundsException e)
742     {
743       assert false : "Signature must have been checked before this method was called";
744       return null;
745     }
746   }
747
748   /**
749    * Gets the types of the parameters.
750    * 
751    * @param parameters
752    *          the parameters
753    * @return the class-types of the arguments
754    */
755   private static Class<?>[] getParameterTypes(final Object[] parameters)
756   {
757     if (parameters == null)
758     {
759       return new Class[0];
760     }
761
762     Class<?>[] typesOfParameters = new Class[parameters.length];
763
764     for (int i = 0; i < parameters.length; i++)
765     {
766       typesOfParameters[i] = parameters[i].getClass();
767     }
768     return typesOfParameters;
769   }
770
771   /**
772    * Gets the types of the given parameters. If the parameters don't match the
773    * given methodSignature an IllegalArgumentException is thrown.
774    * 
775    * @param methodSignature
776    *          the signature of the method
777    * @return the parameter types as class[]
778    * @throws NoSuchMethodException
779    *           if the method could not be found
780    * @throws IllegalArgumentException
781    *           if one of the given parameters doesn't math the given
782    *           methodSignature
783    */
784   private static Class<?>[] getParameterTypes(final String methodSignature)
785           throws NoSuchMethodException, IllegalArgumentException
786   {
787     String signature = getSignatureWithoutBraces(methodSignature);
788
789     StringTokenizer tokenizer = new StringTokenizer(signature, ", *");
790     Class<?>[] typesInSignature = new Class[tokenizer.countTokens()];
791
792     for (int x = 0; tokenizer.hasMoreTokens(); x++)
793     {
794       String className = tokenizer.nextToken();
795       try
796       {
797         typesInSignature[x] = getClassForName(className);
798       } catch (ClassNotFoundException e)
799       {
800         NoSuchMethodException noSuchMethodException = new NoSuchMethodException(
801                 methodSignature);
802         noSuchMethodException.initCause(e);
803         throw noSuchMethodException;
804       }
805     }
806     return typesInSignature;
807   }
808
809   /**
810    * Gets the parameter types as a string.
811    * 
812    * @param classTypes
813    *          the types to get as names.
814    * @return the parameter types as a string
815    * 
816    * @see java.lang.Class#argumentTypesToString(Class[])
817    */
818   private static String getParameterTypesAsString(
819           final Class<?>[] classTypes)
820   {
821     assert classTypes != null : "getParameterTypes() should have been called before this method and should have provided not-null classTypes";
822     if (classTypes.length == 0)
823     {
824       return "";
825     }
826
827     StringBuilder parameterTypes = new StringBuilder();
828     for (Class<?> clazz : classTypes)
829     {
830       assert clazz != null : "getParameterTypes() should have been called before this method and should have provided not-null classTypes";
831       parameterTypes.append(clazz.getName()).append(", ");
832     }
833
834     return parameterTypes.substring(0, parameterTypes.length() - 2);
835   }
836
837   /**
838    * Removes the braces around the methods signature.
839    * 
840    * @param methodSignature
841    *          the signature with braces
842    * @return the signature without braces
843    */
844   private static String getSignatureWithoutBraces(
845           final String methodSignature)
846   {
847     try
848     {
849       return methodSignature.substring(methodSignature.indexOf('(') + 1,
850               methodSignature.indexOf(')'));
851     } catch (IndexOutOfBoundsException e)
852     {
853       assert false : "signature must have been checked before this method";
854       return null;
855     }
856   }
857
858 }