release 0.9.6
[debian/openrocket] / src / net / sf / openrocket / util / Reflection.java
1 package net.sf.openrocket.util;
2
3 import java.lang.reflect.Constructor;
4 import java.lang.reflect.InvocationTargetException;
5
6 import net.sf.openrocket.rocketcomponent.RocketComponent;
7
8
9 public class Reflection {
10         
11         private static final String ROCKETCOMPONENT_PACKAGE = "net.sf.openrocket.rocketcomponent";
12         
13         /**
14          * Simple wrapper class that converts the Method.invoke() exceptions into suitable
15          * RuntimeExceptions.
16          * 
17          * @author Sampo Niskanen <sampo.niskanen@iki.fi>
18          */
19         public static class Method {
20                 private final java.lang.reflect.Method method;
21                 public Method(java.lang.reflect.Method m) {
22                         method = m;
23                 }
24                 /**
25                  * Same as Method.invoke(), but the possible exceptions are wrapped into 
26                  * RuntimeExceptions.
27                  */
28                 public Object invoke(Object obj, Object... args) {
29                         try {
30                                 return method.invoke(obj, args);
31                         } catch (IllegalArgumentException e) {
32                                 throw new BugException("Error while invoking method '"+method+"'. "+
33                                                 "Please report this as a bug.",e);
34                         } catch (IllegalAccessException e) {
35                                 throw new BugException("Error while invoking method '"+method+"'. "+
36                                                 "Please report this as a bug.",e);
37                         } catch (InvocationTargetException e) {
38                                 throw Reflection.handleWrappedException(e);
39                         }
40                 }
41                 /**
42                  * Invoke static method.  Equivalent to invoke(null, args...).
43                  */
44                 public Object invokeStatic(Object... args) {
45                         return invoke(null,args);
46                 }
47                 /**
48                  * Same as Method.toString().
49                  */
50                 @Override
51                 public String toString() {
52                         return method.toString();
53                 }
54         }
55         
56         
57         /**
58          * Handles an InvocationTargetException gracefully.  If the cause is an unchecked
59          * exception it is thrown, otherwise it is encapsulated in a BugException.
60          * <p>
61          * This method has a return type of Error in order to allow writing code like:
62          * <pre>throw Reflection.handleInvocationTargetException(e)</pre>
63          * This allows the compiler verifying that the call will never succeed correctly
64          * and ending that branch of execution.
65          * 
66          * @param e             the InvocationTargetException that occurred (not null).
67          * @return              never returns normally.
68          */
69         public static Error handleWrappedException(Exception e) {
70                 Throwable cause = e.getCause();
71                 if (cause == null) {
72                         throw new BugException("BUG: wrapped exception without cause", e);
73                 }
74                 if (cause instanceof RuntimeException) {
75                         throw (RuntimeException)cause;
76                 }
77                 if (cause instanceof Error) {
78                         throw (Error)cause;
79                 }
80                 throw new BugException("wrapped exception occurred", cause);
81         }
82         
83         
84         
85         /**
86          * Throws an exception if method not found.
87          */
88         public static Reflection.Method findMethodStatic(
89                         Class<? extends RocketComponent> componentClass,
90                         String method, Class<?>... params) {
91                 Reflection.Method m = findMethod(ROCKETCOMPONENT_PACKAGE, componentClass, 
92                                 "", method, params);
93                 if (m == null) {
94                         throw new BugException("Could not find method for componentClass="
95                                         +componentClass+" method="+method);
96                 }
97                 return m;
98         }
99         
100         
101         
102         public static Reflection.Method findMethod(String pack, RocketComponent component, 
103                         String method, Class<?>...params) {
104                 return findMethod(pack,component.getClass(),"",method,params);
105         }
106         
107         
108         public static Reflection.Method findMethod(String pack, RocketComponent component, 
109                         String suffix, String method, Class<?>... params) {
110                 return findMethod(pack, component.getClass(), suffix, method, params);
111         }
112
113         
114         public static Reflection.Method findMethod(String pack, 
115                         Class<? extends RocketComponent> componentClass, 
116                         String suffix, String method, Class<?>... params) {
117                 Class<?> currentclass;
118                 String name;
119                 
120                 currentclass = componentClass;
121                 while ((currentclass != null) && (currentclass != Object.class)) {
122                         name = currentclass.getCanonicalName();
123                         if (name.lastIndexOf('.')>=0)
124                                 name = name.substring(name.lastIndexOf(".")+1);
125                         name = pack + "." + name + suffix;
126                         
127                         try {
128                                 Class<?> c = Class.forName(name);
129                                 java.lang.reflect.Method m = c.getMethod(method,params);
130                                 return new Reflection.Method(m);
131                         } catch (ClassNotFoundException ignore) {
132                         } catch (NoSuchMethodException ignore) {
133                         }
134
135                         currentclass = currentclass.getSuperclass();
136                 }
137                 return null;
138         }
139         
140         
141         public static Object construct(String pack, RocketComponent component, String suffix,
142                         Object... params) {
143                 
144                 Class<?> currentclass;
145                 String name;
146                 
147                 currentclass = component.getClass();
148                 while ((currentclass != null) && (currentclass != Object.class)) {
149                         name = currentclass.getCanonicalName();
150                         if (name.lastIndexOf('.')>=0)
151                                 name = name.substring(name.lastIndexOf(".")+1);
152                         name = pack + "." + name + suffix;
153                         
154                         try {
155                                 Class<?> c = Class.forName(name);
156                                 Class<?>[] paramClasses = new Class<?>[params.length];
157                                 for (int i=0; i < params.length; i++) {
158                                         paramClasses[i] = params[i].getClass();
159                                 }
160                                 
161                                 // Constructors must be searched manually.  Why?!
162                                 main: for (Constructor<?> constructor: c.getConstructors()) {
163                                         Class<?>[] parameterTypes = constructor.getParameterTypes();
164                                         if (params.length != parameterTypes.length)
165                                                 continue;
166                                         for (int i=0; i < params.length; i++) {
167                                                 if (!parameterTypes[i].isInstance(params[i])) 
168                                                         continue main;
169                                         }
170                                         // Matching constructor found
171                                         return constructor.newInstance(params);
172                                 }
173                         } catch (ClassNotFoundException ignore) {
174                         } catch (IllegalArgumentException e) {
175                                 throw new BugException("Construction of "+name+" failed",e);
176                         } catch (InstantiationException e) {
177                                 throw new BugException("Construction of "+name+" failed",e);
178                         } catch (IllegalAccessException e) {
179                                 throw new BugException("Construction of "+name+" failed",e);
180                         } catch (InvocationTargetException e) {
181                                 throw Reflection.handleWrappedException(e);
182                         }
183
184                         currentclass = currentclass.getSuperclass();
185                 }
186                 throw new BugException("Suitable constructor for component "+component+ 
187                                 " not found");
188         }
189 }