001: package org.andromda.scriptwrappers;
002:
003: import java.io.BufferedReader;
004: import java.io.File;
005: import java.io.FileNotFoundException;
006: import java.io.FileReader;
007: import java.io.Reader;
008:
009: import java.lang.reflect.Method;
010:
011: import java.util.Arrays;
012: import java.util.Iterator;
013: import java.util.LinkedHashSet;
014: import java.util.Set;
015:
016: import groovy.lang.GroovyClassLoader;
017: import groovy.lang.GroovyObject;
018:
019: /**
020: * This is a wrapper class for a Groovy script. The generated Java classes contain a private
021: * final copy of this wrapper and delegates all methods to this class so that their Groovy-counterparts
022: * can be executed.
023: *
024: * @author Chad Brandon
025: */
026: public class GroovyScriptWrapper {
027: private String scriptPath;
028: private Object stub;
029:
030: /**
031: * StubClass is always the generated class (not any subclasses),
032: * while stub may be an instance of a subclassed scripted class.
033: */
034: public GroovyScriptWrapper(Object stub, String scriptPath)
035: throws java.lang.InstantiationError {
036: this .stub = stub;
037: this .scriptPath = scriptPath;
038: }
039:
040: /**
041: * Invokes the method with the given <code>methodName</code> on the instance.
042: *
043: * @param methodName the name of the method to invoke.
044: * @param args the arguments to pass to the method.
045: * @return the return result of invoking the operation.
046: */
047: public Object invoke(String methodName, Object[] args) {
048: try {
049: final GroovyClassLoader grooveyClassLoader = new GroovyClassLoader(
050: this .getClassLoader());
051: final Class groovyClass = grooveyClassLoader.parseClass(
052: this .getContents(new File(this .scriptPath)),
053: scriptPath);
054: final GroovyObject groovyObject = (GroovyObject) groovyClass
055: .newInstance();
056:
057: this .copyProperties(this .stub, groovyObject);
058: return groovyObject.invokeMethod(methodName, args);
059: } catch (final Throwable throwable) {
060: throw new java.lang.RuntimeException(throwable);
061: }
062: }
063:
064: /**
065: * Retrieves the appropriate class loader instance as the parent of the groovyClassLoader.
066: *
067: * @return the class loader instance.
068: */
069: private ClassLoader getClassLoader() {
070: ClassLoader classLoader = Thread.currentThread()
071: .getContextClassLoader();
072: if (classLoader == null) {
073: classLoader = this .getClass().getClassLoader();
074: }
075: return classLoader;
076: }
077:
078: /**
079: * The line separator.
080: */
081: private static final char LINE_SEPARATOR = '\n';
082:
083: /**
084: * Loads the resource and returns the contents as a String.
085: *
086: * @param resource the name of the resource.
087: * @return the contents of the resource as a string.
088: * @throws FileNotFoundException
089: */
090: private String getContents(final File file)
091: throws FileNotFoundException {
092: Reader resource = new FileReader(file);
093: final StringBuffer contents = new StringBuffer();
094: try {
095: if (resource != null) {
096: BufferedReader resourceInput = new BufferedReader(
097: resource);
098: for (String line = resourceInput.readLine(); line != null; line = resourceInput
099: .readLine()) {
100: contents.append(line + LINE_SEPARATOR);
101: }
102: resourceInput.close();
103: resourceInput = null;
104: }
105: } catch (final Throwable throwable) {
106: throw new RuntimeException(throwable);
107: }
108:
109: // - return the contents and remove any throws clauses (since groovy doesn't support those)
110: return contents.toString().trim().replaceAll(
111: "\\s+throws\\s+.+\\s+", " ");
112: }
113:
114: /**
115: * Copies all properties from the given <code>from</code> instance to the given
116: * <code>to</code> instance.
117: *
118: * @param from the instance from which to copy all properties.
119: * @param to the instance of which to copy all properties.
120: * @throws Exception
121: */
122: private void copyProperties(final Object from, final GroovyObject to)
123: throws Exception {
124: final Set methods = new LinkedHashSet();
125: loadSuperMethods(from.getClass(), methods);
126: for (final Iterator iterator = methods.iterator(); iterator
127: .hasNext();) {
128: final Method method = (Method) iterator.next();
129:
130: final String methodName = method.getName();
131: final String getPrefix = "get";
132: if (methodName.startsWith(getPrefix)
133: && method.getParameterTypes().length == 0) {
134: String propertyName = methodName.replaceAll(getPrefix,
135: "");
136:
137: Method setterMethod = null;
138: try {
139: setterMethod = from.getClass().getMethod(
140: "set" + propertyName,
141: new Class[] { method.getReturnType() });
142: } catch (final Exception exception) {
143: // - ignore
144: }
145: if (setterMethod != null) {
146: method.setAccessible(true);
147: final Object value = method.invoke(from, null);
148: to.invokeMethod(setterMethod.getName(),
149: new Object[] { value });
150: }
151: }
152: }
153: }
154:
155: /**
156: * Loads all methods from the clazz's super classes.
157: *
158: * @param methods the list to load full of methods.
159: * @param clazz the class to retrieve the methods.
160: * @return the loaded methods.
161: */
162: private static Set loadSuperMethods(final Class clazz,
163: final Set methods) {
164: if (clazz.getSuperclass() != null) {
165: methods.addAll(Arrays.asList(clazz.getSuperclass()
166: .getDeclaredMethods()));
167: methods.addAll(loadSuperMethods(clazz.getSuperclass(),
168: methods));
169: }
170: return methods;
171: }
172: }
|