1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.xbean.recipe;
19
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.lang.reflect.Constructor;
23 import java.lang.reflect.Method;
24 import java.lang.reflect.Modifier;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.WeakHashMap;
31 import java.util.Arrays;
32
33 import org.objectweb.asm.ClassReader;
34 import org.objectweb.asm.Label;
35 import org.objectweb.asm.MethodVisitor;
36 import org.objectweb.asm.Type;
37 import org.objectweb.asm.commons.EmptyVisitor;
38
39
40
41
42
43
44
45 public class AsmParameterNameLoader implements ParameterNameLoader {
46
47
48
49 private final WeakHashMap<Constructor,List<String>> constructorCache = new WeakHashMap<Constructor,List<String>>();
50
51
52
53
54 private final WeakHashMap<Method,List<String>> methodCache = new WeakHashMap<Method,List<String>>();
55
56
57
58
59
60
61 public List<String> get(Method method) {
62
63 if (methodCache.containsKey(method)) {
64 return methodCache.get(method);
65 }
66
67 Map<Method,List<String>> allMethodParameters = getAllMethodParameters(method.getDeclaringClass(), method.getName());
68 return allMethodParameters.get(method);
69 }
70
71
72
73
74
75
76 public List<String> get(Constructor constructor) {
77
78 if (constructorCache.containsKey(constructor)) {
79 return constructorCache.get(constructor);
80 }
81
82 Map<Constructor,List<String>> allConstructorParameters = getAllConstructorParameters(constructor.getDeclaringClass());
83 return allConstructorParameters.get(constructor);
84 }
85
86
87
88
89
90
91 public Map<Constructor,List<String>> getAllConstructorParameters(Class clazz) {
92
93 List<Constructor> constructors = new ArrayList<Constructor>(Arrays.asList(clazz.getConstructors()));
94 constructors.addAll(Arrays.asList(clazz.getDeclaredConstructors()));
95 if (constructors.isEmpty()) {
96 return Collections.emptyMap();
97 }
98
99
100 if (constructorCache.containsKey(constructors.get(0))) {
101 Map<Constructor,List<String>> constructorParameters = new HashMap<Constructor,List<String>>();
102 for (Constructor constructor : constructors) {
103 constructorParameters.put(constructor, constructorCache.get(constructor));
104 }
105 return constructorParameters;
106 }
107
108
109 Map<Constructor,List<String>> constructorParameters = new HashMap<Constructor,List<String>> ();
110 try {
111 ClassReader reader = AsmParameterNameLoader.createClassReader(clazz);
112
113 AsmParameterNameLoader.AllParameterNamesDiscoveringVisitor visitor = new AsmParameterNameLoader.AllParameterNamesDiscoveringVisitor(clazz);
114 reader.accept(visitor, 0);
115
116 Map exceptions = visitor.getExceptions();
117 if (exceptions.size() == 1) {
118 throw new RuntimeException((Exception)exceptions.values().iterator().next());
119 }
120 if (!exceptions.isEmpty()) {
121 throw new RuntimeException(exceptions.toString());
122 }
123
124 constructorParameters = visitor.getConstructorParameters();
125 } catch (IOException ex) {
126 }
127
128
129 for (Constructor constructor : constructors) {
130 constructorCache.put(constructor, constructorParameters.get(constructor));
131 }
132 return constructorParameters;
133 }
134
135
136
137
138
139
140
141 public Map<Method,List<String>> getAllMethodParameters(Class clazz, String methodName) {
142
143 Method[] methods = getMethods(clazz, methodName);
144 if (methods.length == 0) {
145 return Collections.emptyMap();
146 }
147
148
149 if (methodCache.containsKey(methods[0])) {
150 Map<Method,List<String>> methodParameters = new HashMap<Method,List<String>>();
151 for (Method method : methods) {
152 methodParameters.put(method, methodCache.get(method));
153 }
154 return methodParameters;
155 }
156
157
158 Map<Method,List<String>> methodParameters = new HashMap<Method,List<String>>();
159 try {
160 ClassReader reader = AsmParameterNameLoader.createClassReader(clazz);
161
162 AsmParameterNameLoader.AllParameterNamesDiscoveringVisitor visitor = new AsmParameterNameLoader.AllParameterNamesDiscoveringVisitor(clazz, methodName);
163 reader.accept(visitor, 0);
164
165 Map exceptions = visitor.getExceptions();
166 if (exceptions.size() == 1) {
167 throw new RuntimeException((Exception)exceptions.values().iterator().next());
168 }
169 if (!exceptions.isEmpty()) {
170 throw new RuntimeException(exceptions.toString());
171 }
172
173 methodParameters = visitor.getMethodParameters();
174 } catch (IOException ex) {
175 }
176
177
178 for (Method method : methods) {
179 methodCache.put(method, methodParameters.get(method));
180 }
181 return methodParameters;
182 }
183
184 private Method[] getMethods(Class clazz, String methodName) {
185 List<Method> methods = new ArrayList<Method>(Arrays.asList(clazz.getMethods()));
186 methods.addAll(Arrays.asList(clazz.getDeclaredMethods()));
187 List<Method> matchingMethod = new ArrayList<Method>(methods.size());
188 for (Method method : methods) {
189 if (method.getName().equals(methodName)) {
190 matchingMethod.add(method);
191 }
192 }
193 return matchingMethod.toArray(new Method[matchingMethod.size()]);
194 }
195
196 private static ClassReader createClassReader(Class declaringClass) throws IOException {
197 InputStream in = null;
198 try {
199 ClassLoader classLoader = declaringClass.getClassLoader();
200 in = classLoader.getResourceAsStream(declaringClass.getName().replace('.', '/') + ".class");
201 ClassReader reader = new ClassReader(in);
202 return reader;
203 } finally {
204 if (in != null) {
205 try {
206 in.close();
207 } catch (IOException ignored) {
208 }
209 }
210 }
211 }
212
213 private static class AllParameterNamesDiscoveringVisitor extends EmptyVisitor {
214 private final Map<Constructor,List<String>> constructorParameters = new HashMap<Constructor,List<String>>();
215 private final Map<Method,List<String>> methodParameters = new HashMap<Method,List<String>>();
216 private final Map<String,Exception> exceptions = new HashMap<String,Exception>();
217 private final String methodName;
218 private final Map<String,Method> methodMap = new HashMap<String,Method>();
219 private final Map<String,Constructor> constructorMap = new HashMap<String,Constructor>();
220
221 public AllParameterNamesDiscoveringVisitor(Class type, String methodName) {
222 this.methodName = methodName;
223
224 List<Method> methods = new ArrayList<Method>(Arrays.asList(type.getMethods()));
225 methods.addAll(Arrays.asList(type.getDeclaredMethods()));
226 for (Method method : methods) {
227 if (method.getName().equals(methodName)) {
228 methodMap.put(Type.getMethodDescriptor(method), method);
229 }
230 }
231 }
232
233 public AllParameterNamesDiscoveringVisitor(Class type) {
234 this.methodName = "<init>";
235
236 List<Constructor> constructors = new ArrayList<Constructor>(Arrays.asList(type.getConstructors()));
237 constructors.addAll(Arrays.asList(type.getDeclaredConstructors()));
238 for (Constructor constructor : constructors) {
239 Type[] types = new Type[constructor.getParameterTypes().length];
240 for (int j = 0; j < types.length; j++) {
241 types[j] = Type.getType(constructor.getParameterTypes()[j]);
242 }
243 constructorMap.put(Type.getMethodDescriptor(Type.VOID_TYPE, types), constructor);
244 }
245 }
246
247 public Map<Constructor, List<String>> getConstructorParameters() {
248 return constructorParameters;
249 }
250
251 public Map<Method, List<String>> getMethodParameters() {
252 return methodParameters;
253 }
254
255 public Map<String,Exception> getExceptions() {
256 return exceptions;
257 }
258
259 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
260 if (!name.equals(this.methodName)) {
261 return null;
262 }
263
264 try {
265 final List<String> parameterNames;
266 final boolean isStaticMethod;
267
268 if (methodName.equals("<init>")) {
269 Constructor constructor = constructorMap.get(desc);
270 if (constructor == null) {
271 return null;
272 }
273 parameterNames = new ArrayList<String>(constructor.getParameterTypes().length);
274 parameterNames.addAll(Collections.<String>nCopies(constructor.getParameterTypes().length, null));
275 constructorParameters.put(constructor, parameterNames);
276 isStaticMethod = false;
277 } else {
278 Method method = methodMap.get(desc);
279 if (method == null) {
280 return null;
281 }
282 parameterNames = new ArrayList<String>(method.getParameterTypes().length);
283 parameterNames.addAll(Collections.<String>nCopies(method.getParameterTypes().length, null));
284 methodParameters.put(method, parameterNames);
285 isStaticMethod = Modifier.isStatic(method.getModifiers());
286 }
287
288 return new EmptyVisitor() {
289
290 public void visitLocalVariable(String name, String description, String signature, Label start, Label end, int index) {
291 if (isStaticMethod) {
292 parameterNames.set(index, name);
293 } else if (index > 0) {
294
295 parameterNames.set(index - 1, name);
296 }
297 }
298 };
299 } catch (Exception e) {
300 this.exceptions.put(signature, e);
301 }
302 return null;
303 }
304 }
305 }