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. package org.apache.commons.jexl.util.introspection;
  17. import org.apache.commons.jexl.util.ArrayIterator;
  18. import org.apache.commons.jexl.util.EnumerationIterator;
  19. import org.apache.commons.jexl.util.AbstractExecutor;
  20. import org.apache.commons.jexl.util.GetExecutor;
  21. import org.apache.commons.jexl.util.BooleanPropertyExecutor;
  22. import org.apache.commons.jexl.util.PropertyExecutor;
  23. import org.apache.commons.logging.Log;
  24. import java.lang.reflect.Method;
  25. import java.lang.reflect.InvocationTargetException;
  26. import java.util.Iterator;
  27. import java.util.Collection;
  28. import java.util.Map;
  29. import java.util.Enumeration;
  30. import java.util.ArrayList;
  31. /**
  32. * Implementation of Uberspect to provide the default introspective
  33. * functionality of Velocity
  34. *
  35. * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
  36. * @version $Id: UberspectImpl.java,v 1.6 2004/08/19 17:15:59 dion Exp $
  37. */
  38. public class UberspectImpl implements Uberspect, UberspectLoggable
  39. {
  40. /**
  41. * Our runtime logger.
  42. */
  43. private Log rlog;
  44. /**
  45. * the default Velocity introspector
  46. */
  47. private static Introspector introspector;
  48. /**
  49. * init - does nothing - we need to have setRuntimeLogger
  50. * called before getting our introspector, as the default
  51. * vel introspector depends upon it.
  52. */
  53. public void init()
  54. throws Exception
  55. {
  56. }
  57. /**
  58. * Sets the runtime logger - this must be called before anything
  59. * else besides init() as to get the logger. Makes the pull
  60. * model appealing...
  61. */
  62. public void setRuntimeLogger(Log runtimeLogger)
  63. {
  64. rlog = runtimeLogger;
  65. introspector = new Introspector(rlog);
  66. }
  67. /**
  68. * To support iteratives - #foreach()
  69. */
  70. public Iterator getIterator(Object obj, Info i)
  71. throws Exception
  72. {
  73. if (obj.getClass().isArray())
  74. {
  75. return new ArrayIterator(obj);
  76. }
  77. else if (obj instanceof Collection)
  78. {
  79. return ((Collection) obj).iterator();
  80. }
  81. else if (obj instanceof Map)
  82. {
  83. return ((Map) obj).values().iterator();
  84. }
  85. else if (obj instanceof Iterator)
  86. {
  87. rlog.warn ("Warning! The iterative "
  88. + " is an Iterator in the #foreach() loop at ["
  89. + i.getLine() + "," + i.getColumn() + "]"
  90. + " in template " + i.getTemplateName()
  91. + ". Because it's not resetable,"
  92. + " if used in more than once, this may lead to"
  93. + " unexpected results.");
  94. return ((Iterator) obj);
  95. }
  96. else if (obj instanceof Enumeration)
  97. {
  98. rlog.warn ("Warning! The iterative "
  99. + " is an Enumeration in the #foreach() loop at ["
  100. + i.getLine() + "," + i.getColumn() + "]"
  101. + " in template " + i.getTemplateName()
  102. + ". Because it's not resetable,"
  103. + " if used in more than once, this may lead to"
  104. + " unexpected results.");
  105. return new EnumerationIterator((Enumeration) obj);
  106. }
  107. /* we have no clue what this is */
  108. rlog.warn ("Could not determine type of iterator in "
  109. + "#foreach loop "
  110. + " at [" + i.getLine() + "," + i.getColumn() + "]"
  111. + " in template " + i.getTemplateName() );
  112. return null;
  113. }
  114. /**
  115. * Method
  116. */
  117. public VelMethod getMethod(Object obj, String methodName, Object[] args, Info i)
  118. throws Exception
  119. {
  120. if (obj == null)
  121. return null;
  122. Method m = introspector.getMethod(obj.getClass(), methodName, args);
  123. return (m != null) ? new VelMethodImpl(m) : null;
  124. }
  125. /**
  126. * Property getter
  127. */
  128. public VelPropertyGet getPropertyGet(Object obj, String identifier, Info i)
  129. throws Exception
  130. {
  131. AbstractExecutor executor;
  132. Class claz = obj.getClass();
  133. /*
  134. * first try for a getFoo() type of property
  135. * (also getfoo() )
  136. */
  137. executor = new PropertyExecutor(rlog,introspector, claz, identifier);
  138. /*
  139. * look for boolean isFoo()
  140. */
  141. if( executor.isAlive() == false)
  142. {
  143. executor = new BooleanPropertyExecutor(rlog, introspector, claz, identifier);
  144. }
  145. /*
  146. * if that didn't work, look for get("foo")
  147. */
  148. if (executor.isAlive() == false)
  149. {
  150. executor = new GetExecutor(rlog, introspector, claz, identifier);
  151. }
  152. return (executor != null) ? new VelGetterImpl(executor) : null;
  153. }
  154. /**
  155. * Property setter
  156. */
  157. public VelPropertySet getPropertySet(Object obj, String identifier, Object arg, Info i)
  158. throws Exception
  159. {
  160. Class claz = obj.getClass();
  161. VelMethod vm = null;
  162. try
  163. {
  164. /*
  165. * first, we introspect for the set<identifier> setter method
  166. */
  167. Object[] params = {arg};
  168. try
  169. {
  170. vm = getMethod(obj, "set" + identifier, params, i);
  171. if (vm == null)
  172. {
  173. throw new NoSuchMethodException();
  174. }
  175. }
  176. catch(NoSuchMethodException nsme2)
  177. {
  178. StringBuffer sb = new StringBuffer("set");
  179. sb.append(identifier);
  180. if (Character.isLowerCase( sb.charAt(3)))
  181. {
  182. sb.setCharAt(3, Character.toUpperCase(sb.charAt(3)));
  183. }
  184. else
  185. {
  186. sb.setCharAt(3, Character.toLowerCase(sb.charAt(3)));
  187. }
  188. vm = getMethod(obj, sb.toString(), params, i);
  189. if (vm == null)
  190. {
  191. throw new NoSuchMethodException();
  192. }
  193. }
  194. }
  195. catch (NoSuchMethodException nsme)
  196. {
  197. /*
  198. * right now, we only support the Map interface
  199. */
  200. if (Map.class.isAssignableFrom(claz))
  201. {
  202. Object[] params = {new Object(), new Object()};
  203. vm = getMethod(obj, "put", params, i);
  204. if (vm!=null)
  205. return new VelSetterImpl(vm, identifier);
  206. }
  207. }
  208. return (vm!=null) ? new VelSetterImpl(vm) : null;
  209. }
  210. /**
  211. * Implementation of VelMethod
  212. */
  213. public class VelMethodImpl implements VelMethod
  214. {
  215. Method method = null;
  216. public VelMethodImpl(Method m)
  217. {
  218. method = m;
  219. }
  220. private VelMethodImpl()
  221. {
  222. }
  223. public Object invoke(Object o, Object[] params)
  224. throws Exception
  225. {
  226. try
  227. {
  228. return method.invoke(o, params);
  229. }
  230. catch( InvocationTargetException e )
  231. {
  232. final Throwable t = e.getTargetException();
  233. if( t instanceof Exception )
  234. {
  235. throw (Exception)t;
  236. }
  237. else if (t instanceof Error)
  238. {
  239. throw (Error)t;
  240. }
  241. else
  242. {
  243. throw e;
  244. }
  245. }
  246. }
  247. public boolean isCacheable()
  248. {
  249. return true;
  250. }
  251. public String getMethodName()
  252. {
  253. return method.getName();
  254. }
  255. public Class getReturnType()
  256. {
  257. return method.getReturnType();
  258. }
  259. }
  260. public class VelGetterImpl implements VelPropertyGet
  261. {
  262. AbstractExecutor ae = null;
  263. public VelGetterImpl(AbstractExecutor exec)
  264. {
  265. ae = exec;
  266. }
  267. private VelGetterImpl()
  268. {
  269. }
  270. public Object invoke(Object o)
  271. throws Exception
  272. {
  273. return ae.execute(o);
  274. }
  275. public boolean isCacheable()
  276. {
  277. return true;
  278. }
  279. public String getMethodName()
  280. {
  281. return ae.getMethod().getName();
  282. }
  283. }
  284. public class VelSetterImpl implements VelPropertySet
  285. {
  286. VelMethod vm = null;
  287. String putKey = null;
  288. public VelSetterImpl(VelMethod velmethod)
  289. {
  290. this.vm = velmethod;
  291. }
  292. public VelSetterImpl(VelMethod velmethod, String key)
  293. {
  294. this.vm = velmethod;
  295. putKey = key;
  296. }
  297. private VelSetterImpl()
  298. {
  299. }
  300. public Object invoke(Object o, Object value)
  301. throws Exception
  302. {
  303. ArrayList al = new ArrayList();
  304. if (putKey != null)
  305. {
  306. al.add(putKey);
  307. al.add(value);
  308. }
  309. else
  310. {
  311. al.add(value);
  312. }
  313. return vm.invoke(o,al.toArray());
  314. }
  315. public boolean isCacheable()
  316. {
  317. return true;
  318. }
  319. public String getMethodName()
  320. {
  321. return vm.getMethodName();
  322. }
  323. }
  324. }