1 |
| package org.drools.semantics.java; |
2 |
| |
3 |
| import java.io.IOException; |
4 |
| import java.io.StringReader; |
5 |
| import java.lang.reflect.Method; |
6 |
| import java.util.ArrayList; |
7 |
| import java.util.HashSet; |
8 |
| import java.util.Iterator; |
9 |
| import java.util.List; |
10 |
| import java.util.Map; |
11 |
| import java.util.Set; |
12 |
| import java.util.StringTokenizer; |
13 |
| |
14 |
| import org.codehaus.janino.DebuggingInformation; |
15 |
| import org.codehaus.janino.EvaluatorBase; |
16 |
| import org.codehaus.janino.Java; |
17 |
| import org.codehaus.janino.Mod; |
18 |
| import org.codehaus.janino.Parser; |
19 |
| import org.codehaus.janino.Scanner; |
20 |
| import org.codehaus.janino.Location; |
21 |
| import org.codehaus.janino.Java.CompileException; |
22 |
| import org.codehaus.janino.Parser.ParseException; |
23 |
| import org.codehaus.janino.Scanner.ScanException; |
24 |
| import org.codehaus.janino.util.PrimitiveWrapper; |
25 |
| |
26 |
| import org.drools.rule.Declaration; |
27 |
| import org.drools.semantics.base.ClassObjectType; |
28 |
| import org.drools.spi.Importer; |
29 |
| import org.drools.spi.ObjectType; |
30 |
| |
31 |
| public class JavaScriptEvaluator extends EvaluatorBase |
32 |
| { |
33 |
| |
34 |
| private final Method method; |
35 |
| |
36 |
207
| public Method getMethod()
|
37 |
| { |
38 |
207
| return this.method;
|
39 |
| } |
40 |
| |
41 |
209
| public JavaScriptEvaluator(String code,
|
42 |
| String className, |
43 |
| Class interfaceToImplement, |
44 |
| String[] parameterNames, |
45 |
| Declaration[] declarations, |
46 |
| Importer importer, |
47 |
| Map applicationData, |
48 |
| Class baseClass, |
49 |
| ClassLoader classLoader) throws Scanner.ScanException, |
50 |
| Parser.ParseException, |
51 |
| Java.CompileException, |
52 |
| IOException, |
53 |
| ClassNotFoundException |
54 |
| { |
55 |
209
| super( classLoader );
|
56 |
| |
57 |
209
| Scanner scanner = new Scanner( null,
|
58 |
| new StringReader( code ) ); |
59 |
| |
60 |
209
| Method[] methods = interfaceToImplement.getDeclaredMethods( );
|
61 |
0
| if ( methods.length != 1 ) throw new RuntimeException( "Interface \"" + interfaceToImplement + "\" must declare exactly one method" );
|
62 |
209
| Method methodToImplement = methods[0];
|
63 |
209
| String methodName = methodToImplement.getName( );
|
64 |
209
| Class[] parameterTypes = methodToImplement.getParameterTypes( );
|
65 |
| |
66 |
0
| if ( parameterNames.length != parameterTypes.length ) throw new RuntimeException( "Lengths of \"parameterNames\" and \"parameterTypes\" do not match" );
|
67 |
| |
68 |
| |
69 |
209
| Java.CompilationUnit compilationUnit = new Java.CompilationUnit( scanner.peek( ).getLocation( ).getFileName( ) );
|
70 |
| |
71 |
| |
72 |
209
| this.parseImportDeclarations( compilationUnit,
|
73 |
| scanner ); |
74 |
| |
75 |
| |
76 |
209
| Java.Block block = this.addClassMethodBlockDeclaration( scanner.peek( ).getLocation( ),
|
77 |
| compilationUnit, |
78 |
| className, |
79 |
| baseClass, |
80 |
| new Class[]{interfaceToImplement}, |
81 |
| false, |
82 |
| methodToImplement.getReturnType( ), |
83 |
| methodName, |
84 |
| parameterNames, |
85 |
| parameterTypes, |
86 |
| methodToImplement.getExceptionTypes( ) ); |
87 |
| |
88 |
| |
89 |
| |
90 |
209
| Set imports = new HashSet( importer.getImports( ) );
|
91 |
| |
92 |
| |
93 |
209
| Parser parser = new Parser( scanner );
|
94 |
| |
95 |
| |
96 |
| |
97 |
209
| addDeclarations( scanner,
|
98 |
| block, |
99 |
| declarations, |
100 |
| imports ); |
101 |
209
| addAppData( scanner,
|
102 |
| block, |
103 |
| applicationData, |
104 |
| imports); |
105 |
| |
106 |
209
| Location loc = scanner.peek( ).getLocation( );
|
107 |
| |
108 |
| |
109 |
209
| Iterator it = imports.iterator( );
|
110 |
209
| String type;
|
111 |
209
| List list;
|
112 |
209
| StringTokenizer st;
|
113 |
209
| String token;
|
114 |
209
| boolean importOnDemand;
|
115 |
209
| while ( it.hasNext( ) )
|
116 |
| { |
117 |
309
| importOnDemand = false;
|
118 |
309
| list = new ArrayList( );
|
119 |
309
| type = (String) it.next( );
|
120 |
309
| st = new StringTokenizer( type,
|
121 |
| "." ); |
122 |
309
| while ( st.hasMoreTokens( ) )
|
123 |
| { |
124 |
1565
| token = st.nextToken( );
|
125 |
1565
| if ( !token.equals( "*" ) )
|
126 |
| { |
127 |
1543
| list.add( token );
|
128 |
| } |
129 |
| else |
130 |
| { |
131 |
22
| importOnDemand = true;
|
132 |
| } |
133 |
| } |
134 |
309
| if ( importOnDemand )
|
135 |
| { |
136 |
22
| compilationUnit.addImportDeclaration( new Java.TypeImportOnDemandDeclaration( loc,
|
137 |
| (String[]) list.toArray( new String[list.size( )] ) ) ); |
138 |
| } |
139 |
| else |
140 |
| { |
141 |
287
| compilationUnit.addImportDeclaration( new Java.SingleTypeImportDeclaration( loc,
|
142 |
| (String[]) list.toArray( new String[list.size( )] ) ) ); |
143 |
| } |
144 |
| } |
145 |
| |
146 |
209
| while ( !scanner.peek( ).isEOF( ) )
|
147 |
| { |
148 |
341
| block.addStatement( parser.parseBlockStatement( block ) );
|
149 |
| } |
150 |
| |
151 |
| |
152 |
| |
153 |
| |
154 |
| |
155 |
| |
156 |
208
| Class c;
|
157 |
| |
158 |
| |
159 |
208
| c = this.compileAndLoad( compilationUnit,
|
160 |
| DebuggingInformation.ALL, |
161 |
| className ); |
162 |
| |
163 |
| |
164 |
| |
165 |
| |
166 |
| |
167 |
| |
168 |
| |
169 |
| |
170 |
207
| try
|
171 |
| { |
172 |
207
| this.method = c.getMethod( methodName,
|
173 |
| parameterTypes ); |
174 |
| } |
175 |
| catch ( NoSuchMethodException ex ) |
176 |
| { |
177 |
0
| throw new RuntimeException( ex.toString( ) );
|
178 |
| } |
179 |
| |
180 |
0
| if ( this.method == null ) throw new RuntimeException( "Method \"" + methodName + "\" not found" );
|
181 |
| } |
182 |
| |
183 |
209
| private void addAppData(Scanner scanner,
|
184 |
| Java.Block block, |
185 |
| Map appData, |
186 |
| Set imports ) |
187 |
| { |
188 |
209
| Set keys = appData.keySet( );
|
189 |
209
| Iterator it = keys.iterator( );
|
190 |
209
| String key;
|
191 |
209
| Class clazz;
|
192 |
209
| String type;
|
193 |
209
| int nestedClassPosition;
|
194 |
| |
195 |
209
| while ( it.hasNext( ) )
|
196 |
| { |
197 |
8
| key = (String) it.next( );
|
198 |
8
| clazz = (Class) appData.get( key );
|
199 |
| |
200 |
8
| type = clazz.getName( );
|
201 |
8
| nestedClassPosition = type.indexOf( '$' );
|
202 |
| |
203 |
8
| if ( nestedClassPosition != -1 )
|
204 |
| { |
205 |
3
| type = type.substring( 0,
|
206 |
| nestedClassPosition ); |
207 |
| } |
208 |
| |
209 |
8
| imports.add( type );
|
210 |
| |
211 |
8
| Location loc = scanner.peek( ).getLocation( );
|
212 |
| |
213 |
8
| Java.VariableDeclarator[] variables = new Java.VariableDeclarator[]{
|
214 |
| new Java.VariableDeclarator( loc, |
215 |
| key, |
216 |
| 0, |
217 |
| new Java.Cast( loc, |
218 |
| this.classToType( loc, |
219 |
| clazz ), |
220 |
| new Java.MethodInvocation( loc, |
221 |
| block, |
222 |
| new Java.AmbiguousName( loc, |
223 |
| block, |
224 |
| new String[]{"applicationData"} ), |
225 |
| "get", |
226 |
| new Java.Rvalue[]{new Java.Literal( loc, |
227 |
| key )} ) ) )}; |
228 |
| |
229 |
8
| block.addStatement( new Java.LocalVariableDeclarationStatement( loc,
|
230 |
| block, |
231 |
| Mod.FINAL, |
232 |
| this.classToType( loc, |
233 |
| clazz ), |
234 |
| variables ) ); |
235 |
| } |
236 |
| } |
237 |
| |
238 |
209
| private void addDeclarations(Scanner scanner,
|
239 |
| Java.Block block, |
240 |
| Declaration[] declarations, |
241 |
| Set imports) |
242 |
| { |
243 |
209
| ObjectType objectType;
|
244 |
209
| Class clazz;
|
245 |
209
| String identifier;
|
246 |
209
| Declaration declaration;
|
247 |
| |
248 |
209
| String type;
|
249 |
209
| int nestedClassPosition;
|
250 |
| |
251 |
209
| for ( int i = 0; i < declarations.length; i++ )
|
252 |
| { |
253 |
373
| declaration = declarations[i];
|
254 |
373
| identifier = declaration.getIdentifier( );
|
255 |
373
| objectType = declaration.getObjectType( );
|
256 |
| |
257 |
373
| clazz = ((ClassObjectType) objectType).getType( );
|
258 |
| |
259 |
373
| type = clazz.getName( );
|
260 |
373
| nestedClassPosition = type.indexOf( '$' );
|
261 |
| |
262 |
373
| if ( nestedClassPosition != -1 )
|
263 |
| { |
264 |
23
| type = type.substring( 0,
|
265 |
| nestedClassPosition ); |
266 |
| } |
267 |
| |
268 |
373
| imports.add( type );
|
269 |
| |
270 |
373
| Location loc = scanner.peek( ).getLocation( );
|
271 |
| |
272 |
373
| Java.VariableDeclarator[] variables = new Java.VariableDeclarator[]{
|
273 |
| new Java.VariableDeclarator( loc, |
274 |
| identifier, |
275 |
| 0, |
276 |
| new Java.Cast( loc, |
277 |
| this.classToType( loc, |
278 |
| clazz ), |
279 |
| new Java.MethodInvocation( loc, |
280 |
| block, |
281 |
| new Java.AmbiguousName( loc, |
282 |
| block, |
283 |
| new String[]{"tuple"} ), |
284 |
| "get", |
285 |
| new Java.Rvalue[]{new Java.ArrayAccessExpression( loc, |
286 |
| new Java.AmbiguousName( loc, |
287 |
| block, |
288 |
| new String[]{"decls"} ), |
289 |
| new Java.ConstantValue( loc, |
290 |
| PrimitiveWrapper.wrap( i ) ) )} ) ) )}; |
291 |
| |
292 |
373
| block.addStatement( new Java.LocalVariableDeclarationStatement( loc,
|
293 |
| block, |
294 |
| Mod.FINAL, |
295 |
| this.classToType( loc, |
296 |
| clazz ), |
297 |
| variables ) ); |
298 |
| } |
299 |
| |
300 |
| } |
301 |
| |
302 |
209
| public static Object compile(String block,
|
303 |
| String className, |
304 |
| Class interfaceToImplement, |
305 |
| String[] parameterNames, |
306 |
| Declaration[] declarations, |
307 |
| Importer importer, |
308 |
| Map applicationData, |
309 |
| Class baseClass, |
310 |
| ClassLoader classLoader) throws ScanException, ParseException, CompileException, IOException, ClassNotFoundException |
311 |
| { |
312 |
209
| JavaScriptEvaluator scriptEvaluator = new JavaScriptEvaluator( block,
|
313 |
| className, |
314 |
| interfaceToImplement, |
315 |
| parameterNames, |
316 |
| declarations, |
317 |
| importer, |
318 |
| applicationData, |
319 |
| baseClass, |
320 |
| classLoader ); |
321 |
| |
322 |
207
| try
|
323 |
| { |
324 |
207
| return scriptEvaluator.getMethod( ).getDeclaringClass( ).newInstance( );
|
325 |
| } |
326 |
| catch ( InstantiationException e ) |
327 |
| { |
328 |
| |
329 |
0
| throw new RuntimeException( e.toString( ) );
|
330 |
| } |
331 |
| catch ( IllegalAccessException e ) |
332 |
| { |
333 |
| |
334 |
0
| throw new RuntimeException( e.toString( ) );
|
335 |
| } |
336 |
| } |
337 |
| } |