View Javadoc

1   /*
2    * Copyright 2002,2004 The Apache Software Foundation.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
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         /* we have no clue what this is */
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          * first try for a getFoo() type of property (also getfoo() )
133          */
134 
135         executor = new PropertyExecutor(rlog, introspector, claz, identifier);
136 
137         /*
138          * look for boolean isFoo()
139          */
140 
141         if (!executor.isAlive()) {
142             executor = new BooleanPropertyExecutor(rlog, introspector, claz, identifier);
143         }
144 
145         /*
146          * if that didn't work, look for get("foo")
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              * first, we introspect for the set<identifier> setter method
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              * right now, we only support the Map interface
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 }