View Javadoc
1   package org.andromda.scriptwrappers;
2   
3   import groovy.lang.GroovyClassLoader;
4   import groovy.lang.GroovyObject;
5   import java.io.BufferedReader;
6   import java.io.File;
7   import java.io.FileNotFoundException;
8   import java.io.FileReader;
9   import java.io.IOException;
10  import java.io.Reader;
11  import java.lang.reflect.Method;
12  import java.util.Arrays;
13  import java.util.Iterator;
14  import java.util.LinkedHashSet;
15  import java.util.Set;
16  
17  /**
18   * This is a wrapper class for a Groovy script. The generated Java classes contain a private
19   * final copy of this wrapper and delegates all methods to this class so that their Groovy-counterparts
20   * can be executed.
21   *
22   * @author Chad Brandon
23   */
24  public class GroovyScriptWrapper
25  {
26      private String scriptPath;
27      private Object stub;
28  
29      /**
30       * StubClass is always the generated class (not any subclasses),
31       * while stub may be an instance of a subclassed scripted class.
32       * @param stub
33       * @param scriptPath
34       * @throws InstantiationError
35       */
36      public GroovyScriptWrapper(
37          Object stub,
38          String scriptPath)
39          throws InstantiationError
40      {
41          this.stub = stub;
42          this.scriptPath = scriptPath;
43      }
44  
45      /**
46       * Invokes the method with the given <code>methodName</code> on the instance.
47       *
48       * @param methodName the name of the method to invoke.
49       * @param args the arguments to pass to the method.
50       * @return the return result of invoking the operation.
51       */
52      public Object invoke(
53          String methodName,
54          Object[] args)
55      {
56          try
57          {
58              final GroovyClassLoader grooveyClassLoader = new GroovyClassLoader(this.getClassLoader());
59              final Class groovyClass =
60                  grooveyClassLoader.parseClass(
61                      this.getContents(new File(this.scriptPath)),
62                      scriptPath);
63              final GroovyObject groovyObject = (GroovyObject)groovyClass.newInstance();
64  
65              this.copyProperties(
66                  this.stub,
67                  groovyObject);
68              Object rtn = groovyObject.invokeMethod(
69                  methodName,
70                  args);
71              grooveyClassLoader.close();
72              return rtn;
73          }
74          catch (final Throwable throwable)
75          {
76              throw new RuntimeException(throwable);
77          }
78      }
79  
80      /**
81       * Retrieves the appropriate class loader instance as the parent of the groovyClassLoader.
82       *
83       * @return the class loader instance.
84       */
85      private ClassLoader getClassLoader()
86      {
87          ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
88          if (classLoader == null)
89          {
90              classLoader = this.getClass().getClassLoader();
91          }
92          return classLoader;
93      }
94  
95      /**
96       * The line separator.
97       */
98      private static final char LINE_SEPARATOR = '\n';
99  
100     /**
101      * Loads the resource and returns the contents as a String.
102      *
103      * @param resource the name of the resource.
104      * @return the contents of the resource as a string.
105      * @throws FileNotFoundException
106      */
107     @SuppressWarnings("static-method")
108     private String getContents(final File file)
109         throws FileNotFoundException
110     {
111         Reader resource = new FileReader(file);
112         final StringBuilder contents = new StringBuilder();
113         try
114         {
115             BufferedReader resourceInput = new BufferedReader(resource);
116             for (String line = resourceInput.readLine(); line != null; line = resourceInput.readLine())
117             {
118                 contents.append(line).append(LINE_SEPARATOR);
119             }
120             resourceInput.close();
121             resourceInput = null;
122         }
123         catch (final Throwable throwable)
124         {
125             throw new RuntimeException(throwable);
126         }
127         finally
128         {
129             try
130             {
131                 resource.close();
132             }
133             catch (IOException ex)
134             {
135                 // Ignore
136             }
137         }
138 
139         // - return the contents and remove any throws clauses (since groovy doesn't support those)
140         return contents.toString().trim().replaceAll(
141             "\\s+throws\\s+.+\\s+",
142             " ");
143     }
144 
145     /**
146      * Copies all properties from the given <code>from</code> instance to the given
147      * <code>to</code> instance.
148      *
149      * @param from the instance from which to copy all properties.
150      * @param to the instance of which to copy all properties.
151      * @throws Exception
152      */
153     @SuppressWarnings("static-method")
154     private void copyProperties(
155         final Object from,
156         final GroovyObject to)
157         throws Exception
158     {
159         final Set methods = new LinkedHashSet();
160         loadSuperMethods(
161             from.getClass(),
162             methods);
163         for (final Iterator iterator = methods.iterator(); iterator.hasNext();)
164         {
165             final Method method = (Method)iterator.next();
166 
167             final String methodName = method.getName();
168             final String getPrefix = "get";
169             if (methodName.startsWith(getPrefix) && method.getParameterTypes().length == 0)
170             {
171                 String propertyName = methodName.replaceAll(
172                         getPrefix,
173                         "");
174 
175                 Method setterMethod = null;
176                 try
177                 {
178                     setterMethod =
179                         from.getClass().getMethod(
180                             "set" + propertyName,
181                             new Class[] {method.getReturnType()});
182                 }
183                 catch (final Exception exception)
184                 {
185                     // - ignore
186                 }
187                 if (setterMethod != null)
188                 {
189                     method.setAccessible(true);
190                     final Object value = method.invoke(
191                             from);
192                     to.invokeMethod(
193                         setterMethod.getName(),
194                         new Object[] {value});
195                 }
196             }
197         }
198     }
199 
200     /**
201      * Loads all methods from the clazz's super classes.
202      *
203      * @param methods the list to load full of methods.
204      * @param clazz the class to retrieve the methods.
205      * @return the loaded methods.
206      */
207     private static Set loadSuperMethods(
208         final Class clazz,
209         final Set methods)
210     {
211         if (clazz.getSuperclass() != null)
212         {
213             methods.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredMethods()));
214             methods.addAll(loadSuperMethods(
215                     clazz.getSuperclass(),
216                     methods));
217         }
218         return methods;
219     }
220 }