View Javadoc

1   package org.apache.velocity.util.introspection;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.    
20   */
21  
22  import java.lang.reflect.Method;
23  import java.util.Collection;
24  import java.util.Enumeration;
25  import java.util.Iterator;
26  import java.util.Map;
27  
28  import org.apache.velocity.runtime.RuntimeLogger;
29  import org.apache.velocity.runtime.log.Log;
30  import org.apache.velocity.runtime.log.RuntimeLoggerLog;
31  import org.apache.velocity.runtime.parser.node.AbstractExecutor;
32  import org.apache.velocity.runtime.parser.node.BooleanPropertyExecutor;
33  import org.apache.velocity.runtime.parser.node.GetExecutor;
34  import org.apache.velocity.runtime.parser.node.MapGetExecutor;
35  import org.apache.velocity.runtime.parser.node.MapSetExecutor;
36  import org.apache.velocity.runtime.parser.node.PropertyExecutor;
37  import org.apache.velocity.runtime.parser.node.PutExecutor;
38  import org.apache.velocity.runtime.parser.node.SetExecutor;
39  import org.apache.velocity.runtime.parser.node.SetPropertyExecutor;
40  import org.apache.velocity.util.ArrayIterator;
41  import org.apache.velocity.util.EnumerationIterator;
42  
43  /**
44   *  Implementation of Uberspect to provide the default introspective
45   *  functionality of Velocity
46   *
47   * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
48   * @author <a href="mailto:henning@apache.org">Henning P. Schmiedehausen</a>
49   * @version $Id: UberspectImpl.java 525698 2007-04-05 05:03:07Z nbubna $
50   */
51  public class UberspectImpl implements Uberspect, UberspectLoggable
52  {
53      /**
54       *  Our runtime logger.
55       */
56      protected Log log;
57  
58      /**
59       *  the default Velocity introspector
60       */
61      protected Introspector introspector;
62  
63      /**
64       *  init - generates the Introspector. As the setup code
65       *  makes sure that the log gets set before this is called,
66       *  we can initialize the Introspector using the log object.
67       */
68      public void init()
69      {
70          introspector = new Introspector(log);
71      }
72  
73      /**
74       *  Sets the runtime logger - this must be called before anything
75       *  else.
76       *
77       * @param log The logger instance to use.
78       */
79      public void setLog(Log log)
80      {
81          this.log = log;
82      }
83  
84      /**
85       * @param runtimeLogger
86       * @deprecated Use setLog(Log log) instead.
87       */
88      public void setRuntimeLogger(RuntimeLogger runtimeLogger)
89      {
90          // in the off chance anyone still uses this method
91          // directly, use this hack to keep it working
92          setLog(new RuntimeLoggerLog(runtimeLogger));
93      }
94  
95      /**
96       *  To support iterative objects used in a <code>#foreach()</code>
97       *  loop.
98       *
99       * @param obj The iterative object.
100      * @param i Info about the object's location.
101      * @return An {@link Iterator} object.
102      * @throws Exception
103      */
104     public Iterator getIterator(Object obj, Info i)
105             throws Exception
106     {
107         if (obj.getClass().isArray())
108         {
109             return new ArrayIterator(obj);
110         }
111         else if (obj instanceof Collection)
112         {
113             return ((Collection) obj).iterator();
114         }
115         else if (obj instanceof Map)
116         {
117             return ((Map) obj).values().iterator();
118         }
119         else if (obj instanceof Iterator)
120         {
121             if (log.isDebugEnabled())
122             {
123                 log.debug("The iterative object in the #foreach() loop at " +
124                            i + " is of type java.util.Iterator.  Because " +
125                            "it is not resettable, if used in more than once it " +
126                            "may lead to unexpected results.");
127             }
128             return ((Iterator) obj);
129         }
130         else if (obj instanceof Enumeration)
131         {
132             if (log.isDebugEnabled())
133             {
134                 log.debug("The iterative object in the #foreach() loop at " +
135                            i + " is of type java.util.Enumeration.  Because " +
136                            "it is not resettable, if used in more than once it " +
137                            "may lead to unexpected results.");
138             }
139             return new EnumerationIterator((Enumeration) obj);
140         }
141 
142         /*  we have no clue what this is  */
143         log.info("Could not determine type of iterator in #foreach loop at " + i);
144 
145         return null;
146     }
147 
148     /**
149      *  Method
150      * @param obj
151      * @param methodName
152      * @param args
153      * @param i
154      * @return A Velocity Method.
155      * @throws Exception
156      */
157     public VelMethod getMethod(Object obj, String methodName, Object[] args, Info i)
158             throws Exception
159     {
160         if (obj == null)
161         {
162             return null;
163         }
164 
165         Method m = introspector.getMethod(obj.getClass(), methodName, args);
166         if (m != null)
167         {
168             return new VelMethodImpl(m);
169         }
170         else if (obj.getClass().isArray())
171         {
172             // only return *supported* array methods
173             if (VelArrayMethod.supports(methodName, args))
174             {
175                 return new VelArrayMethod(obj.getClass(), methodName, args);
176             }
177         }
178         return null;
179     }
180 
181     /**
182      * Property  getter
183      * @param obj
184      * @param identifier
185      * @param i
186      * @return A Velocity Getter Method.
187      * @throws Exception
188      */
189     public VelPropertyGet getPropertyGet(Object obj, String identifier, Info i)
190             throws Exception
191     {
192         if (obj == null)
193         {
194             return null;
195         }
196 
197         Class claz = obj.getClass();
198 
199         /*
200          *  first try for a getFoo() type of property
201          *  (also getfoo() )
202          */
203         AbstractExecutor executor = new PropertyExecutor(log, introspector, claz, identifier);
204 
205         /*
206          * Let's see if we are a map...
207          */
208         if (!executor.isAlive()) 
209         {
210             executor = new MapGetExecutor(log, claz, identifier);
211         }
212 
213         /*
214          *  if that didn't work, look for get("foo")
215          */
216 
217         if (!executor.isAlive())
218         {
219             executor = new GetExecutor(log, introspector, claz, identifier);
220         }
221 
222         /*
223          *  finally, look for boolean isFoo()
224          */
225 
226         if (!executor.isAlive())
227         {
228             executor = new BooleanPropertyExecutor(log, introspector, claz,
229                                                    identifier);
230         }
231 
232         return (executor.isAlive()) ? new VelGetterImpl(executor) : null;
233     }
234 
235     /**
236      * Property setter
237      * @param obj
238      * @param identifier
239      * @param arg
240      * @param i
241      * @return A Velocity Setter method.
242      * @throws Exception
243      */
244     public VelPropertySet getPropertySet(Object obj, String identifier,
245                                          Object arg, Info i)
246             throws Exception
247     {
248         if (obj == null)
249         {
250             return null;
251         }
252 
253         Class claz = obj.getClass();
254 
255         /*
256          *  first try for a setFoo() type of property
257          *  (also setfoo() )
258          */
259         SetExecutor executor = new SetPropertyExecutor(log, introspector, claz, identifier, arg);
260 
261         /*
262          * Let's see if we are a map...
263          */
264         if (!executor.isAlive())  {
265             executor = new MapSetExecutor(log, claz, identifier);
266         }
267 
268         /*
269          *  if that didn't work, look for put("foo", arg)
270          */
271 
272         if (!executor.isAlive())
273         {
274             executor = new PutExecutor(log, introspector, claz, arg, identifier);
275         }
276 
277         return (executor.isAlive()) ? new VelSetterImpl(executor) : null;
278     }
279 
280     /**
281      *  Implementation of VelMethod
282      */
283     public static class VelMethodImpl implements VelMethod
284     {
285         final Method method;
286 
287         /**
288          * @param m
289          */
290         public VelMethodImpl(Method m)
291         {
292             method = m;
293         }
294 
295         private VelMethodImpl()
296         {
297             method = null;
298         }
299 
300         /**
301          * @see VelMethod#invoke(java.lang.Object, java.lang.Object[])
302          */
303         public Object invoke(Object o, Object[] params)
304             throws Exception
305         {
306             return method.invoke(o, params);
307         }
308 
309         /**
310          * @see org.apache.velocity.util.introspection.VelMethod#isCacheable()
311          */
312         public boolean isCacheable()
313         {
314             return true;
315         }
316 
317         /**
318          * @see org.apache.velocity.util.introspection.VelMethod#getMethodName()
319          */
320         public String getMethodName()
321         {
322             return method.getName();
323         }
324 
325         /**
326          * @see org.apache.velocity.util.introspection.VelMethod#getReturnType()
327          */
328         public Class getReturnType()
329         {
330             return method.getReturnType();
331         }
332     }
333 
334     /**
335      *
336      *
337      */
338     public static class VelGetterImpl implements VelPropertyGet
339     {
340         final AbstractExecutor getExecutor;
341 
342         /**
343          * @param exec
344          */
345         public VelGetterImpl(AbstractExecutor exec)
346         {
347             getExecutor = exec;
348         }
349 
350         private VelGetterImpl()
351         {
352             getExecutor = null;
353         }
354 
355         /**
356          * @see org.apache.velocity.util.introspection.VelPropertyGet#invoke(java.lang.Object)
357          */
358         public Object invoke(Object o)
359             throws Exception
360         {
361             return getExecutor.execute(o);
362         }
363 
364         /**
365          * @see org.apache.velocity.util.introspection.VelPropertyGet#isCacheable()
366          */
367         public boolean isCacheable()
368         {
369             return true;
370         }
371 
372         /**
373          * @see org.apache.velocity.util.introspection.VelPropertyGet#getMethodName()
374          */
375         public String getMethodName()
376         {
377             return getExecutor.isAlive() ? getExecutor.getMethod().getName() : null;
378         }
379     }
380 
381     /**
382      *
383      */
384     public static class VelSetterImpl implements VelPropertySet
385     {
386         private final SetExecutor setExecutor;
387 
388         /**
389          * @param setExecutor
390          */
391         public VelSetterImpl(final SetExecutor setExecutor)
392         {
393             this.setExecutor = setExecutor;
394         }
395 
396         private VelSetterImpl()
397         {
398             setExecutor = null;
399         }
400 
401         /**
402          * Invoke the found Set Executor.
403          *
404          * @param o is the Object to invoke it on.
405          * @param value in the Value to set.
406          * @return The resulting Object.
407          * @throws Exception
408          */
409         public Object invoke(final Object o, final Object value)
410             throws Exception
411         {
412             return setExecutor.execute(o, value);
413         }
414 
415         /**
416          * @see org.apache.velocity.util.introspection.VelPropertySet#isCacheable()
417          */
418         public boolean isCacheable()
419         {
420             return true;
421         }
422 
423         /**
424          * @see org.apache.velocity.util.introspection.VelPropertySet#getMethodName()
425          */
426         public String getMethodName()
427         {
428             return setExecutor.isAlive() ? setExecutor.getMethod().getName() : null;
429         }
430     }
431 }