1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.jexl.util.introspection;
18
19 import org.apache.commons.jexl.util.ArrayIterator;
20 import org.apache.commons.jexl.util.EnumerationIterator;
21 import org.apache.commons.jexl.util.AbstractExecutor;
22 import org.apache.commons.jexl.util.GetExecutor;
23 import org.apache.commons.jexl.util.BooleanPropertyExecutor;
24 import org.apache.commons.jexl.util.PropertyExecutor;
25 import org.apache.commons.logging.Log;
26
27 import java.lang.reflect.Method;
28 import java.lang.reflect.InvocationTargetException;
29 import java.util.Iterator;
30 import java.util.Collection;
31 import java.util.Map;
32 import java.util.Enumeration;
33 import java.util.ArrayList;
34
35 /***
36 * Implementation of Uberspect to provide the default introspective
37 * functionality of Velocity.
38 *
39 * @since 1.0
40 * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
41 * @version $Id: UberspectImpl.java 398509 2006-05-01 03:34:35Z dion $
42 */
43 public class UberspectImpl implements Uberspect, UberspectLoggable {
44 /*** index of the first character of the property. */
45 private static final int PROPERTY_START_INDEX = 3;
46
47 /***
48 * Our runtime logger.
49 */
50 private Log rlog;
51
52 /***
53 * the default Velocity introspector.
54 */
55 private static Introspector introspector;
56
57 /***
58 * init - does nothing - we need to have setRuntimeLogger called before
59 * getting our introspector, as the default vel introspector depends upon
60 * it.
61 * @throws Exception on any error.
62 */
63 public void init() throws Exception {
64 }
65
66 /***
67 * Sets the runtime logger - this must be called before anything else
68 * besides init() as to get the logger. Makes the pull model appealing...
69 * @param runtimeLogger service to use for logging.
70 */
71 public void setRuntimeLogger(Log runtimeLogger) {
72 rlog = runtimeLogger;
73 introspector = new Introspector(rlog);
74 }
75
76 /***
77 * {@inheritDoc}
78 */
79 public Iterator getIterator(Object obj, Info i) throws Exception {
80 if (obj.getClass().isArray()) {
81 return new ArrayIterator(obj);
82 } else if (obj instanceof Collection) {
83 return ((Collection) obj).iterator();
84 } else if (obj instanceof Map) {
85 return ((Map) obj).values().iterator();
86 } else if (obj instanceof Iterator) {
87 rlog.warn("Warning! The iterative " + " is an Iterator in the #foreach() loop at [" + i.getLine() + ","
88 + i.getColumn() + "]" + " in template " + i.getTemplateName() + ". Because it's not resetable,"
89 + " if used in more than once, this may lead to" + " unexpected results.");
90
91 return ((Iterator) obj);
92 } else if (obj instanceof Enumeration) {
93 rlog.warn("Warning! The iterative " + " is an Enumeration in the #foreach() loop at [" + i.getLine() + ","
94 + i.getColumn() + "]" + " in template " + i.getTemplateName() + ". Because it's not resetable,"
95 + " if used in more than once, this may lead to" + " unexpected results.");
96
97 return new EnumerationIterator((Enumeration) obj);
98 }
99
100
101 rlog.warn("Could not determine type of iterator in " + "#foreach loop " + " at [" + i.getLine() + ","
102 + i.getColumn() + "]" + " in template " + i.getTemplateName());
103
104 return null;
105 }
106
107 /***
108 * {@inheritDoc}
109 */
110 public VelMethod getMethod(Object obj, String methodName, Object[] args, Info i) throws Exception {
111 if (obj == null) {
112 return null;
113 }
114
115 Method m = introspector.getMethod(obj.getClass(), methodName, args);
116 if (m == null && obj instanceof Class) {
117 m = introspector.getMethod((Class) obj, methodName, args);
118 }
119
120 return (m == null) ? null : new VelMethodImpl(m);
121 }
122
123 /***
124 * {@inheritDoc}
125 */
126 public VelPropertyGet getPropertyGet(Object obj, String identifier, Info i) throws Exception {
127 AbstractExecutor executor;
128
129 Class claz = obj.getClass();
130
131
132
133
134
135 executor = new PropertyExecutor(rlog, introspector, claz, identifier);
136
137
138
139
140
141 if (!executor.isAlive()) {
142 executor = new BooleanPropertyExecutor(rlog, introspector, claz, identifier);
143 }
144
145
146
147
148
149 if (!executor.isAlive()) {
150 executor = new GetExecutor(rlog, introspector, claz, identifier);
151 }
152
153 return (executor == null) ? null : new VelGetterImpl(executor);
154 }
155
156 /***
157 * {@inheritDoc}
158 */
159 public VelPropertySet getPropertySet(Object obj, String identifier, Object arg, Info i) throws Exception {
160 Class claz = obj.getClass();
161
162 VelMethod vm = null;
163 try {
164
165
166
167
168 Object[] params = {arg};
169
170 try {
171 vm = getMethod(obj, "set" + identifier, params, i);
172
173 if (vm == null) {
174 throw new NoSuchMethodException();
175 }
176 } catch (NoSuchMethodException nsme2) {
177 StringBuffer sb = new StringBuffer("set");
178 sb.append(identifier);
179
180 if (Character.isLowerCase(sb.charAt(PROPERTY_START_INDEX))) {
181 sb.setCharAt(PROPERTY_START_INDEX, Character.toUpperCase(sb.charAt(PROPERTY_START_INDEX)));
182 } else {
183 sb.setCharAt(PROPERTY_START_INDEX, Character.toLowerCase(sb.charAt(PROPERTY_START_INDEX)));
184 }
185
186 vm = getMethod(obj, sb.toString(), params, i);
187
188 if (vm == null) {
189 throw new NoSuchMethodException();
190 }
191 }
192 } catch (NoSuchMethodException nsme) {
193
194
195
196
197 if (Map.class.isAssignableFrom(claz)) {
198 Object[] params = {new Object(), new Object()};
199
200 vm = getMethod(obj, "put", params, i);
201
202 if (vm != null) {
203 return new VelSetterImpl(vm, identifier);
204 }
205 }
206 }
207
208 return (vm == null) ? null : new VelSetterImpl(vm);
209 }
210
211 /***
212 * An implementation of {@link VelMethod}.
213 */
214 public class VelMethodImpl implements VelMethod {
215 /*** the method. */
216 protected Method method = null;
217 /***
218 * Create a new instance.
219 *
220 * @param m the method.
221 */
222 public VelMethodImpl(Method m) {
223 method = m;
224 }
225
226 /***
227 * {@inheritDoc}
228 */
229 public Object invoke(Object o, Object[] params) throws Exception {
230 try {
231 return method.invoke(o, params);
232 } catch (InvocationTargetException e) {
233 final Throwable t = e.getTargetException();
234
235 if (t instanceof Exception) {
236 throw (Exception) t;
237 } else if (t instanceof Error) {
238 throw (Error) t;
239 } else {
240 throw e;
241 }
242 }
243 }
244
245 /***
246 * {@inheritDoc}
247 */
248 public boolean isCacheable() {
249 return true;
250 }
251
252 /***
253 * {@inheritDoc}
254 */
255 public String getMethodName() {
256 return method.getName();
257 }
258
259 /***
260 * {@inheritDoc}
261 */
262 public Class getReturnType() {
263 return method.getReturnType();
264 }
265 }
266
267 /***
268 * {@inheritDoc}
269 */
270 public class VelGetterImpl implements VelPropertyGet {
271 /*** executor for performing the get. */
272 protected AbstractExecutor ae = null;
273
274 /***
275 * Create the getter using an {@link AbstractExecutor} to
276 * do the work.
277 * @param exec the executor.
278 */
279 public VelGetterImpl(AbstractExecutor exec) {
280 ae = exec;
281 }
282
283 /***
284 * {@inheritDoc}
285 */
286 public Object invoke(Object o) throws Exception {
287 return ae.execute(o);
288 }
289
290 /***
291 * {@inheritDoc}
292 */
293 public boolean isCacheable() {
294 return true;
295 }
296
297 /***
298 * {@inheritDoc}
299 */
300 public String getMethodName() {
301 return ae.getMethod().getName();
302 }
303 }
304
305 /***
306 * {@inheritDoc}
307 */
308 public class VelSetterImpl implements VelPropertySet {
309 /*** the method to call. */
310 protected VelMethod vm = null;
311 /*** the key for indexed and other properties. */
312 protected String putKey = null;
313
314 /***
315 * Create an instance.
316 * @param velmethod the method to call on set.
317 */
318 public VelSetterImpl(VelMethod velmethod) {
319 this.vm = velmethod;
320 }
321
322 /***
323 * Create an instance.
324 * @param velmethod the method to call on set.
325 * @param key the index or other value passed to a
326 * setProperty(xxx, value) method.
327 */
328 public VelSetterImpl(VelMethod velmethod, String key) {
329 this.vm = velmethod;
330 putKey = key;
331 }
332
333 /*** {@inheritDoc} */
334 public Object invoke(Object o, Object value) throws Exception {
335 ArrayList al = new ArrayList();
336
337 if (putKey == null) {
338 al.add(value);
339 } else {
340 al.add(putKey);
341 al.add(value);
342 }
343
344 return vm.invoke(o, al.toArray());
345 }
346
347 /*** {@inheritDoc} */
348 public boolean isCacheable() {
349 return true;
350 }
351
352 /*** {@inheritDoc} */
353 public String getMethodName() {
354 return vm.getMethodName();
355 }
356
357 }
358 }