1 | package org.apache.velocity.util.introspection; |
2 | |
3 | /* |
4 | * Copyright 2002,2004 The Apache Software Foundation. |
5 | * |
6 | * Licensed under the Apache License, Version 2.0 (the "License"); |
7 | * you may not use this file except in compliance with the License. |
8 | * You may obtain a copy of the License at |
9 | * |
10 | * http://www.apache.org/licenses/LICENSE-2.0 |
11 | * |
12 | * Unless required by applicable law or agreed to in writing, software |
13 | * distributed under the License is distributed on an "AS IS" BASIS, |
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
15 | * See the License for the specific language governing permissions and |
16 | * limitations under the License. |
17 | */ |
18 | |
19 | import org.apache.velocity.util.ArrayIterator; |
20 | import org.apache.velocity.util.EnumerationIterator; |
21 | import org.apache.velocity.runtime.RuntimeServices; |
22 | import org.apache.velocity.runtime.RuntimeLogger; |
23 | import org.apache.velocity.runtime.parser.node.AbstractExecutor; |
24 | import org.apache.velocity.runtime.parser.node.PropertyExecutor; |
25 | import org.apache.velocity.runtime.parser.node.GetExecutor; |
26 | import org.apache.velocity.runtime.parser.node.BooleanPropertyExecutor; |
27 | |
28 | import java.lang.reflect.Method; |
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 | * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a> |
40 | * @version $Id: UberspectImpl.java,v 1.2.4.1 2004/03/03 23:23:08 geirm Exp $ |
41 | */ |
42 | public class UberspectImpl implements Uberspect, UberspectLoggable |
43 | { |
44 | /** |
45 | * Our runtime logger. |
46 | */ |
47 | private RuntimeLogger rlog; |
48 | |
49 | /** |
50 | * the default Velocity introspector |
51 | */ |
52 | private static Introspector introspector; |
53 | |
54 | /** |
55 | * init - does nothing - we need to have setRuntimeLogger |
56 | * called before getting our introspector, as the default |
57 | * vel introspector depends upon it. |
58 | */ |
59 | public void init() |
60 | throws Exception |
61 | { |
62 | } |
63 | |
64 | /** |
65 | * Sets the runtime logger - this must be called before anything |
66 | * else besides init() as to get the logger. Makes the pull |
67 | * model appealing... |
68 | */ |
69 | public void setRuntimeLogger(RuntimeLogger runtimeLogger) |
70 | { |
71 | rlog = runtimeLogger; |
72 | introspector = new Introspector(rlog); |
73 | } |
74 | |
75 | /** |
76 | * To support iteratives - #foreach() |
77 | */ |
78 | public Iterator getIterator(Object obj, Info i) |
79 | throws Exception |
80 | { |
81 | if (obj.getClass().isArray()) |
82 | { |
83 | return new ArrayIterator(obj); |
84 | } |
85 | else if (obj instanceof Collection) |
86 | { |
87 | return ((Collection) obj).iterator(); |
88 | } |
89 | else if (obj instanceof Map) |
90 | { |
91 | return ((Map) obj).values().iterator(); |
92 | } |
93 | else if (obj instanceof Iterator) |
94 | { |
95 | rlog.warn ("Warning! The iterative " |
96 | + " is an Iterator in the #foreach() loop at [" |
97 | + i.getLine() + "," + i.getColumn() + "]" |
98 | + " in template " + i.getTemplateName() |
99 | + ". Because it's not resetable," |
100 | + " if used in more than once, this may lead to" |
101 | + " unexpected results."); |
102 | |
103 | return ((Iterator) obj); |
104 | } |
105 | else if (obj instanceof Enumeration) |
106 | { |
107 | rlog.warn ("Warning! The iterative " |
108 | + " is an Enumeration in the #foreach() loop at [" |
109 | + i.getLine() + "," + i.getColumn() + "]" |
110 | + " in template " + i.getTemplateName() |
111 | + ". Because it's not resetable," |
112 | + " if used in more than once, this may lead to" |
113 | + " unexpected results."); |
114 | |
115 | return new EnumerationIterator((Enumeration) obj); |
116 | } |
117 | |
118 | /* we have no clue what this is */ |
119 | rlog.warn ("Could not determine type of iterator in " |
120 | + "#foreach loop " |
121 | + " at [" + i.getLine() + "," + i.getColumn() + "]" |
122 | + " in template " + i.getTemplateName() ); |
123 | |
124 | return null; |
125 | } |
126 | |
127 | /** |
128 | * Method |
129 | */ |
130 | public VelMethod getMethod(Object obj, String methodName, Object[] args, Info i) |
131 | throws Exception |
132 | { |
133 | if (obj == null) |
134 | return null; |
135 | |
136 | Method m = introspector.getMethod(obj.getClass(), methodName, args); |
137 | |
138 | return (m != null) ? new VelMethodImpl(m) : null; |
139 | } |
140 | |
141 | /** |
142 | * Property getter |
143 | */ |
144 | public VelPropertyGet getPropertyGet(Object obj, String identifier, Info i) |
145 | throws Exception |
146 | { |
147 | AbstractExecutor executor; |
148 | |
149 | Class claz = obj.getClass(); |
150 | |
151 | /* |
152 | * first try for a getFoo() type of property |
153 | * (also getfoo() ) |
154 | */ |
155 | |
156 | executor = new PropertyExecutor(rlog,introspector, claz, identifier); |
157 | |
158 | /* |
159 | * if that didn't work, look for get("foo") |
160 | */ |
161 | |
162 | if (executor.isAlive() == false) |
163 | { |
164 | executor = new GetExecutor(rlog, introspector, claz, identifier); |
165 | } |
166 | |
167 | /* |
168 | * finally, look for boolean isFoo() |
169 | */ |
170 | |
171 | if( executor.isAlive() == false) |
172 | { |
173 | executor = new BooleanPropertyExecutor(rlog, introspector, claz, identifier); |
174 | } |
175 | |
176 | return (executor != null) ? new VelGetterImpl(executor) : null; |
177 | } |
178 | |
179 | /** |
180 | * Property setter |
181 | */ |
182 | public VelPropertySet getPropertySet(Object obj, String identifier, Object arg, Info i) |
183 | throws Exception |
184 | { |
185 | Class claz = obj.getClass(); |
186 | |
187 | VelPropertySet vs = null; |
188 | VelMethod vm = null; |
189 | try |
190 | { |
191 | /* |
192 | * first, we introspect for the set<identifier> setter method |
193 | */ |
194 | |
195 | Object[] params = {arg}; |
196 | |
197 | try |
198 | { |
199 | vm = getMethod(obj, "set" + identifier, params, i); |
200 | |
201 | if (vm == null) |
202 | { |
203 | throw new NoSuchMethodException(); |
204 | } |
205 | } |
206 | catch(NoSuchMethodException nsme2) |
207 | { |
208 | StringBuffer sb = new StringBuffer("set"); |
209 | sb.append(identifier); |
210 | |
211 | if (Character.isLowerCase( sb.charAt(3))) |
212 | { |
213 | sb.setCharAt(3, Character.toUpperCase(sb.charAt(3))); |
214 | } |
215 | else |
216 | { |
217 | sb.setCharAt(3, Character.toLowerCase(sb.charAt(3))); |
218 | } |
219 | |
220 | vm = getMethod(obj, sb.toString(), params, i); |
221 | |
222 | if (vm == null) |
223 | { |
224 | throw new NoSuchMethodException(); |
225 | } |
226 | } |
227 | } |
228 | catch (NoSuchMethodException nsme) |
229 | { |
230 | /* |
231 | * right now, we only support the Map interface |
232 | */ |
233 | |
234 | if (Map.class.isAssignableFrom(claz)) |
235 | { |
236 | Object[] params = {new Object(), new Object()}; |
237 | |
238 | vm = getMethod(obj, "put", params, i); |
239 | |
240 | if (vm!=null) |
241 | return new VelSetterImpl(vm, identifier); |
242 | } |
243 | } |
244 | |
245 | return (vm!=null) ? new VelSetterImpl(vm) : null; |
246 | } |
247 | |
248 | /** |
249 | * Implementation of VelMethod |
250 | */ |
251 | public class VelMethodImpl implements VelMethod |
252 | { |
253 | Method method = null; |
254 | |
255 | public VelMethodImpl(Method m) |
256 | { |
257 | method = m; |
258 | } |
259 | |
260 | private VelMethodImpl() |
261 | { |
262 | } |
263 | |
264 | public Object invoke(Object o, Object[] params) |
265 | throws Exception |
266 | { |
267 | return method.invoke(o, params); |
268 | } |
269 | |
270 | public boolean isCacheable() |
271 | { |
272 | return true; |
273 | } |
274 | |
275 | public String getMethodName() |
276 | { |
277 | return method.getName(); |
278 | } |
279 | |
280 | public Class getReturnType() |
281 | { |
282 | return method.getReturnType(); |
283 | } |
284 | } |
285 | |
286 | public class VelGetterImpl implements VelPropertyGet |
287 | { |
288 | AbstractExecutor ae = null; |
289 | |
290 | public VelGetterImpl(AbstractExecutor exec) |
291 | { |
292 | ae = exec; |
293 | } |
294 | |
295 | private VelGetterImpl() |
296 | { |
297 | } |
298 | |
299 | public Object invoke(Object o) |
300 | throws Exception |
301 | { |
302 | return ae.execute(o); |
303 | } |
304 | |
305 | public boolean isCacheable() |
306 | { |
307 | return true; |
308 | } |
309 | |
310 | public String getMethodName() |
311 | { |
312 | return ae.getMethod().getName(); |
313 | } |
314 | |
315 | } |
316 | |
317 | public class VelSetterImpl implements VelPropertySet |
318 | { |
319 | VelMethod vm = null; |
320 | String putKey = null; |
321 | |
322 | public VelSetterImpl(VelMethod velmethod) |
323 | { |
324 | this.vm = velmethod; |
325 | } |
326 | |
327 | public VelSetterImpl(VelMethod velmethod, String key) |
328 | { |
329 | this.vm = velmethod; |
330 | putKey = key; |
331 | } |
332 | |
333 | private VelSetterImpl() |
334 | { |
335 | } |
336 | |
337 | public Object invoke(Object o, Object value) |
338 | throws Exception |
339 | { |
340 | ArrayList al = new ArrayList(); |
341 | |
342 | if (putKey != null) |
343 | { |
344 | al.add(putKey); |
345 | al.add(value); |
346 | } |
347 | else |
348 | { |
349 | al.add(value); |
350 | } |
351 | |
352 | return vm.invoke(o,al.toArray()); |
353 | } |
354 | |
355 | public boolean isCacheable() |
356 | { |
357 | return true; |
358 | } |
359 | |
360 | public String getMethodName() |
361 | { |
362 | return vm.getMethodName(); |
363 | } |
364 | |
365 | } |
366 | } |