1 package org.andromda.maven.plugin.andromdapp.script;
2
3 import java.io.File;
4 import java.net.URL;
5 import javassist.ClassPool;
6 import javassist.CtClass;
7 import javassist.CtField;
8 import javassist.CtMethod;
9 import javassist.LoaderClassPath;
10 import javassist.Modifier;
11 import javassist.NotFoundException;
12 import org.andromda.core.common.ExceptionUtils;
13 import org.apache.commons.lang.StringUtils;
14
15
16
17
18
19
20
21
22 public final class ScriptClassGenerator
23 {
24
25
26
27 private static ScriptClassGenerator instance;
28
29
30
31
32 private String scriptWrapperName;
33
34
35
36
37
38
39
40
41 public static final ScriptClassGenerator getInstance(final String scriptWrapperName)
42 {
43 ExceptionUtils.checkEmpty(
44 "scriptWrapperName",
45 scriptWrapperName);
46 instance = new ScriptClassGenerator();
47 instance.scriptWrapperName = scriptWrapperName;
48 return instance;
49 }
50
51 private ScriptClassGenerator()
52 {
53
54 }
55
56
57
58
59
60
61
62 public void modifyClass(
63 final String scriptDirectory,
64 final Class existingClass)
65 {
66 try
67 {
68 final String className = existingClass.getName();
69
70 final ClassPool pool = ClassPool.getDefault();
71 final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
72 if (contextClassLoader != null)
73 {
74 pool.insertClassPath(new LoaderClassPath(contextClassLoader));
75 }
76 final CtClass ctClass = pool.get(className);
77
78
79 ctClass.defrost();
80
81 final String scriptWrapperFieldName = "scriptWrapper";
82 try
83 {
84 ctClass.getField(scriptWrapperFieldName);
85 }
86 catch (Exception exception)
87 {
88 final CtField scriptWrapper =
89 new CtField(
90 convert(
91 pool,
92 this.scriptWrapperName),
93 scriptWrapperFieldName,
94 ctClass);
95 scriptWrapper.setModifiers(Modifier.PRIVATE + Modifier.FINAL);
96 ctClass.addField(
97 scriptWrapper,
98 getScriptWrapperInitialization(
99 scriptDirectory,
100 className));
101 }
102
103 final CtMethod[] existingMethods = ctClass.getDeclaredMethods();
104 for (int ctr = 0; ctr < existingMethods.length; ctr++)
105 {
106 final CtMethod method = existingMethods[ctr];
107 if (!Modifier.isStatic(method.getModifiers()))
108 {
109 final CtClass returnType = method.getReturnType();
110 String methodBody;
111 if (returnType.equals(CtClass.voidType))
112 {
113 methodBody =
114 '{' + constructArgumentString(method) + "scriptWrapper.invoke(\"" + method.getName() +
115 "\", arguments);}";
116 }
117 else
118 {
119 if (returnType.isPrimitive())
120 {
121 methodBody =
122 '{' + constructArgumentString(method) + " return ((" + getWrapperTypeName(returnType) +
123 ")scriptWrapper.invoke(\"" + method.getName() + "\", arguments))." +
124 returnType.getName() + "Value();}";
125 }
126 else
127 {
128 methodBody =
129 '{' + constructArgumentString(method) + " return (" + method.getReturnType().getName() +
130 ")scriptWrapper.invoke(\"" + method.getName() + "\", arguments);}";
131 }
132 }
133 method.setBody(methodBody);
134 }
135 }
136
137 final File directory = getClassOutputDirectory(existingClass);
138 ctClass.writeFile(directory.getAbsolutePath());
139 }
140 catch (final Throwable throwable)
141 {
142 throwable.printStackTrace();
143 throw new ScriptClassGeneratorException(throwable);
144 }
145 }
146
147
148
149
150
151
152 private File getClassOutputDirectory(final Class existingClass)
153 {
154 final String className = existingClass.getName();
155 final String classResourcePath = '/' + className.replace(
156 '.',
157 '/') + ".class";
158 final URL classResource = existingClass.getResource(classResourcePath);
159 if (classResource == null)
160 {
161 throw new ScriptClassGeneratorException("Could not find the class resource '" + classResourcePath + '\'');
162 }
163 final String file = classResource.getFile().replaceAll(".*(\\\\|//)", "/");
164 return new File(StringUtils.replace(file, classResourcePath, ""));
165 }
166
167 private String constructArgumentString(final CtMethod method)
168 throws NotFoundException
169 {
170 CtClass[] argumentTypes = method.getParameterTypes();
171 final int argumentNumber = argumentTypes.length;
172 final StringBuilder arguments =
173 new StringBuilder("final Object[] arguments = new Object[" + argumentNumber + "];");
174 for (int ctr = 1; ctr <= argumentNumber; ctr++)
175 {
176 final CtClass argumentType = argumentTypes[ctr - 1];
177 arguments.append("arguments[").append(ctr - 1).append("] = ");
178 if (argumentType.isPrimitive())
179 {
180 arguments.append("new java.lang.").append(getWrapperTypeName(argumentType)).append("($").append(ctr).append(");");
181 }
182 else
183 {
184 arguments.append('$').append(ctr).append(';');
185 }
186 }
187 return arguments.toString();
188 }
189
190 private String getWrapperTypeName(CtClass ctClass)
191 {
192 final String typeName = ctClass.getName();
193 StringBuilder name = new StringBuilder(typeName);
194 if ("int".equalsIgnoreCase(typeName))
195 {
196 name.append("eger");
197 }
198 return StringUtils.capitalize(name.toString());
199 }
200
201 private String getScriptWrapperInitialization(
202 final String directory,
203 final String className)
204 {
205 return "new " + this.scriptWrapperName + "(this, \"" +
206 new File(
207 directory,
208 className.replace(
209 '.',
210 '/')).getAbsolutePath().replace(
211 '\\',
212 '/') + ".java" + "\");";
213 }
214
215
216
217
218
219
220
221
222
223 private CtClass convert(
224 final ClassPool pool,
225 final String className)
226 throws NotFoundException
227 {
228 CtClass ctClass = null;
229 if (className != null)
230 {
231 ctClass = pool.get(className);
232 }
233 return ctClass;
234 }
235 }