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.Array;
23 import java.lang.reflect.Method;
24 import java.util.Collection;
25 import java.util.Enumeration;
26 import java.util.Iterator;
27 import java.util.Map;
28
29 import org.apache.velocity.runtime.RuntimeLogger;
30 import org.apache.velocity.runtime.log.Log;
31 import org.apache.velocity.runtime.log.RuntimeLoggerLog;
32 import org.apache.velocity.runtime.parser.node.AbstractExecutor;
33 import org.apache.velocity.runtime.parser.node.BooleanPropertyExecutor;
34 import org.apache.velocity.runtime.parser.node.GetExecutor;
35 import org.apache.velocity.runtime.parser.node.MapGetExecutor;
36 import org.apache.velocity.runtime.parser.node.MapSetExecutor;
37 import org.apache.velocity.runtime.parser.node.PropertyExecutor;
38 import org.apache.velocity.runtime.parser.node.PutExecutor;
39 import org.apache.velocity.runtime.parser.node.SetExecutor;
40 import org.apache.velocity.runtime.parser.node.SetPropertyExecutor;
41 import org.apache.velocity.util.ArrayIterator;
42 import org.apache.velocity.util.ArrayListWrapper;
43 import org.apache.velocity.util.EnumerationIterator;
44
45
46
47
48
49
50
51
52
53 public class UberspectImpl implements Uberspect, UberspectLoggable
54 {
55
56
57
58 protected Log log;
59
60
61
62
63 protected Introspector introspector;
64
65
66
67
68
69
70 public void init() throws Exception
71 {
72 introspector = new Introspector(log);
73 }
74
75
76
77
78
79
80
81
82 public void setLog(Log log)
83 {
84 this.log = log;
85 }
86
87
88
89
90
91 public void setRuntimeLogger(RuntimeLogger runtimeLogger)
92 {
93
94
95 setLog(new RuntimeLoggerLog(runtimeLogger));
96 }
97
98
99
100
101
102
103
104
105
106
107 public Iterator getIterator(Object obj, Info i)
108 throws Exception
109 {
110 if (obj.getClass().isArray())
111 {
112 return new ArrayIterator(obj);
113 }
114 else if (obj instanceof Collection)
115 {
116 return ((Collection) obj).iterator();
117 }
118 else if (obj instanceof Map)
119 {
120 return ((Map) obj).values().iterator();
121 }
122 else if (obj instanceof Iterator)
123 {
124 if (log.isDebugEnabled())
125 {
126 log.debug("The iterative object in the #foreach() loop at " +
127 i + " is of type java.util.Iterator. Because " +
128 "it is not resettable, if used in more than once it " +
129 "may lead to unexpected results.");
130 }
131 return ((Iterator) obj);
132 }
133 else if (obj instanceof Enumeration)
134 {
135 if (log.isDebugEnabled())
136 {
137 log.debug("The iterative object in the #foreach() loop at " +
138 i + " is of type java.util.Enumeration. Because " +
139 "it is not resettable, if used in more than once it " +
140 "may lead to unexpected results.");
141 }
142 return new EnumerationIterator((Enumeration) obj);
143 }
144 else
145 {
146
147
148
149 Class type = obj.getClass();
150 try
151 {
152 Method iter = type.getMethod("iterator", null);
153 Class returns = iter.getReturnType();
154 if (Iterator.class.isAssignableFrom(returns))
155 {
156 return (Iterator)iter.invoke(obj, null);
157 }
158 else
159 {
160 log.debug("iterator() method of reference in #foreach loop at "
161 + i + " does not return a true Iterator.");
162 }
163 }
164 catch (NoSuchMethodException nsme)
165 {
166
167 }
168 }
169
170
171 log.debug("Could not determine type of iterator in #foreach loop at " + i);
172
173 return null;
174 }
175
176
177
178
179
180
181
182
183
184
185 public VelMethod getMethod(Object obj, String methodName, Object[] args, Info i)
186 throws Exception
187 {
188 if (obj == null)
189 {
190 return null;
191 }
192
193 Method m = introspector.getMethod(obj.getClass(), methodName, args);
194 if (m != null)
195 {
196 return new VelMethodImpl(m);
197 }
198
199 Class cls = obj.getClass();
200
201 if (cls.isArray())
202 {
203
204 m = introspector.getMethod(ArrayListWrapper.class, methodName, args);
205 if (m != null)
206 {
207
208
209 return new VelMethodImpl(m, true);
210 }
211 }
212
213 else if (cls == Class.class)
214 {
215 m = introspector.getMethod((Class)obj, methodName, args);
216 if (m != null)
217 {
218 return new VelMethodImpl(m);
219 }
220 }
221 return null;
222 }
223
224
225
226
227
228
229
230
231
232 public VelPropertyGet getPropertyGet(Object obj, String identifier, Info i)
233 throws Exception
234 {
235 if (obj == null)
236 {
237 return null;
238 }
239
240 Class claz = obj.getClass();
241
242
243
244
245
246 AbstractExecutor executor = new PropertyExecutor(log, introspector, claz, identifier);
247
248
249
250
251 if (!executor.isAlive())
252 {
253 executor = new MapGetExecutor(log, claz, identifier);
254 }
255
256
257
258
259
260 if (!executor.isAlive())
261 {
262 executor = new GetExecutor(log, introspector, claz, identifier);
263 }
264
265
266
267
268
269 if (!executor.isAlive())
270 {
271 executor = new BooleanPropertyExecutor(log, introspector, claz,
272 identifier);
273 }
274
275 return (executor.isAlive()) ? new VelGetterImpl(executor) : null;
276 }
277
278
279
280
281
282
283
284
285
286
287 public VelPropertySet getPropertySet(Object obj, String identifier,
288 Object arg, Info i)
289 throws Exception
290 {
291 if (obj == null)
292 {
293 return null;
294 }
295
296 Class claz = obj.getClass();
297
298
299
300
301
302 SetExecutor executor = new SetPropertyExecutor(log, introspector, claz, identifier, arg);
303
304
305
306
307 if (!executor.isAlive()) {
308 executor = new MapSetExecutor(log, claz, identifier);
309 }
310
311
312
313
314
315 if (!executor.isAlive())
316 {
317 executor = new PutExecutor(log, introspector, claz, arg, identifier);
318 }
319
320 return (executor.isAlive()) ? new VelSetterImpl(executor) : null;
321 }
322
323
324
325
326 public static class VelMethodImpl implements VelMethod
327 {
328 final Method method;
329 Boolean isVarArg;
330 boolean wrapArray;
331
332
333
334
335 public VelMethodImpl(Method m)
336 {
337 this(m, false);
338 }
339
340
341
342
343 public VelMethodImpl(Method method, boolean wrapArray)
344 {
345 this.method = method;
346 this.wrapArray = wrapArray;
347 }
348
349 private VelMethodImpl()
350 {
351 method = null;
352 }
353
354
355
356
357 public Object invoke(Object o, Object[] actual)
358 throws Exception
359 {
360
361 if (wrapArray)
362 {
363 o = new ArrayListWrapper(o);
364 }
365
366 if (isVarArg())
367 {
368 Class[] formal = method.getParameterTypes();
369 int index = formal.length - 1;
370 if (actual.length >= index)
371 {
372 Class type = formal[index].getComponentType();
373 actual = handleVarArg(type, index, actual);
374 }
375 }
376
377
378 return doInvoke(o, actual);
379 }
380
381
382
383
384
385
386
387 protected Object doInvoke(Object o, Object[] actual) throws Exception
388 {
389 return method.invoke(o, actual);
390 }
391
392
393
394
395
396 public boolean isVarArg()
397 {
398 if (isVarArg == null)
399 {
400 Class[] formal = method.getParameterTypes();
401 if (formal == null || formal.length == 0)
402 {
403 this.isVarArg = Boolean.FALSE;
404 }
405 else
406 {
407 Class last = formal[formal.length - 1];
408
409
410 this.isVarArg = Boolean.valueOf(last.isArray());
411 }
412 }
413 return isVarArg.booleanValue();
414 }
415
416
417
418
419
420
421
422
423
424
425
426 private Object[] handleVarArg(final Class type,
427 final int index,
428 Object[] actual)
429 {
430
431 if (actual.length == index)
432 {
433
434 actual = new Object[] { Array.newInstance(type, 0) };
435 }
436
437 else if (actual.length == index + 1 && actual[index] != null)
438 {
439
440 Class argClass = actual[index].getClass();
441 if (!argClass.isArray() &&
442 IntrospectionUtils.isMethodInvocationConvertible(type,
443 argClass,
444 false))
445 {
446
447 Object lastActual = Array.newInstance(type, 1);
448 Array.set(lastActual, 0, actual[index]);
449 actual[index] = lastActual;
450 }
451 }
452
453 else if (actual.length > index + 1)
454 {
455
456 int size = actual.length - index;
457 Object lastActual = Array.newInstance(type, size);
458 for (int i = 0; i < size; i++)
459 {
460 Array.set(lastActual, i, actual[index + i]);
461 }
462
463
464 Object[] newActual = new Object[index + 1];
465 for (int i = 0; i < index; i++)
466 {
467 newActual[i] = actual[i];
468 }
469 newActual[index] = lastActual;
470
471
472 actual = newActual;
473 }
474 return actual;
475 }
476
477
478
479
480 public boolean isCacheable()
481 {
482 return true;
483 }
484
485
486
487
488 public String getMethodName()
489 {
490 return method.getName();
491 }
492
493
494
495
496 public Class getReturnType()
497 {
498 return method.getReturnType();
499 }
500 }
501
502
503
504
505
506 public static class VelGetterImpl implements VelPropertyGet
507 {
508 final AbstractExecutor getExecutor;
509
510
511
512
513 public VelGetterImpl(AbstractExecutor exec)
514 {
515 getExecutor = exec;
516 }
517
518 private VelGetterImpl()
519 {
520 getExecutor = null;
521 }
522
523
524
525
526 public Object invoke(Object o)
527 throws Exception
528 {
529 return getExecutor.execute(o);
530 }
531
532
533
534
535 public boolean isCacheable()
536 {
537 return true;
538 }
539
540
541
542
543 public String getMethodName()
544 {
545 return getExecutor.isAlive() ? getExecutor.getMethod().getName() : null;
546 }
547 }
548
549
550
551
552 public static class VelSetterImpl implements VelPropertySet
553 {
554 private final SetExecutor setExecutor;
555
556
557
558
559 public VelSetterImpl(final SetExecutor setExecutor)
560 {
561 this.setExecutor = setExecutor;
562 }
563
564 private VelSetterImpl()
565 {
566 setExecutor = null;
567 }
568
569
570
571
572
573
574
575
576
577 public Object invoke(final Object o, final Object value)
578 throws Exception
579 {
580 return setExecutor.execute(o, value);
581 }
582
583
584
585
586 public boolean isCacheable()
587 {
588 return true;
589 }
590
591
592
593
594 public String getMethodName()
595 {
596 return setExecutor.isAlive() ? setExecutor.getMethod().getName() : null;
597 }
598 }
599 }