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 populateMethodCacheWithInterface(methodCache, interfaces[i]);
137 }
138 }
139
140 return methodCache;
141 }
142
143
144 private void populateMethodCacheWithInterface(MethodCache methodCache, Class iface)
145 {
146 if (Modifier.isPublic(iface.getModifiers()))
147 {
148 populateMethodCacheWith(methodCache, iface);
149 }
150 Class[] supers = iface.getInterfaces();
151 for (int i=0; i < supers.length; i++)
152 {
153 populateMethodCacheWithInterface(methodCache, supers[i]);
154 }
155 }
156
157 private void populateMethodCacheWith(MethodCache methodCache, Class classToReflect)
158 {
159 if (debugReflection && log.isDebugEnabled())
160 {
161 log.debug("Reflecting " + classToReflect);
162 }
163
164 try
165 {
166 Method[] methods = classToReflect.getDeclaredMethods();
167 for (int i = 0; i < methods.length; i++)
168 {
169 int modifiers = methods[i].getModifiers();
170 if (Modifier.isPublic(modifiers))
171 {
172 methodCache.put(methods[i]);
173 }
174 }
175 }
176 catch (SecurityException se)
177 {
178 if (log.isDebugEnabled())
179 {
180 log.debug("While accessing methods of " + classToReflect + ": ", se);
181 }
182 }
183 }
184
185
186
187
188
189
190
191 private static final class MethodCache
192 {
193 private static final Object CACHE_MISS = new Object();
194
195 private static final String NULL_ARG = Object.class.getName();
196
197 private static final Map convertPrimitives = new HashMap();
198
199 static
200 {
201 convertPrimitives.put(Boolean.TYPE, Boolean.class.getName());
202 convertPrimitives.put(Byte.TYPE, Byte.class.getName());
203 convertPrimitives.put(Character.TYPE, Character.class.getName());
204 convertPrimitives.put(Double.TYPE, Double.class.getName());
205 convertPrimitives.put(Float.TYPE, Float.class.getName());
206 convertPrimitives.put(Integer.TYPE, Integer.class.getName());
207 convertPrimitives.put(Long.TYPE, Long.class.getName());
208 convertPrimitives.put(Short.TYPE, Short.class.getName());
209 }
210
211
212 private final Log log;
213
214
215
216
217
218 private final Map cache = new HashMap();
219
220
221 private final MethodMap methodMap = new MethodMap();
222
223 private MethodCache(Log log)
224 {
225 this.log = log;
226 }
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244 public Method get(final String name, final Object [] params)
245 throws MethodMap.AmbiguousException
246 {
247 String methodKey = makeMethodKey(name, params);
248
249 Object cacheEntry = cache.get(methodKey);
250 if (cacheEntry == CACHE_MISS)
251 {
252
253 return null;
254 }
255
256 if (cacheEntry == null)
257 {
258 try
259 {
260
261 cacheEntry = methodMap.find(name, params);
262 }
263 catch(MethodMap.AmbiguousException ae)
264 {
265
266
267
268 cache.put(methodKey, CACHE_MISS);
269 throw ae;
270 }
271
272 cache.put(methodKey,
273 (cacheEntry != null) ? cacheEntry : CACHE_MISS);
274 }
275
276
277 return (Method) cacheEntry;
278 }
279
280 private void put(Method method)
281 {
282 String methodKey = makeMethodKey(method);
283
284
285
286
287
288 if (cache.get(methodKey) == null)
289 {
290 cache.put(methodKey, method);
291 methodMap.add(method);
292 if (debugReflection && log.isDebugEnabled())
293 {
294 log.debug("Adding " + method);
295 }
296 }
297 }
298
299
300
301
302
303
304
305
306
307 private String makeMethodKey(final Method method)
308 {
309 Class[] parameterTypes = method.getParameterTypes();
310 int args = parameterTypes.length;
311 if (args == 0)
312 {
313 return method.getName();
314 }
315
316 StrBuilder methodKey = new StrBuilder((args+1)*16).append(method.getName());
317
318 for (int j = 0; j < args; j++)
319 {
320
321
322
323
324
325
326
327
328
329
330 if (parameterTypes[j].isPrimitive())
331 {
332 methodKey.append((String) convertPrimitives.get(parameterTypes[j]));
333 }
334 else
335 {
336 methodKey.append(parameterTypes[j].getName());
337 }
338 }
339
340 return methodKey.toString();
341 }
342
343 private String makeMethodKey(String method, Object[] params)
344 {
345 int args = params.length;
346 if (args == 0)
347 {
348 return method;
349 }
350
351 StrBuilder methodKey = new StrBuilder((args+1)*16).append(method);
352
353 for (int j = 0; j < args; j++)
354 {
355 Object arg = params[j];
356 if (arg == null)
357 {
358 methodKey.append(NULL_ARG);
359 }
360 else
361 {
362 methodKey.append(arg.getClass().getName());
363 }
364 }
365
366 return methodKey.toString();
367 }
368 }
369 }