001: /*
002: * Copyright 2002,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.commons.jexl.util.introspection;
018:
019: import org.apache.commons.jexl.util.ArrayIterator;
020: import org.apache.commons.jexl.util.EnumerationIterator;
021: import org.apache.commons.jexl.util.AbstractExecutor;
022: import org.apache.commons.jexl.util.GetExecutor;
023: import org.apache.commons.jexl.util.BooleanPropertyExecutor;
024: import org.apache.commons.jexl.util.PropertyExecutor;
025: import org.apache.commons.logging.Log;
026:
027: import java.lang.reflect.Method;
028: import java.lang.reflect.InvocationTargetException;
029: import java.util.Iterator;
030: import java.util.Collection;
031: import java.util.Map;
032: import java.util.Enumeration;
033: import java.util.ArrayList;
034:
035: /**
036: * Implementation of Uberspect to provide the default introspective
037: * functionality of Velocity.
038: *
039: * @since 1.0
040: * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
041: * @version $Id: UberspectImpl.java 398509 2006-05-01 03:34:35Z dion $
042: */
043: public class UberspectImpl implements Uberspect, UberspectLoggable {
044: /** index of the first character of the property. */
045: private static final int PROPERTY_START_INDEX = 3;
046:
047: /**
048: * Our runtime logger.
049: */
050: private Log rlog;
051:
052: /**
053: * the default Velocity introspector.
054: */
055: private static Introspector introspector;
056:
057: /**
058: * init - does nothing - we need to have setRuntimeLogger called before
059: * getting our introspector, as the default vel introspector depends upon
060: * it.
061: * @throws Exception on any error.
062: */
063: public void init() throws Exception {
064: }
065:
066: /**
067: * Sets the runtime logger - this must be called before anything else
068: * besides init() as to get the logger. Makes the pull model appealing...
069: * @param runtimeLogger service to use for logging.
070: */
071: public void setRuntimeLogger(Log runtimeLogger) {
072: rlog = runtimeLogger;
073: introspector = new Introspector(rlog);
074: }
075:
076: /**
077: * {@inheritDoc}
078: */
079: public Iterator getIterator(Object obj, Info i) throws Exception {
080: if (obj.getClass().isArray()) {
081: return new ArrayIterator(obj);
082: } else if (obj instanceof Collection) {
083: return ((Collection) obj).iterator();
084: } else if (obj instanceof Map) {
085: return ((Map) obj).values().iterator();
086: } else if (obj instanceof Iterator) {
087: rlog.warn("Warning! The iterative "
088: + " is an Iterator in the #foreach() loop at ["
089: + i.getLine() + "," + i.getColumn() + "]"
090: + " in template " + i.getTemplateName()
091: + ". Because it's not resetable,"
092: + " if used in more than once, this may lead to"
093: + " unexpected results.");
094:
095: return ((Iterator) obj);
096: } else if (obj instanceof Enumeration) {
097: rlog.warn("Warning! The iterative "
098: + " is an Enumeration in the #foreach() loop at ["
099: + i.getLine() + "," + i.getColumn() + "]"
100: + " in template " + i.getTemplateName()
101: + ". Because it's not resetable,"
102: + " if used in more than once, this may lead to"
103: + " unexpected results.");
104:
105: return new EnumerationIterator((Enumeration) obj);
106: }
107:
108: /* we have no clue what this is */
109: rlog.warn("Could not determine type of iterator in "
110: + "#foreach loop " + " at [" + i.getLine() + ","
111: + i.getColumn() + "]" + " in template "
112: + i.getTemplateName());
113:
114: return null;
115: }
116:
117: /**
118: * {@inheritDoc}
119: */
120: public VelMethod getMethod(Object obj, String methodName,
121: Object[] args, Info i) throws Exception {
122: if (obj == null) {
123: return null;
124: }
125:
126: Method m = introspector.getMethod(obj.getClass(), methodName,
127: args);
128: if (m == null && obj instanceof Class) {
129: m = introspector.getMethod((Class) obj, methodName, args);
130: }
131:
132: return (m == null) ? null : new VelMethodImpl(m);
133: }
134:
135: /**
136: * {@inheritDoc}
137: */
138: public VelPropertyGet getPropertyGet(Object obj, String identifier,
139: Info i) throws Exception {
140: AbstractExecutor executor;
141:
142: Class claz = obj.getClass();
143:
144: /*
145: * first try for a getFoo() type of property (also getfoo() )
146: */
147:
148: executor = new PropertyExecutor(rlog, introspector, claz,
149: identifier);
150:
151: /*
152: * look for boolean isFoo()
153: */
154:
155: if (!executor.isAlive()) {
156: executor = new BooleanPropertyExecutor(rlog, introspector,
157: claz, identifier);
158: }
159:
160: /*
161: * if that didn't work, look for get("foo")
162: */
163:
164: if (!executor.isAlive()) {
165: executor = new GetExecutor(rlog, introspector, claz,
166: identifier);
167: }
168:
169: return (executor == null) ? null : new VelGetterImpl(executor);
170: }
171:
172: /**
173: * {@inheritDoc}
174: */
175: public VelPropertySet getPropertySet(Object obj, String identifier,
176: Object arg, Info i) throws Exception {
177: Class claz = obj.getClass();
178:
179: VelMethod vm = null;
180: try {
181: /*
182: * first, we introspect for the set<identifier> setter method
183: */
184:
185: Object[] params = { arg };
186:
187: try {
188: vm = getMethod(obj, "set" + identifier, params, i);
189:
190: if (vm == null) {
191: throw new NoSuchMethodException();
192: }
193: } catch (NoSuchMethodException nsme2) {
194: StringBuffer sb = new StringBuffer("set");
195: sb.append(identifier);
196:
197: if (Character.isLowerCase(sb
198: .charAt(PROPERTY_START_INDEX))) {
199: sb.setCharAt(PROPERTY_START_INDEX, Character
200: .toUpperCase(sb
201: .charAt(PROPERTY_START_INDEX)));
202: } else {
203: sb.setCharAt(PROPERTY_START_INDEX, Character
204: .toLowerCase(sb
205: .charAt(PROPERTY_START_INDEX)));
206: }
207:
208: vm = getMethod(obj, sb.toString(), params, i);
209:
210: if (vm == null) {
211: throw new NoSuchMethodException();
212: }
213: }
214: } catch (NoSuchMethodException nsme) {
215: /*
216: * right now, we only support the Map interface
217: */
218:
219: if (Map.class.isAssignableFrom(claz)) {
220: Object[] params = { new Object(), new Object() };
221:
222: vm = getMethod(obj, "put", params, i);
223:
224: if (vm != null) {
225: return new VelSetterImpl(vm, identifier);
226: }
227: }
228: }
229:
230: return (vm == null) ? null : new VelSetterImpl(vm);
231: }
232:
233: /**
234: * An implementation of {@link VelMethod}.
235: */
236: public class VelMethodImpl implements VelMethod {
237: /** the method. */
238: protected Method method = null;
239:
240: /**
241: * Create a new instance.
242: *
243: * @param m the method.
244: */
245: public VelMethodImpl(Method m) {
246: method = m;
247: }
248:
249: /**
250: * {@inheritDoc}
251: */
252: public Object invoke(Object o, Object[] params)
253: throws Exception {
254: try {
255: return method.invoke(o, params);
256: } catch (InvocationTargetException e) {
257: final Throwable t = e.getTargetException();
258:
259: if (t instanceof Exception) {
260: throw (Exception) t;
261: } else if (t instanceof Error) {
262: throw (Error) t;
263: } else {
264: throw e;
265: }
266: }
267: }
268:
269: /**
270: * {@inheritDoc}
271: */
272: public boolean isCacheable() {
273: return true;
274: }
275:
276: /**
277: * {@inheritDoc}
278: */
279: public String getMethodName() {
280: return method.getName();
281: }
282:
283: /**
284: * {@inheritDoc}
285: */
286: public Class getReturnType() {
287: return method.getReturnType();
288: }
289: }
290:
291: /**
292: * {@inheritDoc}
293: */
294: public class VelGetterImpl implements VelPropertyGet {
295: /** executor for performing the get. */
296: protected AbstractExecutor ae = null;
297:
298: /**
299: * Create the getter using an {@link AbstractExecutor} to
300: * do the work.
301: * @param exec the executor.
302: */
303: public VelGetterImpl(AbstractExecutor exec) {
304: ae = exec;
305: }
306:
307: /**
308: * {@inheritDoc}
309: */
310: public Object invoke(Object o) throws Exception {
311: return ae.execute(o);
312: }
313:
314: /**
315: * {@inheritDoc}
316: */
317: public boolean isCacheable() {
318: return true;
319: }
320:
321: /**
322: * {@inheritDoc}
323: */
324: public String getMethodName() {
325: return ae.getMethod().getName();
326: }
327: }
328:
329: /**
330: * {@inheritDoc}
331: */
332: public class VelSetterImpl implements VelPropertySet {
333: /** the method to call. */
334: protected VelMethod vm = null;
335: /** the key for indexed and other properties. */
336: protected String putKey = null;
337:
338: /**
339: * Create an instance.
340: * @param velmethod the method to call on set.
341: */
342: public VelSetterImpl(VelMethod velmethod) {
343: this .vm = velmethod;
344: }
345:
346: /**
347: * Create an instance.
348: * @param velmethod the method to call on set.
349: * @param key the index or other value passed to a
350: * setProperty(xxx, value) method.
351: */
352: public VelSetterImpl(VelMethod velmethod, String key) {
353: this .vm = velmethod;
354: putKey = key;
355: }
356:
357: /** {@inheritDoc} */
358: public Object invoke(Object o, Object value) throws Exception {
359: ArrayList al = new ArrayList();
360:
361: if (putKey == null) {
362: al.add(value);
363: } else {
364: al.add(putKey);
365: al.add(value);
366: }
367:
368: return vm.invoke(o, al.toArray());
369: }
370:
371: /** {@inheritDoc} */
372: public boolean isCacheable() {
373: return true;
374: }
375:
376: /** {@inheritDoc} */
377: public String getMethodName() {
378: return vm.getMethodName();
379: }
380:
381: }
382: }
|