001: package org.andromda.scriptwrappers;
002:
003: import java.lang.reflect.Method;
004:
005: import java.util.Arrays;
006: import java.util.Iterator;
007: import java.util.LinkedHashSet;
008: import java.util.Set;
009:
010: import bsh.EvalError;
011: import bsh.Interpreter;
012: import bsh.Primitive;
013:
014: /**
015: * This is a wrapper class for a BeanShell script. The generated Java classes contain a private
016: * final copy of this wrapper and delegates all methods to this class so that their BeanShell-counterparts
017: * can be executed.
018: *
019: * @author Chad Brandon
020: */
021: public class BshScriptWrapper {
022: private final Interpreter interpreter;
023: private String scriptPath;
024: private Object stub;
025:
026: /**
027: * StubClass is always the generated class (not any subclasses),
028: * while stub may be an instance of a subclassed scripted class.
029: */
030: public BshScriptWrapper(Object stub, String scriptPath)
031: throws java.lang.InstantiationError {
032: this .stub = stub;
033: this .scriptPath = scriptPath;
034: this .interpreter = initialize(stub, scriptPath);
035: }
036:
037: private BshScriptWrapper() {
038: this .interpreter = null;
039: }
040:
041: /**
042: * Initializes the interpreter.
043: *
044: * @param stub the stub class.
045: * @param scriptPath the path to the script file.
046: * @return the initialized interpreter.
047: */
048: private final Interpreter initialize(Object stub, String scriptPath) {
049: final Interpreter interpreter = new Interpreter();
050: interpreter.setClassLoader(stub.getClass().getClassLoader());
051: return interpreter;
052: }
053:
054: /**
055: * Invokes the method with the given <code>methodName</code> on the instance.
056: *
057: * @param methodName the name of the method to invoke.
058: * @param args the arguments to pass to the method.
059: * @return the return result of invoking the operation.
060: */
061: public Object invoke(String methodName, Object[] args) {
062: try {
063: try {
064: final Class stubClass = stub.getClass();
065: this .interpreter.source(scriptPath);
066: this .interpreter.set("instance", interpreter
067: .eval(" new " + stubClass.getName() + "();"));
068: this .interpreter.set("stub", stub);
069:
070: // - copy any properties
071: this .interpreter.eval(BshScriptWrapper.class.getName()
072: + ".copyProperties(stub, instance);");
073: } catch (final Exception exception) {
074: exception.printStackTrace();
075: throw new InstantiationError(
076: "Problems instantiating script '" + scriptPath
077: + "':" + exception);
078: }
079: final StringBuffer arguments = new StringBuffer();
080: if (args != null) {
081: for (int ctr = 1; ctr <= args.length; ctr++) {
082: final String argument = "$" + ctr;
083: this .interpreter.set(argument, args[ctr - 1]);
084: arguments.append(argument);
085: if (ctr != args.length) {
086: arguments.append(", ");
087: }
088: }
089: }
090:
091: Object returnValue = this .interpreter.eval("instance."
092: + methodName + "(" + arguments + ");");
093:
094: if (returnValue instanceof bsh.Primitive) {
095: returnValue = Primitive.unwrap(returnValue);
096: }
097:
098: return returnValue;
099: } catch (EvalError exception) {
100: throw new java.lang.RuntimeException(exception);
101: }
102: }
103:
104: /**
105: * Copies all properties from the given <code>from</code> instance to the given
106: * <code>to</code> instance.
107: *
108: * @param from the instance from which to copy all properties.
109: * @param to the instance of which to copy all properties.
110: * @throws Exception
111: */
112: protected static void copyProperties(final Object from,
113: final Object to) throws Exception {
114: final Set methods = new LinkedHashSet();
115: loadSuperMethods(from.getClass(), methods);
116: for (final Iterator iterator = methods.iterator(); iterator
117: .hasNext();) {
118: final Method method = (Method) iterator.next();
119:
120: final String methodName = method.getName();
121: final String getPrefix = "get";
122: if (methodName.startsWith(getPrefix)
123: && method.getParameterTypes().length == 0) {
124: String propertyName = methodName.replaceAll(getPrefix,
125: "");
126:
127: Method setterMethod = null;
128: try {
129: setterMethod = from.getClass().getMethod(
130: "set" + propertyName,
131: new Class[] { method.getReturnType() });
132: } catch (final Exception exception) {
133: // - ignore
134: }
135: if (setterMethod != null) {
136: method.setAccessible(true);
137: final Object value = method.invoke(from, null);
138: setterMethod.invoke(to, new Object[] { value });
139: }
140: }
141: }
142: }
143:
144: /**
145: * Loads all methods from the clazz's super classes.
146: *
147: * @param methods the list to load full of methods.
148: * @param clazz the class to retrieve the methods.
149: * @return the loaded methods.
150: */
151: private static Set loadSuperMethods(final Class clazz,
152: final Set methods) {
153: if (clazz.getSuperclass() != null) {
154: methods.addAll(Arrays.asList(clazz.getSuperclass()
155: .getDeclaredMethods()));
156: methods.addAll(loadSuperMethods(clazz.getSuperclass(),
157: methods));
158: }
159: return methods;
160: }
161: }
|