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