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