1 package org.apache.velocity.util.introspection;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.lang.reflect.Method;
23 import java.lang.reflect.Modifier;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29
30 import org.apache.velocity.runtime.log.Log;
31
32
33
34
35
36
37
38
39
40
41
42
43
44 public class ClassMap
45 {
46
47 private static final boolean debugReflection = false;
48
49
50 private final Log log;
51
52
53
54
55
56 private final Class clazz;
57
58 private final MethodCache methodCache;
59
60
61
62
63
64 public ClassMap(final Class clazz, final Log log)
65 {
66 this.clazz = clazz;
67 this.log = log;
68
69 if (debugReflection && log.isDebugEnabled())
70 {
71 log.debug("=================================================================");
72 log.debug("== Class: " + clazz);
73 }
74
75 methodCache = new MethodCache(log);
76
77 populateMethodCache();
78
79 if (debugReflection && log.isDebugEnabled())
80 {
81 log.debug("=================================================================");
82 }
83 }
84
85
86
87
88
89
90 public Class getCachedClass()
91 {
92 return clazz;
93 }
94
95
96
97
98
99
100
101
102
103 public Method findMethod(final String name, final Object[] params)
104 throws MethodMap.AmbiguousException
105 {
106 return methodCache.get(name, params);
107 }
108
109
110
111
112
113
114 private void populateMethodCache()
115 {
116
117
118
119
120
121
122
123
124
125
126
127
128
129 List classesToReflect = new ArrayList();
130
131
132 for (Class classToReflect = getCachedClass(); classToReflect != null ; classToReflect = classToReflect.getSuperclass())
133 {
134 if (Modifier.isPublic(classToReflect.getModifiers()))
135 {
136 classesToReflect.add(classToReflect);
137 if (debugReflection && log.isDebugEnabled())
138 {
139 log.debug("Adding " + classToReflect + " for reflection");
140 }
141 }
142 Class [] interfaces = classToReflect.getInterfaces();
143 for (int i = 0; i < interfaces.length; i++)
144 {
145 if (Modifier.isPublic(interfaces[i].getModifiers()))
146 {
147 classesToReflect.add(interfaces[i]);
148 if (debugReflection && log.isDebugEnabled())
149 {
150 log.debug("Adding " + interfaces[i] + " for reflection");
151 }
152 }
153 }
154 }
155
156 for (Iterator it = classesToReflect.iterator(); it.hasNext(); )
157 {
158 Class classToReflect = (Class) it.next();
159 if (debugReflection && log.isDebugEnabled())
160 {
161 log.debug("Reflecting " + classToReflect);
162 }
163
164
165 try
166 {
167 Method[] methods = classToReflect.getMethods();
168
169 for (int i = 0; i < methods.length; i++)
170 {
171
172
173 int modifiers = methods[i].getModifiers();
174 if (Modifier.isPublic(modifiers))
175 {
176
177
178
179
180 if (classToReflect.isInterface() || !Modifier.isAbstract(modifiers))
181 {
182 methodCache.put(methods[i]);
183 }
184 }
185 }
186 }
187 catch (SecurityException se)
188 {
189 if (log.isDebugEnabled())
190 {
191 log.debug("While accessing methods of " + classToReflect + ": ", se);
192 }
193 }
194 }
195 }
196
197
198
199
200
201
202
203 private static final class MethodCache
204 {
205 private static final class CacheMiss { }
206
207 private static final CacheMiss CACHE_MISS = new CacheMiss();
208
209 private static final Object OBJECT = new Object();
210
211 private static final Map convertPrimitives = new HashMap();
212
213 static
214 {
215 convertPrimitives.put(Boolean.TYPE, Boolean.class.getName());
216 convertPrimitives.put(Byte.TYPE, Byte.class.getName());
217 convertPrimitives.put(Character.TYPE, Character.class.getName());
218 convertPrimitives.put(Double.TYPE, Double.class.getName());
219 convertPrimitives.put(Float.TYPE, Float.class.getName());
220 convertPrimitives.put(Integer.TYPE, Integer.class.getName());
221 convertPrimitives.put(Long.TYPE, Long.class.getName());
222 convertPrimitives.put(Short.TYPE, Short.class.getName());
223 }
224
225
226 private final Log log;
227
228
229
230
231
232 private final Map cache = new HashMap();
233
234
235 private final MethodMap methodMap = new MethodMap();
236
237 private MethodCache(Log log)
238 {
239 this.log = log;
240 }
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258 public synchronized Method get(final String name, final Object [] params)
259 throws MethodMap.AmbiguousException
260 {
261 String methodKey = makeMethodKey(name, params);
262
263 Object cacheEntry = cache.get(methodKey);
264
265
266 if (cacheEntry == CACHE_MISS)
267 {
268 return null;
269 }
270
271 if (cacheEntry == null)
272 {
273 try
274 {
275
276 cacheEntry = methodMap.find(name, params);
277 }
278 catch(MethodMap.AmbiguousException ae)
279 {
280
281
282
283 cache.put(methodKey, CACHE_MISS);
284 throw ae;
285 }
286
287 cache.put(methodKey,
288 (cacheEntry != null) ? cacheEntry : CACHE_MISS);
289 }
290
291
292
293 return (Method) cacheEntry;
294 }
295
296 public synchronized void put(Method method)
297 {
298 String methodKey = makeMethodKey(method);
299
300
301
302
303
304 if (cache.get(methodKey) == null)
305 {
306 cache.put(methodKey, method);
307 methodMap.add(method);
308 if (debugReflection && log.isDebugEnabled())
309 {
310 log.debug("Adding " + method);
311 }
312 }
313 }
314
315
316
317
318
319
320
321
322
323 private String makeMethodKey(final Method method)
324 {
325 Class[] parameterTypes = method.getParameterTypes();
326
327 StringBuffer methodKey = new StringBuffer(method.getName());
328
329 for (int j = 0; j < parameterTypes.length; j++)
330 {
331
332
333
334
335
336
337
338
339
340
341 if (parameterTypes[j].isPrimitive())
342 {
343 methodKey.append((String) convertPrimitives.get(parameterTypes[j]));
344 }
345 else
346 {
347 methodKey.append(parameterTypes[j].getName());
348 }
349 }
350
351 return methodKey.toString();
352 }
353
354 private String makeMethodKey(String method, Object[] params)
355 {
356 StringBuffer methodKey = new StringBuffer().append(method);
357
358 for (int j = 0; j < params.length; j++)
359 {
360 Object arg = params[j];
361
362 if (arg == null)
363 {
364 arg = OBJECT;
365 }
366
367 methodKey.append(arg.getClass().getName());
368 }
369
370 return methodKey.toString();
371 }
372 }
373 }