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