001: package murlen.util.fscript;
002:
003: import murlen.util.fscript.introspection.IntrospectorBase;
004:
005: import java.util.ArrayList;
006: import java.lang.reflect.Method;
007:
008: /**
009: * <p>ReflectionExtension - general extension for object access where either
010: * the class handles the processing (if it implements FSExtension),
011: * or reflection is used.
012: * </b>
013: * <p>
014: * <I>Copyright (C) 2002-2003 </I></p>
015: * <p>
016: * This library is free software; you can redistribute it and/or
017: * modify it under the terms of the GNU Library General Public
018: * License as published by the Free Software Foundation; either
019: * version 2 of the License, or (at your option) any later version.</p>
020: * <p>
021: * This library is distributed in the hope that it will be useful,
022: * but WITHOUT ANY WARRANTY; without even the implied warranty of
023: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
024: * Library General Public License for more details.</p>
025: *
026: * <p>You should have received a copy of the GNU Library General Public
027: * License along with this library; if not, write to the Free
028: * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA </p>
029: *
030: * @author Joachim Van der Auwera
031: * @author murlen
032: *
033: * modifications by Joachim Van der Auwera
034: * 05.08.2002 started (Joachim)
035: * 08.09.2002 major changes to allow overloading of params for methods/fields
036: * and use of static methods/fields. (murlen)
037: * 08-10.10.2002 use Velocity introspection code for better handling of native
038: * types and improved speed
039: * 18.11.2002 imporved exception handling
040: * 28.01.2003 fixed problem with return handling on objectMethod()
041: * 15.03.2003 pluggable exception handling
042: * 28.02.2004 make nulls typed
043: */
044:
045: public class FSReflectionExtension implements FSParserExtension {
046: Parser parser;
047: IntrospectorBase introspector = new IntrospectorBase();
048: ExceptionHandler exceptionHandler = new ExceptionHandler() {
049: public void handle(String name, Exception exc)
050: throws FSException {
051: throw new FSException("Error calling method " + name + " "
052: + exc.getMessage());
053: }
054: };
055: FSObject nullObj = new FSObject(null);
056:
057: public void setParser(Parser parser) {
058: this .parser = parser;
059: }
060:
061: public Object getVar(String name) throws FSException {
062: int pos = name.indexOf('.');
063: if (pos > 0) {
064: String oname = name.substring(0, pos);
065: name = name.substring(pos + 1);
066: Object object = parser.getVar(oname);
067: if (object != null && object instanceof FSObject) {
068: object = ((FSObject) object).getObject();
069: if (object == null)
070: throw new FSException("variable " + oname
071: + " is null");
072:
073: return getObjectVar(object, name);
074: }
075: }
076: throw new FSUnsupportedException();
077: }
078:
079: public void setVar(String name, Object value) throws FSException {
080: int pos = name.indexOf('.');
081: if (pos > 0) {
082: String oname = name.substring(0, pos);
083: name = name.substring(pos + 1);
084: Object object = parser.getVar(oname);
085: if (object != null && object instanceof FSObject) {
086: object = ((FSObject) object).getObject();
087: if (object == null)
088: throw new FSException("variable " + oname
089: + " is null");
090: setObjectVar(object, name, value);
091: } else {
092: throw new FSUnsupportedException();
093: }
094: } else {
095: throw new FSUnsupportedException();
096: }
097: }
098:
099: public Object getVar(String name, Object index) throws FSException {
100: // @todo ...
101: return null;
102: }
103:
104: public void setVar(String name, Object index, Object value)
105: throws FSException {
106: // @todo ...
107: }
108:
109: public Object callFunction(String name, ArrayList params)
110: throws FSException {
111: int pos = name.indexOf('.');
112:
113: if (pos > 0) {
114: String oname = name.substring(0, pos);
115: name = name.substring(pos + 1);
116: Object object = parser.getVar(oname);
117:
118: // we only work with FSObjects
119: if (object != null && object instanceof FSObject) {
120: object = ((FSObject) object).getObject();
121:
122: if (object == null)
123: throw new FSException("variable " + oname
124: + " is null");
125:
126: return objectMethod(object, name, params.toArray());
127: }
128: } else if (name.equals("create")) {
129: // create a new object
130: ArrayList cParams = (ArrayList) params.clone();
131: cParams.remove(0);
132: return createObject(params.get(0).toString(), cParams);
133:
134: } else if (name.equals("getClass")) {
135: // get a class object - this lets us call static methods etc.
136: return new FSObject(getClass((String) params.get(0)));
137: } else if (name.equals("null")) {
138: // get a typed null object
139: return new FSObject(null, getClass((String) params.get(0)));
140: }
141: throw new FSUnsupportedException();
142: }
143:
144: /*
145: * Called to invoke a method of a given object - tries to be fairly
146: * comprehensive when it comes to checking types/etc so we don't get
147: * bad calls
148: */
149: protected Object objectMethod(Object target, String methodName,
150: Object params[]) throws FSException {
151:
152: Method method = null;
153: try {
154: Class c;
155: if (target instanceof Class) {
156: c = (Class) target;
157: } else {
158: c = target.getClass();
159: }
160: method = introspector.getMethod(c, methodName, params);
161: } catch (Exception ex) {
162: throw new FSException("Error calling method " + methodName
163: + ex.getMessage());
164: }
165: if (method == null)
166: throw new FSReflectionException("Error method "
167: + methodName + " does not exists or ambigous");
168: try {
169: // rebuild argument list, unwrap all the FSObjects
170: Object params2[] = new Object[params.length];
171: for (int i = params.length - 1; i >= 0; i--) {
172: Object obj = params[i];
173: if (obj instanceof FSObject)
174: obj = ((FSObject) obj).getObject();
175: params2[i] = obj;
176: }
177: return normalizeObj(method.invoke(target, params2), method
178: .getReturnType());
179: } catch (Exception ex) {
180: exceptionHandler.handle(methodName, ex);
181: return nullObj;
182: }
183: }
184:
185: private Object createObject(String className, ArrayList params)
186: throws FSException {
187: try {
188: Class c;
189:
190: // create the class
191: c = getClass(className);
192:
193: if (c == null)
194: return null;
195:
196: // build array of our params
197: Object[] o = new Object[params.size()];
198: Object tmpObj;
199:
200: for (int i = 0; i < o.length; i++) {
201: //unwrap fsobjects
202: tmpObj = params.get(i);
203: if (tmpObj instanceof FSObject) {
204:
205: o[i] = ((FSObject) tmpObj).getObject();
206: } else {
207: o[i] = tmpObj;
208: }
209: }
210:
211: java.lang.reflect.Constructor[] constructors = c
212: .getDeclaredConstructors();
213:
214: // find appropriate constructor
215: for (int i = 0; i < constructors.length; i++) {
216: Class[] classes = constructors[i].getParameterTypes();
217:
218: if (checkMethods(classes, o)) {
219: // use to create object
220: return normalizeObj(constructors[i].newInstance(o),
221: c);
222: }
223: }
224: } catch (Exception e) {
225: throw new FSException("Error Creating new object "
226: + e.getMessage());
227: }
228:
229: return null;
230: }
231:
232: /*
233: * Returns a reference to a Class object of type <name>
234: */
235: private Class getClass(String name) throws FSException {
236: try {
237: return Thread.currentThread().getContextClassLoader()
238: .loadClass(name);
239: } catch (Exception e) {
240: throw new FSException("Error getting class " + name + " "
241: + e.getMessage());
242: }
243:
244: }
245:
246: /*
247: * Sets a field of an object (also tries java brans style set)
248: */
249: protected void setObjectVar(Object o, String name, Object value)
250: throws FSException {
251: Object arr[] = new Object[1];
252: arr[0] = value;
253: try {
254: objectMethod(o, "set" + name, arr);
255: } catch (FSReflectionException ex) {
256: // unwrap fsobject
257: if (value instanceof FSObject) {
258: value = ((FSObject) value).getObject();
259: }
260:
261: // method not found, try direct field access
262: Class c;
263: //handle use of static classes
264: if (o instanceof Class) {
265: //just cast to a class
266: c = (Class) o;
267: } else {
268: //it is not a class so we need to get the class
269: c = o.getClass();
270: }
271: java.lang.reflect.Field f = null;
272:
273: try {
274: //does the class have this field?
275: f = c.getField(name);
276: } catch (NoSuchFieldException e) {
277: }
278:
279: if (f != null) {
280: try {
281: f.set(o, value);
282: } catch (Exception e) {
283: throw new FSException("Could not access " + name
284: + " " + e.getMessage());
285: }
286: } else {
287: // oops field not found
288: throw ex;
289: }
290: }
291: }
292:
293: /*
294: * Returns a field of an object (also tries java beans style get)
295: */
296: protected Object getObjectVar(Object o, String name)
297: throws FSException {
298: try {
299: return objectMethod(o, "get" + name, new Object[0]);
300: } catch (FSReflectionException ex) {
301: // try direct field access
302: Class c;
303:
304: //handle static classes
305: if (o instanceof Class) {
306: c = (Class) o;
307: } else {
308: c = o.getClass();
309: }
310:
311: java.lang.reflect.Field f = null;
312:
313: try {
314: f = c.getField(name);
315: } catch (NoSuchFieldException e) {
316: }
317:
318: if (f != null) {
319: try {
320: return normalizeObj(f.get(o), f.getType());
321: } catch (Exception e) {
322: throw new FSException("Could not access " + name
323: + " " + e.getMessage());
324: }
325: } else {
326: // oops field not found
327: throw ex;
328: }
329:
330: }
331: }
332:
333: // used to check that parameters of calling
334: // object and method call match
335: private boolean checkMethods(Class[] c, Object[] o) {
336:
337: int n, len;
338:
339: // easy exit not the same length params
340: if (c.length != o.length) {
341: return false;
342: }
343:
344: // check that methods have same types
345: len = c.length;
346: for (n = 0; n < len; n++) {
347:
348: if (!c[n].isInstance(o[n])
349: && !(c[n].equals(Integer.TYPE) && o[n] instanceof Integer)
350: && !(c[n].equals(Double.TYPE) && o[n] instanceof Double)) {
351: return false;
352: }
353: }
354:
355: return true;
356:
357: }
358:
359: /*
360: * Ensures the right type is passed back i.e. Integer,String,Double
361: * or FSObject
362: */
363: private Object normalizeObj(Object o, Class c) {
364: // Return the right type...
365: if (o instanceof Integer || o instanceof String
366: || o instanceof Double) {
367: return o;
368: } else {
369: return new FSObject(o, c);
370: }
371: }
372:
373: /**
374: * set the exception handler routine which should be called for all exceptions caused by the
375: * referenced getter and setter methods (not the exceptions while trying to call, but only the
376: * exceptions thrown by the called code).
377: * @param eh the exception handler which should be used
378: */
379: public void setExceptionHandler(ExceptionHandler eh) {
380: exceptionHandler = eh;
381: }
382:
383: public interface ExceptionHandler {
384: public void handle(String name, Exception exc)
385: throws FSException;
386: }
387:
388: // marker for method not found exceptions
389: public class FSReflectionException extends FSException {
390: public FSReflectionException() {
391: }
392:
393: public FSReflectionException(String msg) {
394: super(msg);
395: }
396: }
397:
398: }
|