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   * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
40   * @version $Id: UberspectImpl.java,v 1.6 2004/08/19 17:15:59 dion Exp $
41   */
42  public class UberspectImpl implements Uberspect, UberspectLoggable
43  {
44      /***
45       *  Our runtime logger.
46       */
47      private Log 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(Log 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          *  look for boolean isFoo()
160          */
161 
162         if( executor.isAlive() == false)
163         {
164             executor = new BooleanPropertyExecutor(rlog, introspector, claz, identifier);
165         }
166 
167         /*
168          *  if that didn't work, look for get("foo")
169          */
170 
171         if (executor.isAlive() == false)
172         {
173             executor = new GetExecutor(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         VelMethod vm = null;
188         try
189         {
190             /*
191              *  first, we introspect for the set<identifier> setter method
192              */
193 
194             Object[] params = {arg};
195 
196             try
197             {
198                 vm = getMethod(obj, "set" + identifier, params, i);
199 
200                 if (vm == null)
201                 {
202                    throw new NoSuchMethodException();
203                 }
204             }
205             catch(NoSuchMethodException nsme2)
206             {
207                 StringBuffer sb = new StringBuffer("set");
208                 sb.append(identifier);
209 
210                 if (Character.isLowerCase( sb.charAt(3)))
211                 {
212                     sb.setCharAt(3, Character.toUpperCase(sb.charAt(3)));
213                 }
214                 else
215                 {
216                     sb.setCharAt(3, Character.toLowerCase(sb.charAt(3)));
217                 }
218 
219                 vm = getMethod(obj, sb.toString(), params, i);
220 
221                 if (vm == null)
222                 {
223                    throw new NoSuchMethodException();
224                 }
225             }
226         }
227         catch (NoSuchMethodException nsme)
228         {
229             /*
230              *  right now, we only support the Map interface
231              */
232 
233             if (Map.class.isAssignableFrom(claz))
234             {
235                 Object[] params = {new Object(), new Object()};
236 
237                 vm = getMethod(obj, "put", params, i);
238 
239                 if (vm!=null)
240                     return new VelSetterImpl(vm, identifier);
241             }
242        }
243 
244        return (vm!=null) ?  new VelSetterImpl(vm) : null;
245     }
246 
247     /***
248      *  Implementation of VelMethod
249      */
250     public class VelMethodImpl implements VelMethod
251     {
252         Method method = null;
253 
254         public VelMethodImpl(Method m)
255         {
256             method = m;
257         }
258 
259         private VelMethodImpl()
260         {
261         }
262 
263         public Object invoke(Object o, Object[] params)
264             throws Exception
265         {
266             try
267             {
268                 return method.invoke(o, params);
269             }
270             catch( InvocationTargetException e )
271             {
272                 final Throwable t = e.getTargetException();
273 
274                 if( t instanceof Exception )
275                 {
276                     throw (Exception)t;
277                 }
278                 else if (t instanceof Error)
279                 {
280                     throw (Error)t;
281                 }
282                 else
283                 {
284                     throw e;
285                 }
286             }
287         }
288 
289         public boolean isCacheable()
290         {
291             return true;
292         }
293 
294         public String getMethodName()
295         {
296             return method.getName();
297         }
298 
299         public Class getReturnType()
300         {
301             return method.getReturnType();
302         }
303     }
304 
305     public class VelGetterImpl implements VelPropertyGet
306     {
307         AbstractExecutor ae = null;
308 
309         public VelGetterImpl(AbstractExecutor exec)
310         {
311             ae = exec;
312         }
313 
314         private VelGetterImpl()
315         {
316         }
317 
318         public Object invoke(Object o)
319             throws Exception
320         {
321             return ae.execute(o);
322         }
323 
324         public boolean isCacheable()
325         {
326             return true;
327         }
328 
329         public String getMethodName()
330         {
331             return ae.getMethod().getName();
332         }
333 
334     }
335 
336     public class VelSetterImpl implements VelPropertySet
337     {
338         VelMethod vm = null;
339         String putKey = null;
340 
341         public VelSetterImpl(VelMethod velmethod)
342         {
343             this.vm = velmethod;
344         }
345 
346         public VelSetterImpl(VelMethod velmethod, String key)
347         {
348             this.vm = velmethod;
349             putKey = key;
350         }
351 
352         private VelSetterImpl()
353         {
354         }
355 
356         public Object invoke(Object o, Object value)
357             throws Exception
358         {
359             ArrayList al = new ArrayList();
360 
361             if (putKey != null)
362             {
363                 al.add(putKey);
364                 al.add(value);
365             }
366             else
367             {
368                 al.add(value);
369             }
370 
371             return vm.invoke(o,al.toArray());
372         }
373 
374         public boolean isCacheable()
375         {
376             return true;
377         }
378 
379         public String getMethodName()
380         {
381             return vm.getMethodName();
382         }
383 
384     }
385 }