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 }