create changelog entry
[debian/openrocket] / core / 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          * Find a method from the rocket component classes.
94          * Throws an exception if method not found.
95          */
96         public static Reflection.Method findMethod(
97                         Class<? extends RocketComponent> componentClass,
98                         String method, Class<?>... params) {
99                 Reflection.Method m = findMethod(ROCKETCOMPONENT_PACKAGE, componentClass,
100                                 "", method, params);
101                 if (m == null) {
102                         throw new BugException("Could not find method for componentClass="
103                                         + componentClass + " method=" + method);
104                 }
105                 return m;
106         }
107         
108         
109
110         public static Reflection.Method findMethod(String pack, RocketComponent component,
111                         String method, Class<?>... params) {
112                 return findMethod(pack, component.getClass(), "", method, params);
113         }
114         
115         
116         public static Reflection.Method findMethod(String pack, RocketComponent component,
117                         String suffix, String method, Class<?>... params) {
118                 return findMethod(pack, component.getClass(), suffix, method, params);
119         }
120         
121         
122         public static Reflection.Method findMethod(String pack,
123                         Class<? extends RocketComponent> componentClass,
124                         String suffix, String method, Class<?>... params) {
125                 Class<?> currentclass;
126                 String name;
127                 
128                 currentclass = componentClass;
129                 while ((currentclass != null) && (currentclass != Object.class)) {
130                         name = currentclass.getCanonicalName();
131                         if (name.lastIndexOf('.') >= 0)
132                                 name = name.substring(name.lastIndexOf(".") + 1);
133                         name = pack + "." + name + suffix;
134                         
135                         try {
136                                 Class<?> c = Class.forName(name);
137                                 java.lang.reflect.Method m = c.getMethod(method, params);
138                                 return new Reflection.Method(m);
139                         } catch (ClassNotFoundException ignore) {
140                         } catch (NoSuchMethodException ignore) {
141                         }
142                         
143                         currentclass = currentclass.getSuperclass();
144                 }
145                 return null;
146         }
147         
148         
149         public static Object construct(String pack, RocketComponent component, String suffix,
150                         Object... params) {
151                 
152                 Class<?> currentclass;
153                 String name;
154                 
155                 currentclass = component.getClass();
156                 while ((currentclass != null) && (currentclass != Object.class)) {
157                         name = currentclass.getCanonicalName();
158                         if (name.lastIndexOf('.') >= 0)
159                                 name = name.substring(name.lastIndexOf(".") + 1);
160                         name = pack + "." + name + suffix;
161                         
162                         try {
163                                 Class<?> c = Class.forName(name);
164                                 Class<?>[] paramClasses = new Class<?>[params.length];
165                                 for (int i = 0; i < params.length; i++) {
166                                         paramClasses[i] = params[i].getClass();
167                                 }
168                                 
169                                 // Constructors must be searched manually.  Why?!
170                                 main: for (Constructor<?> constructor : c.getConstructors()) {
171                                         Class<?>[] parameterTypes = constructor.getParameterTypes();
172                                         if (params.length != parameterTypes.length)
173                                                 continue;
174                                         for (int i = 0; i < params.length; i++) {
175                                                 if (!parameterTypes[i].isInstance(params[i]))
176                                                         continue main;
177                                         }
178                                         // Matching constructor found
179                                         return constructor.newInstance(params);
180                                 }
181                         } catch (ClassNotFoundException ignore) {
182                         } catch (IllegalArgumentException e) {
183                                 throw new BugException("Construction of " + name + " failed", e);
184                         } catch (InstantiationException e) {
185                                 throw new BugException("Construction of " + name + " failed", e);
186                         } catch (IllegalAccessException e) {
187                                 throw new BugException("Construction of " + name + " failed", e);
188                         } catch (InvocationTargetException e) {
189                                 throw Reflection.handleWrappedException(e);
190                         }
191                         
192                         currentclass = currentclass.getSuperclass();
193                 }
194                 throw new BugException("Suitable constructor for component " + component +
195                                 " not found");
196         }
197 }