1 | package com.hammurapi.eventbus; |
2 | |
3 | import java.io.File; |
4 | import java.io.FileOutputStream; |
5 | import java.io.FileWriter; |
6 | import java.io.IOException; |
7 | import java.io.ObjectOutputStream; |
8 | import java.io.StringWriter; |
9 | import java.io.Writer; |
10 | import java.lang.reflect.Method; |
11 | import java.lang.reflect.ParameterizedType; |
12 | import java.lang.reflect.Type; |
13 | import java.lang.reflect.TypeVariable; |
14 | import java.util.ArrayList; |
15 | import java.util.Arrays; |
16 | import java.util.Collection; |
17 | import java.util.Collections; |
18 | import java.util.Comparator; |
19 | import java.util.HashMap; |
20 | import java.util.HashSet; |
21 | import java.util.Iterator; |
22 | import java.util.List; |
23 | import java.util.Locale; |
24 | import java.util.Map; |
25 | import java.util.Set; |
26 | |
27 | import org.apache.commons.lang.StringEscapeUtils; |
28 | import org.onemind.jxp.JxpContext; |
29 | import org.onemind.jxp.JxpPageSource; |
30 | import org.onemind.jxp.JxpProcessor; |
31 | import org.onemind.jxp.ResourceStreamPageSource; |
32 | |
33 | import com.hammurapi.common.Context; |
34 | import com.hammurapi.common.TokenExpander; |
35 | import com.hammurapi.common.TokenExpander.TokenSource; |
36 | import com.hammurapi.convert.AbstractReflectiveAtomicConvertersBundle; |
37 | import com.hammurapi.convert.ConverterMethod; |
38 | import com.hammurapi.convert.ConvertingService; |
39 | import com.hammurapi.eventbus.IntrospectorBase.IntrospectionResult; |
40 | import com.hammurapi.extract.BinaryExtractor; |
41 | import com.hammurapi.extract.ComparisonResult; |
42 | import com.hammurapi.extract.CompositePredicate; |
43 | import com.hammurapi.extract.Constant; |
44 | import com.hammurapi.extract.Equal; |
45 | import com.hammurapi.extract.Extractor; |
46 | import com.hammurapi.extract.False; |
47 | import com.hammurapi.extract.IndexedExtractor; |
48 | import com.hammurapi.extract.InstanceOfPredicate; |
49 | import com.hammurapi.extract.MappedExtractor; |
50 | import com.hammurapi.extract.Not; |
51 | import com.hammurapi.extract.NotEqual; |
52 | import com.hammurapi.extract.Predicate; |
53 | import com.hammurapi.extract.True; |
54 | import com.hammurapi.extract.java.JavaExtractor; |
55 | import com.hammurapi.render.RenderingException; |
56 | import com.hammurapi.render.WriterRenderer; |
57 | |
58 | /** |
59 | * This class generates source code for Java binding class. |
60 | * By default binder classes are put to the same package as handler classes and have postfix ''JavaBinder''. |
61 | * This behavior can be changed by subclassing the compiler. The class keeps track of generated predicate |
62 | * classes and attempts to reuse them. |
63 | * |
64 | * @author Pavel Vlasov |
65 | * |
66 | */ |
67 | public class JavaBinderCompiler { |
68 | |
69 | protected static final String BINDER_CLASS_PACKAGE = "binderClassPackage"; |
70 | private static final String JAVA_INLINE = "java_inline"; |
71 | private static final String JAVA = "java"; |
72 | private static final String BIND_HELPER = "bindHelper"; |
73 | |
74 | private class EventDispatchContextDescriptor { |
75 | |
76 | private Class<EventDispatchContext> contextClass; |
77 | private _Type[] contextTypeParameters; |
78 | |
79 | public EventDispatchContextDescriptor(Class<EventDispatchContext> contextClass, _Type[] eventBusTypeParameters) { |
80 | this.contextClass = contextClass; |
81 | |
82 | TypeVariable<Class<EventDispatchContext>>[] cctp = EventDispatchContext.class.getTypeParameters(); |
83 | contextTypeParameters = new _Type[cctp.length]; |
84 | for (int i=0; i< cctp.length; ++i) { |
85 | contextTypeParameters[i] = _Type.createType(cctp[i]); |
86 | } |
87 | |
88 | List<List<PathEntry>> iPaths = inheritancePaths(contextClass, EventDispatchContext.class); |
89 | Collections.sort(iPaths, new Comparator<List<PathEntry>>() { |
90 | |
91 | @Override |
92 | public int compare(List<PathEntry> o1, List<PathEntry> o2) { |
93 | int sizeDelta = o1.size() - o2.size(); |
94 | if (sizeDelta!=0) { |
95 | return sizeDelta; |
96 | } |
97 | return o1.hashCode() - o2.hashCode(); |
98 | } |
99 | }); |
100 | for (List<PathEntry> path: iPaths) { |
101 | for (PathEntry pe: path) { |
102 | pe.bind(contextTypeParameters); |
103 | } |
104 | break; |
105 | } |
106 | |
107 | for (int i=0; i<contextTypeParameters.length; ++i) { |
108 | bind(contextTypeParameters, i, new _TypeVariable("E"), eventBusTypeParameters[0]); |
109 | bind(contextTypeParameters, i, new _TypeVariable("P"), eventBusTypeParameters[1]); |
110 | bind(contextTypeParameters, i, new _TypeVariable("C"), eventBusTypeParameters[2]); |
111 | bind(contextTypeParameters, i, new _TypeVariable("K"), eventBusTypeParameters[3]); |
112 | bind(contextTypeParameters, i, new _TypeVariable("H"), eventBusTypeParameters[4]); |
113 | bind(contextTypeParameters, i, new _TypeVariable("S"), eventBusTypeParameters[5]); |
114 | } |
115 | |
116 | } |
117 | |
118 | private void bind(_Type[] contextTypeParameters, int i, _TypeVariable from, _Type to) { |
119 | if (from.equals(contextTypeParameters[i])) { |
120 | contextTypeParameters[i] = to; |
121 | } else { |
122 | contextTypeParameters[i].bind(from, to); |
123 | } |
124 | } |
125 | |
126 | public String getType() { |
127 | _Class cls = new _Class(contextClass); |
128 | cls.bind(new _TypeVariable("E"), contextTypeParameters[0]); |
129 | cls.bind(new _TypeVariable("P"), contextTypeParameters[1]); |
130 | cls.bind(new _TypeVariable("C"), contextTypeParameters[2]); |
131 | cls.bind(new _TypeVariable("H"), contextTypeParameters[3]); |
132 | cls.bind(new _TypeVariable("S"), contextTypeParameters[4]); |
133 | |
134 | return cls.toJavaDeclaration(); |
135 | } |
136 | |
137 | public String getEventType() { |
138 | return contextTypeParameters[0].toJavaDeclaration(); |
139 | } |
140 | |
141 | public String getStoreType() { |
142 | return contextTypeParameters[4].toJavaDeclaration(); |
143 | } |
144 | |
145 | public boolean isJoin() { |
146 | return EventDispatchJoinContext.class.isAssignableFrom(contextClass); |
147 | } |
148 | } |
149 | |
150 | private class BindMethod<E, HC> { |
151 | |
152 | private IntrospectionResult<E, HC> ir; |
153 | private String name; |
154 | private EventDispatchContextDescriptor eventDispatchContextDescriptor; |
155 | private Class<E> busEventType; |
156 | private Map<String, Object> renderingEnvironment; |
157 | |
158 | @SuppressWarnings({ "unchecked", "rawtypes" }) |
159 | BindMethod(String name, IntrospectionResult<E, HC> ir, Class<E> busEventType, _Type[] typeParameters, Map<String, Object> renderingEnvironment) { |
160 | this.ir = ir; |
161 | this.name = name; |
162 | Class<?> firstParameterType = ir.getMethod().getParameterTypes()[0]; |
163 | if (ir.getOffset()==1 |
164 | && EventDispatchContext.class.isAssignableFrom(firstParameterType) |
165 | && !(EventDispatchContext.class.equals(firstParameterType)) || EventDispatchJoinContext.class.equals(firstParameterType)) { |
166 | |
167 | eventDispatchContextDescriptor = new EventDispatchContextDescriptor((Class<EventDispatchContext>) firstParameterType, typeParameters); |
168 | } |
169 | this.busEventType = busEventType; |
170 | this.renderingEnvironment = renderingEnvironment; |
171 | } |
172 | |
173 | public String getName() { |
174 | return name; |
175 | } |
176 | |
177 | public String getParameterTypeCast(int pIdx) { |
178 | Class<E> parameterType = ir.getParameterTypes()[pIdx]; |
179 | if (parameterType.isAssignableFrom(busEventType)) { |
180 | return ""; |
181 | } |
182 | |
183 | if (parameterType.isPrimitive()) { |
184 | if (boolean.class.equals(parameterType)) { |
185 | return "("+Boolean.class.getName()+")"; |
186 | } else if (char.class.equals(parameterType)) { |
187 | return "("+Character.class.getName()+")"; |
188 | } else if (byte.class.equals(parameterType)) { |
189 | return "("+Byte.class.getName()+")"; |
190 | } else if (short.class.equals(parameterType)) { |
191 | return "("+Short.class.getName()+")"; |
192 | } else if (int.class.equals(parameterType)) { |
193 | return "("+Integer.class.getName()+")"; |
194 | } else if (long.class.equals(parameterType)) { |
195 | return "("+Long.class.getName()+")"; |
196 | } else if (float.class.equals(parameterType)) { |
197 | return "("+Float.class.getName()+")"; |
198 | } else if (double.class.equals(parameterType)) { |
199 | return "("+Double.class.getName()+")"; |
200 | } else if (void.class.equals(parameterType)) { |
201 | throw new IllegalArgumentException("Parameter type cannot be void"); |
202 | } |
203 | } |
204 | return "("+parameterType.getName()+")"; |
205 | } |
206 | |
207 | public Method getMethod() { |
208 | return ir.getMethod(); |
209 | } |
210 | |
211 | public boolean isVoid() { |
212 | return void.class.equals(getMethod().getReturnType()); |
213 | } |
214 | |
215 | public Predicate<E, HC>[] getPredicates() { |
216 | return ir.getPredicates(); |
217 | } |
218 | |
219 | public String inlinePredicate(int pIdx) throws Exception { |
220 | WriterRenderer renderer = ConvertingService.convert(ir.getPredicates()[pIdx], WriterRenderer.class); |
221 | if (renderer==null) { |
222 | throw new EventBusException("Cannot compile predicate "+ir.getPredicates()[pIdx].getClass().getName()); |
223 | } |
224 | |
225 | StringWriter sw = new StringWriter(); |
226 | if (renderer.render(sw, renderingEnvironment, Context.INSTANCE, JAVA_INLINE, null, outputDir)) { |
227 | sw.close(); |
228 | return sw.toString(); |
229 | } |
230 | throw new EventBusException("Cannot compile predicate "+ir.getPredicates()[pIdx].getClass().getName()); |
231 | } |
232 | |
233 | public int getOffset() { |
234 | return ir.getOffset(); |
235 | } |
236 | |
237 | public Class<E>[] getParameterTypes() { |
238 | return ir.getParameterTypes(); |
239 | } |
240 | |
241 | public Handler getHandlerAnnotation() { |
242 | return ir.getHandlerAnnotation(); |
243 | } |
244 | |
245 | public EventDispatchContextDescriptor getEventDispatchContext() { |
246 | return eventDispatchContextDescriptor; |
247 | } |
248 | |
249 | } |
250 | |
251 | public static class JavaExtractorEntry { |
252 | |
253 | private JavaExtractor javaExtractor; |
254 | private String packageName; |
255 | private String className; |
256 | |
257 | JavaExtractorEntry(JavaExtractor javaExtractor, String packageName, String className) { |
258 | super(); |
259 | this.javaExtractor = javaExtractor; |
260 | this.packageName = packageName; |
261 | this.className = className; |
262 | } |
263 | |
264 | JavaExtractor getJavaExtractor() { |
265 | return javaExtractor; |
266 | } |
267 | |
268 | String getPackageName() { |
269 | return packageName; |
270 | } |
271 | |
272 | String getClassName() { |
273 | return className; |
274 | } |
275 | |
276 | } |
277 | |
278 | private class BindHelper<E, HC> { |
279 | |
280 | private Collection<BindMethod<E,HC>> bindMethods = new ArrayList<JavaBinderCompiler.BindMethod<E,HC>>(); |
281 | private Collection<JavaExtractorEntry> javaExtractorEntries; |
282 | private Class<?> contextType; |
283 | |
284 | BindHelper( |
285 | Collection<IntrospectionResult<E, HC>> irc, |
286 | Class<E> busEventType, |
287 | _Type[] typeParameters, |
288 | Collection<JavaExtractorEntry> javaExtractorEntries, |
289 | String binderClassPackage) { |
290 | |
291 | Set<String> usedNames = new HashSet<String>(); |
292 | for (IntrospectionResult<E, HC> ir: irc) { |
293 | String name = ir.getMethod().getName(); |
294 | for (int i=0; usedNames.contains(name); ++i) { |
295 | name = ir.getMethod().getName()+i; |
296 | } |
297 | usedNames.add(name); |
298 | Map<String, Object> renderingEnvironment = new HashMap<String, Object>(); |
299 | renderingEnvironment.put(BIND_HELPER, this); |
300 | renderingEnvironment.put(BINDER_CLASS_PACKAGE, binderClassPackage); |
301 | bindMethods.add(new BindMethod<E, HC>(name, ir, busEventType, typeParameters, renderingEnvironment)); |
302 | } |
303 | this.javaExtractorEntries = javaExtractorEntries; |
304 | this.contextType = ((_Class) typeParameters[2]).clazz; |
305 | } |
306 | |
307 | public Collection<BindMethod<E,HC>> getBindMethods() { |
308 | return bindMethods; |
309 | } |
310 | |
311 | public Collection<JavaExtractorEntry> getJavaExtractorEntries() { |
312 | return javaExtractorEntries; |
313 | } |
314 | |
315 | public Class<?> getContextType() { |
316 | return contextType; |
317 | } |
318 | |
319 | } |
320 | |
321 | private File outputDir; |
322 | private ArrayList<JavaExtractorEntry> javaExtractorEntries = new ArrayList<JavaExtractorEntry>(); |
323 | |
324 | public JavaBinderCompiler(File outputDir) { |
325 | this.outputDir = outputDir; |
326 | } |
327 | |
328 | public <E, C, HC extends C, BC extends EventBus<E, ?, C, ?, ?, ?>> void compileJavaBinder(Class<HC> handlerClass, Class<BC> busClass, ClassLoader classLoader, TokenExpander tokenExpander) { |
329 | |
330 | try { |
331 | Map<String, Object> environment = new HashMap<String, Object>(); |
332 | environment.put(BINDER_CLASS_PACKAGE, getBinderClassPackage(handlerClass)); |
333 | environment.put("binderClassName", getBinderClassName(handlerClass)); |
334 | environment.put("binderClassComment", "Binds handler class "+handlerClass.getName()+" to event bus "+busClass.getName()); |
335 | |
336 | TypeVariable<Class<EventBus>>[] ebtp = EventBus.class.getTypeParameters(); |
337 | _Type[] eventBusTypeParameters = new _Type[ebtp.length]; |
338 | for (int i=0; i< ebtp.length; ++i) { |
339 | eventBusTypeParameters[i] = _Type.createType(ebtp[i]); |
340 | } |
341 | |
342 | List<List<PathEntry>> iPaths = inheritancePaths(busClass, EventBus.class); |
343 | Collections.sort(iPaths, new Comparator<List<PathEntry>>() { |
344 | |
345 | @Override |
346 | public int compare(List<PathEntry> o1, List<PathEntry> o2) { |
347 | int sizeDelta = o1.size() - o2.size(); |
348 | if (sizeDelta!=0) { |
349 | return sizeDelta; |
350 | } |
351 | return o1.hashCode() - o2.hashCode(); |
352 | } |
353 | }); |
354 | for (List<PathEntry> path: iPaths) { |
355 | for (PathEntry pe: path) { |
356 | pe.bind(eventBusTypeParameters); |
357 | } |
358 | break; |
359 | } |
360 | |
361 | _Type pType = eventBusTypeParameters[1]; |
362 | if (pType instanceof _TypeVariable) { |
363 | _Class to = new _Class(Integer.class); |
364 | for (int i=0; i<eventBusTypeParameters.length; ++i) { |
365 | if (pType.equals(eventBusTypeParameters[i])) { |
366 | eventBusTypeParameters[i] = to; |
367 | } else { |
368 | eventBusTypeParameters[i].bind((_TypeVariable) pType, to); |
369 | } |
370 | } |
371 | } |
372 | |
373 | Class<E> busEventType; |
374 | if (eventBusTypeParameters[0] instanceof _TypeVariable) { |
375 | busEventType = (Class<E>) Object.class; |
376 | _Type eType = eventBusTypeParameters[0]; |
377 | _Class to = new _Class(busEventType); |
378 | for (int i=0; i<eventBusTypeParameters.length; ++i) { |
379 | if (eType.equals(eventBusTypeParameters[i])) { |
380 | eventBusTypeParameters[i] = to; |
381 | } else { |
382 | eventBusTypeParameters[i].bind((_TypeVariable) eType, to); |
383 | } |
384 | } |
385 | } else if (eventBusTypeParameters[0] instanceof _Class) { |
386 | busEventType = (Class<E>) ((_Class) eventBusTypeParameters[0]).clazz; |
387 | } else if (eventBusTypeParameters[0] instanceof _ParameterizedType) { |
388 | busEventType = (Class<E>) ((_ParameterizedType) eventBusTypeParameters[0]).rawType; |
389 | } else { |
390 | throw new EventBusException("Unexpected type: "+eventBusTypeParameters[0]); |
391 | } |
392 | |
393 | if (eventBusTypeParameters[2] instanceof _TypeVariable) { |
394 | _Type eType = eventBusTypeParameters[2]; |
395 | _Class to = new _Class(Object.class); |
396 | for (int i=0; i<eventBusTypeParameters.length; ++i) { |
397 | if (eType.equals(eventBusTypeParameters[i])) { |
398 | eventBusTypeParameters[i] = to; |
399 | } else { |
400 | eventBusTypeParameters[i].bind((_TypeVariable) eType, to); |
401 | } |
402 | } |
403 | } |
404 | |
405 | environment.put("E", eventBusTypeParameters[0].toJavaDeclaration()); |
406 | environment.put("P", eventBusTypeParameters[1].toJavaDeclaration()); |
407 | environment.put("C", eventBusTypeParameters[2].toJavaDeclaration()); |
408 | environment.put("K", eventBusTypeParameters[3].toJavaDeclaration()); |
409 | environment.put("H", eventBusTypeParameters[4].toJavaDeclaration()); |
410 | environment.put("S", eventBusTypeParameters[5].toJavaDeclaration()); |
411 | |
412 | List<String> genericParameters = new ArrayList<String>(); |
413 | for (int i=0; i<eventBusTypeParameters.length; ++i) { |
414 | if (eventBusTypeParameters[i] instanceof _TypeVariable) { |
415 | genericParameters.add(eventBusTypeParameters[i].toJavaDeclaration()); |
416 | } |
417 | } |
418 | |
419 | if (genericParameters.isEmpty()) { |
420 | environment.put("genericParameters", ""); |
421 | } else { |
422 | StringBuilder sb = new StringBuilder("<"); |
423 | Iterator<String> gpit = genericParameters.iterator(); |
424 | while (gpit.hasNext()) { |
425 | sb.append(gpit.next()); |
426 | if (gpit.hasNext()) { |
427 | sb.append(", "); |
428 | } |
429 | } |
430 | sb.append(">"); |
431 | environment.put("genericParameters", sb.toString()); |
432 | } |
433 | |
434 | environment.put("B", toJavaDeclaration(busClass, eventBusTypeParameters)); |
435 | environment.put("I", toJavaDeclaration(handlerClass, eventBusTypeParameters)); |
436 | |
437 | IntrospectorBase<E,HC> introspector = new IntrospectorBase<E, HC>(classLoader, tokenExpander); |
438 | |
439 | environment.put("bindHelper", new BindHelper<E, HC>(introspector.introspect(handlerClass, busEventType), busEventType, eventBusTypeParameters, javaExtractorEntries, getBinderClassPackage(handlerClass))); |
440 | |
441 | String prefix = getClass().getPackage().getName().replace('.', '/'); |
442 | JxpPageSource pageSource = new ResourceStreamPageSource("/"+prefix); |
443 | JxpContext context = new JxpContext(pageSource); |
444 | JxpProcessor processor = new JxpProcessor(context); |
445 | |
446 | File packageDir = new File(outputDir, getBinderClassPackage(handlerClass).replace('.', File.separatorChar)); |
447 | packageDir.mkdirs(); |
448 | File outputFile = new File(packageDir, getBinderClassName(handlerClass)+".java"); |
449 | Writer writer = new FileWriter(outputFile); |
450 | try { |
451 | processor.process("JavaBinder.jxp", writer, environment); |
452 | } finally { |
453 | writer.close(); |
454 | } |
455 | } catch (Exception e) { |
456 | throw new EventBusException(e); |
457 | } |
458 | } |
459 | |
460 | private String toJavaDeclaration(Class<?> clazz, _Type[] typeParameters) { |
461 | _Class cls = new _Class(clazz); |
462 | cls.bind(new _TypeVariable("E"), typeParameters[0]); |
463 | cls.bind(new _TypeVariable("P"), typeParameters[1]); |
464 | cls.bind(new _TypeVariable("C"), typeParameters[2]); |
465 | cls.bind(new _TypeVariable("K"), typeParameters[3]); |
466 | cls.bind(new _TypeVariable("H"), typeParameters[4]); |
467 | cls.bind(new _TypeVariable("S"), typeParameters[5]); |
468 | |
469 | return cls.toJavaDeclaration(); |
470 | } |
471 | |
472 | protected String getBinderClassPackage(Class<?> handlerClass) { |
473 | return handlerClass.getPackage().getName(); |
474 | } |
475 | |
476 | protected String getBinderClassName(Class<?> handlerClass) { |
477 | int idx = handlerClass.getName().lastIndexOf('.'); |
478 | return (idx==-1 ? handlerClass.getName() : handlerClass.getName().substring(idx+1))+"JavaBinder"; |
479 | } |
480 | |
481 | private static abstract class _Type { |
482 | |
483 | abstract void bind(_TypeVariable from, _Type to); |
484 | |
485 | abstract String toJavaDeclaration(); |
486 | |
487 | static _Type createType(Type rType) { |
488 | if (rType instanceof TypeVariable<?>) { |
489 | return new _TypeVariable((TypeVariable<?>) rType); |
490 | } |
491 | |
492 | if (rType instanceof ParameterizedType) { |
493 | return new _ParameterizedType((ParameterizedType) rType); |
494 | } |
495 | |
496 | if (rType instanceof Class) { |
497 | return new _Class((Class) rType); |
498 | } |
499 | throw new UnsupportedOperationException(); |
500 | } |
501 | } |
502 | |
503 | private static class _Class extends _Type { |
504 | |
505 | private Class<?> clazz; |
506 | |
507 | public _Class(Class<?> clazz) { |
508 | this.clazz = clazz; |
509 | TypeVariable<?>[] ctp = clazz.getTypeParameters(); |
510 | this.typeParameters = new _Type[ctp.length]; |
511 | for (int i=0; i<ctp.length; ++i) { |
512 | typeParameters[i] = _Type.createType(ctp[i]); |
513 | } |
514 | } |
515 | |
516 | @Override |
517 | public String toString() { |
518 | return "_Class [clazz=" + clazz + "]"; |
519 | } |
520 | |
521 | _Type[] typeParameters; |
522 | |
523 | @Override |
524 | void bind(_TypeVariable from, _Type to) { |
525 | for (int i=0; i<typeParameters.length; ++i) { |
526 | if (from.equals(typeParameters[i])) { |
527 | typeParameters[i] = to; |
528 | } else { |
529 | typeParameters[i].bind(from, to); |
530 | } |
531 | } |
532 | } |
533 | |
534 | @Override |
535 | String toJavaDeclaration() { |
536 | StringBuilder sb = new StringBuilder(((Class) clazz).getCanonicalName()); |
537 | if (typeParameters.length>0) { |
538 | sb.append("<"); |
539 | for (int i=0; i<typeParameters.length; ++i) { |
540 | if (i>0) { |
541 | sb.append(", "); |
542 | } |
543 | sb.append(typeParameters[i].toJavaDeclaration()); |
544 | } |
545 | sb.append(">"); |
546 | } |
547 | return sb.toString(); |
548 | } |
549 | } |
550 | |
551 | private static class _TypeVariable extends _Type { |
552 | private String name; |
553 | |
554 | public _TypeVariable(TypeVariable<?> rTypeVariable) { |
555 | super(); |
556 | this.name = rTypeVariable.getName(); |
557 | } |
558 | |
559 | public _TypeVariable(String name) { |
560 | super(); |
561 | this.name = name; |
562 | } |
563 | |
564 | @Override |
565 | public int hashCode() { |
566 | final int prime = 31; |
567 | int result = 1; |
568 | result = prime * result + ((name == null) ? 0 : name.hashCode()); |
569 | return result; |
570 | } |
571 | |
572 | @Override |
573 | public String toString() { |
574 | return "_TypeVariable [name=" + name + "]"; |
575 | } |
576 | |
577 | @Override |
578 | public boolean equals(Object obj) { |
579 | if (this == obj) |
580 | return true; |
581 | if (obj == null) |
582 | return false; |
583 | if (getClass() != obj.getClass()) |
584 | return false; |
585 | _TypeVariable other = (_TypeVariable) obj; |
586 | if (name == null) { |
587 | if (other.name != null) |
588 | return false; |
589 | } else if (!name.equals(other.name)) |
590 | return false; |
591 | return true; |
592 | } |
593 | |
594 | public String getName() { |
595 | return name; |
596 | } |
597 | |
598 | public void setName(String name) { |
599 | this.name = name; |
600 | } |
601 | |
602 | @Override |
603 | void bind(_TypeVariable from, _Type to) { |
604 | if (from.getName().equals(name)) { |
605 | if (to instanceof _TypeVariable) { |
606 | name = ((_TypeVariable) to).name; |
607 | } else { |
608 | throw new UnsupportedOperationException(); |
609 | } |
610 | } |
611 | } |
612 | |
613 | @Override |
614 | String toJavaDeclaration() { |
615 | return name; |
616 | } |
617 | } |
618 | |
619 | private static class _ParameterizedType extends _Type { |
620 | Type rawType; |
621 | _Type[] actualTypeArguments; |
622 | |
623 | public _ParameterizedType(ParameterizedType pt) { |
624 | rawType = pt.getRawType(); |
625 | Type[] atp = pt.getActualTypeArguments(); |
626 | actualTypeArguments = new _Type[atp.length]; |
627 | for (int i=0; i<atp.length; ++i) { |
628 | actualTypeArguments[i]=_Type.createType(atp[i]); |
629 | } |
630 | } |
631 | |
632 | @Override |
633 | void bind(_TypeVariable from, _Type to) { |
634 | for (int i=0; i<actualTypeArguments.length; ++i) { |
635 | if (from.equals(actualTypeArguments[i])) { |
636 | actualTypeArguments[i] = to; |
637 | } else { |
638 | actualTypeArguments[i].bind(from, to); |
639 | } |
640 | } |
641 | } |
642 | |
643 | @Override |
644 | public String toString() { |
645 | return "_ParameterizedType [rawType=" + rawType |
646 | + ", actualTypeArguments=" |
647 | + Arrays.toString(actualTypeArguments) + "]"; |
648 | } |
649 | |
650 | @Override |
651 | String toJavaDeclaration() { |
652 | StringBuilder sb = new StringBuilder(((Class) rawType).getCanonicalName()); |
653 | sb.append("<"); |
654 | for (int i=0; i<actualTypeArguments.length; ++i) { |
655 | if (i>0) { |
656 | sb.append(", "); |
657 | } |
658 | sb.append(actualTypeArguments[i].toJavaDeclaration()); |
659 | } |
660 | sb.append(">"); |
661 | return sb.toString(); |
662 | } |
663 | |
664 | } |
665 | |
666 | private static class PathEntry { |
667 | Type sType; |
668 | Class<?> clazz; |
669 | public PathEntry(Type type, Class<?> clazz) { |
670 | super(); |
671 | if (clazz==null) { |
672 | throw new IllegalArgumentException(); |
673 | } |
674 | sType = type; |
675 | this.clazz = clazz; |
676 | } |
677 | |
678 | void bind(_Type[] typeParameters) { |
679 | TypeVariable<?>[] ctp = clazz.getTypeParameters(); |
680 | Type[] atp = ((ParameterizedType) sType).getActualTypeArguments(); |
681 | for (int i=0; i<ctp.length; ++i) { |
682 | _TypeVariable from = (_TypeVariable) _Type.createType(ctp[i]); |
683 | _Type to = _Type.createType(atp[i]); |
684 | for (int j=0; j<typeParameters.length; ++j) { |
685 | if (from.equals(typeParameters[j])) { |
686 | typeParameters[j] = to; |
687 | } else { |
688 | typeParameters[j].bind(from, to); |
689 | } |
690 | } |
691 | } |
692 | } |
693 | |
694 | @Override |
695 | public String toString() { |
696 | TypeVariable<?>[] typeParameters = clazz.getTypeParameters(); |
697 | return "PathEntry[type = "+sType+", class = "+clazz+", type parameters = "+(typeParameters==null ? "(no type parameters)" : Arrays.toString(typeParameters))+"]"; |
698 | } |
699 | } |
700 | |
701 | private static <S> List<List<PathEntry>> inheritancePaths(Class<? extends S> from, Class<S> to) { |
702 | List<List<PathEntry>> ret = new ArrayList<List<PathEntry>>(); |
703 | if (from.equals(to)) { |
704 | ret.add(new ArrayList<PathEntry>()); |
705 | } else if (to.isAssignableFrom(from)) { |
706 | Class<?> fromSuperClass = from.getSuperclass(); |
707 | if (fromSuperClass!=null) { |
708 | for (List<PathEntry> path: inheritancePaths((Class<? extends S>) fromSuperClass, to)) { |
709 | Type gsc = from.getGenericSuperclass(); |
710 | Class<? extends S> sc = (Class<? extends S>) from.getSuperclass(); |
711 | if (sc!=null) { |
712 | path.add(new PathEntry(gsc, sc)); |
713 | ret.add(path); |
714 | } |
715 | } |
716 | } |
717 | |
718 | Type[] gi = from.getGenericInterfaces(); |
719 | Class[] si = from.getInterfaces(); |
720 | for (int i=0; i<gi.length; ++i) { |
721 | for (List<PathEntry> path: inheritancePaths((Class<? extends S>) si[i], to)) { |
722 | path.add(new PathEntry(gi[i], si[i])); |
723 | ret.add(path); |
724 | } |
725 | } |
726 | } |
727 | |
728 | return ret; |
729 | } |
730 | |
731 | public static class ConvertersBundle extends AbstractReflectiveAtomicConvertersBundle { |
732 | |
733 | @ConverterMethod |
734 | public WriterRenderer toWriterRenderer(final CompositePredicate predicate) { |
735 | return new WriterRenderer() { |
736 | |
737 | @Override |
738 | public boolean render( |
739 | Writer out, |
740 | Map<String, Object> environment, |
741 | Context context, |
742 | String profile, |
743 | Locale locale, |
744 | File outputDir) throws RenderingException { |
745 | |
746 | if (!JAVA_INLINE.equals(profile)) { |
747 | return false; |
748 | } |
749 | |
750 | try { |
751 | out.write("new "+predicate.getClass().getCanonicalName()+"(0, null, "); |
752 | Iterator<Predicate> pit = predicate.getParts().iterator(); |
753 | while (pit.hasNext()) { |
754 | Predicate part = pit.next(); |
755 | WriterRenderer pwr = ConvertingService.convert(part, WriterRenderer.class); |
756 | if (pwr==null) { |
757 | throw new RenderingException("Cannot render "+part.getClass().getName()); |
758 | } |
759 | if (!pwr.render(out, environment, context, profile, locale, outputDir)) { |
760 | throw new RenderingException("Cannot render "+part.getClass().getName()); |
761 | } |
762 | if (pit.hasNext()) { |
763 | out.write(", "); |
764 | } |
765 | } |
766 | out.write(")"); |
767 | } catch (IOException e) { |
768 | throw new RenderingException(e); |
769 | } |
770 | |
771 | return true; |
772 | } |
773 | }; |
774 | } |
775 | |
776 | @ConverterMethod |
777 | public WriterRenderer toWriterRenderer(final BinaryExtractor extractor) { |
778 | return new WriterRenderer() { |
779 | |
780 | @Override |
781 | public boolean render( |
782 | Writer out, |
783 | Map<String, Object> environment, |
784 | Context context, |
785 | String profile, |
786 | Locale locale, |
787 | File outputDir) throws RenderingException { |
788 | |
789 | if (!JAVA_INLINE.equals(profile)) { |
790 | return false; |
791 | } |
792 | |
793 | try { |
794 | out.write("new "+extractor.getClass().getCanonicalName()+"(0, null, "); |
795 | Extractor leftExtractor = extractor.getLeftExtractor(); |
796 | WriterRenderer lwr = ConvertingService.convert(leftExtractor, WriterRenderer.class); |
797 | if (lwr==null) { |
798 | throw new RenderingException("Cannot render "+leftExtractor.getClass().getName()); |
799 | } |
800 | if (!lwr.render(out, environment, context, profile, locale, outputDir)) { |
801 | throw new RenderingException("Cannot render "+leftExtractor.getClass().getName()); |
802 | } |
803 | out.write(", "); |
804 | Extractor rightExtractor = extractor.getRightExtractor(); |
805 | WriterRenderer rwr = ConvertingService.convert(rightExtractor, WriterRenderer.class); |
806 | if (rwr==null) { |
807 | throw new RenderingException("Cannot render "+rightExtractor.getClass().getName()); |
808 | } |
809 | if (!rwr.render(out, environment, context, profile, locale, outputDir)) { |
810 | throw new RenderingException("Cannot render "+rightExtractor.getClass().getName()); |
811 | } |
812 | |
813 | if (extractor instanceof Equal) { |
814 | out.write(", " + ((Equal) extractor).isIdentity()); |
815 | } else if (extractor instanceof NotEqual) { |
816 | out.write(", " + ((NotEqual) extractor).isIdentity()); |
817 | } |
818 | |
819 | out.write(")"); |
820 | } catch (IOException e) { |
821 | throw new RenderingException(e); |
822 | } |
823 | |
824 | return true; |
825 | } |
826 | }; |
827 | } |
828 | |
829 | @ConverterMethod |
830 | public WriterRenderer toWriterRenderer(final Constant extractor) { |
831 | return new WriterRenderer() { |
832 | |
833 | @Override |
834 | public boolean render( |
835 | Writer out, |
836 | Map<String, Object> environment, |
837 | Context context, |
838 | String profile, |
839 | Locale locale, |
840 | File outputDir) throws RenderingException { |
841 | |
842 | if (!JAVA_INLINE.equals(profile)) { |
843 | return false; |
844 | } |
845 | |
846 | try { |
847 | out.write("new "+extractor.getClass().getCanonicalName()+"("); |
848 | if (extractor.getValue() == null) { |
849 | out.write("null"); |
850 | } else if (extractor.getValue() instanceof String) { |
851 | out.write("\""); |
852 | out.write(StringEscapeUtils.escapeJava(extractor.getValue().toString())); |
853 | out.write("\""); |
854 | } else if (extractor.getValue() instanceof Character) { |
855 | out.write("\'"); |
856 | out.write(StringEscapeUtils.escapeJava(extractor.getValue().toString())); |
857 | out.write("\'"); |
858 | } else { |
859 | out.write("("+extractor.getValue().getClass().getCanonicalName()+") "); |
860 | out.write(String.valueOf(extractor.getValue())); |
861 | } |
862 | out.write(")"); |
863 | } catch (IOException e) { |
864 | throw new RenderingException(e); |
865 | } |
866 | |
867 | return true; |
868 | } |
869 | }; |
870 | } |
871 | |
872 | @ConverterMethod |
873 | public WriterRenderer toWriterRenderer(final False extractor) { |
874 | return new WriterRenderer() { |
875 | |
876 | @Override |
877 | public boolean render( |
878 | Writer out, |
879 | Map<String, Object> environment, |
880 | Context context, |
881 | String profile, |
882 | Locale locale, |
883 | File outputDir) throws RenderingException { |
884 | |
885 | if (!JAVA_INLINE.equals(profile)) { |
886 | return false; |
887 | } |
888 | |
889 | try { |
890 | out.write(False.class.getCanonicalName()+".getInstance()"); |
891 | } catch (IOException e) { |
892 | throw new RenderingException(e); |
893 | } |
894 | |
895 | return true; |
896 | } |
897 | }; |
898 | } |
899 | |
900 | @ConverterMethod |
901 | public WriterRenderer toWriterRenderer(final IndexedExtractor extractor) { |
902 | return new WriterRenderer() { |
903 | |
904 | @Override |
905 | public boolean render( |
906 | Writer out, |
907 | Map<String, Object> environment, |
908 | Context context, |
909 | String profile, |
910 | Locale locale, |
911 | File outputDir) throws RenderingException { |
912 | |
913 | if (!JAVA_INLINE.equals(profile)) { |
914 | return false; |
915 | } |
916 | |
917 | try { |
918 | out.write("new "+extractor.getClass().getCanonicalName()+"("); |
919 | out.write(String.valueOf(extractor.getIndex())); |
920 | out.write(")"); |
921 | } catch (IOException e) { |
922 | throw new RenderingException(e); |
923 | } |
924 | |
925 | return true; |
926 | } |
927 | }; |
928 | } |
929 | |
930 | @ConverterMethod |
931 | public WriterRenderer toWriterRenderer(final InstanceOfPredicate extractor) { |
932 | return new WriterRenderer() { |
933 | |
934 | @Override |
935 | public boolean render( |
936 | Writer out, |
937 | Map<String, Object> environment, |
938 | Context context, |
939 | String profile, |
940 | Locale locale, |
941 | File outputDir) throws RenderingException { |
942 | |
943 | if (!JAVA_INLINE.equals(profile)) { |
944 | return false; |
945 | } |
946 | |
947 | try { |
948 | out.write("new "+extractor.getClass().getCanonicalName()+"("); |
949 | WriterRenderer er = ConvertingService.convert(extractor.getExtractor(), WriterRenderer.class); |
950 | if (er==null) { |
951 | throw new RenderingException("Cannot render "+extractor.getExtractor().getClass().getName()); |
952 | } |
953 | if (!er.render(out, environment, context, profile, locale, outputDir)) { |
954 | throw new RenderingException("Cannot render "+extractor.getExtractor().getClass().getName()); |
955 | } |
956 | out.write(", "); |
957 | out.write(extractor.getInstanceType().getName()+".class"); |
958 | out.write(")"); |
959 | } catch (IOException e) { |
960 | throw new RenderingException(e); |
961 | } |
962 | |
963 | return true; |
964 | } |
965 | }; |
966 | } |
967 | |
968 | @ConverterMethod |
969 | public WriterRenderer toWriterRenderer(final MappedExtractor extractor) { |
970 | return new WriterRenderer() { |
971 | |
972 | @Override |
973 | public boolean render( |
974 | Writer out, |
975 | Map<String, Object> environment, |
976 | Context context, |
977 | String profile, |
978 | Locale locale, |
979 | File outputDir) throws RenderingException { |
980 | |
981 | if (!JAVA_INLINE.equals(profile)) { |
982 | return false; |
983 | } |
984 | |
985 | try { |
986 | out.write("new "+extractor.getClass().getCanonicalName()+"("); |
987 | WriterRenderer ewr = ConvertingService.convert(extractor.getTarget(), WriterRenderer.class); |
988 | if (ewr==null) { |
989 | throw new RenderingException("Cannot render "+extractor.getTarget().getClass().getName()); |
990 | } |
991 | if (!ewr.render(out, environment, context, profile, locale, outputDir)) { |
992 | throw new RenderingException("Cannot render "+extractor.getTarget().getClass().getName()); |
993 | } |
994 | |
995 | out.write(", new int[] {"); |
996 | int[] map = extractor.getMap(); |
997 | for (int i=0; i<map.length; ++i) { |
998 | if (i>0) { |
999 | out.write(", "); |
1000 | } |
1001 | out.write(String.valueOf(map[i])); |
1002 | } |
1003 | |
1004 | out.write("})"); |
1005 | } catch (IOException e) { |
1006 | throw new RenderingException(e); |
1007 | } |
1008 | |
1009 | return true; |
1010 | } |
1011 | }; |
1012 | } |
1013 | |
1014 | // @ConverterMethod |
1015 | // public WriterRenderer toWriterRenderer(final MethodExtractor extractor) { |
1016 | // return new WriterRenderer() { |
1017 | // |
1018 | // @Override |
1019 | // public boolean render( |
1020 | // Writer out, |
1021 | // Map<String, Object> environment, |
1022 | // Context context, |
1023 | // String profile, |
1024 | // Locale locale, |
1025 | // File outputDir) throws RenderingException { |
1026 | // |
1027 | // if (!JAVA_INLINE.equals(profile)) { |
1028 | // return false; |
1029 | // } |
1030 | // |
1031 | // try { |
1032 | // out.write("new "+extractor.getClass().getCanonicalName()+"("); |
1033 | // |
1034 | // out.write("777~~~Not implemented~~~777"); |
1035 | // |
1036 | // out.write(")"); |
1037 | // } catch (IOException e) { |
1038 | // throw new RenderingException(e); |
1039 | // } |
1040 | // |
1041 | // return true; |
1042 | // } |
1043 | // }; |
1044 | // } |
1045 | |
1046 | @ConverterMethod |
1047 | public WriterRenderer toWriterRenderer(final Not extractor) { |
1048 | return new WriterRenderer() { |
1049 | |
1050 | @Override |
1051 | public boolean render( |
1052 | Writer out, |
1053 | Map<String, Object> environment, |
1054 | Context context, |
1055 | String profile, |
1056 | Locale locale, |
1057 | File outputDir) throws RenderingException { |
1058 | |
1059 | if (!JAVA_INLINE.equals(profile)) { |
1060 | return false; |
1061 | } |
1062 | |
1063 | try { |
1064 | out.write("new "+extractor.getClass().getCanonicalName()+"("); |
1065 | |
1066 | WriterRenderer ewr = ConvertingService.convert(extractor.getPredicate(), WriterRenderer.class); |
1067 | if (ewr==null) { |
1068 | throw new RenderingException("Cannot render "+extractor.getPredicate().getClass().getName()); |
1069 | } |
1070 | if (!ewr.render(out, environment, context, profile, locale, outputDir)) { |
1071 | throw new RenderingException("Cannot render "+extractor.getPredicate().getClass().getName()); |
1072 | } |
1073 | |
1074 | out.write(")"); |
1075 | } catch (IOException e) { |
1076 | throw new RenderingException(e); |
1077 | } |
1078 | |
1079 | return true; |
1080 | } |
1081 | }; |
1082 | } |
1083 | |
1084 | // @ConverterMethod |
1085 | // public WriterRenderer toWriterRenderer(final TimeIntervalPredicate extractor) { |
1086 | // return new WriterRenderer() { |
1087 | // |
1088 | // @Override |
1089 | // public boolean render( |
1090 | // Writer out, |
1091 | // Map<String, Object> environment, |
1092 | // Context context, |
1093 | // String profile, |
1094 | // Locale locale, |
1095 | // File outputDir) throws RenderingException { |
1096 | // |
1097 | // if (!JAVA_INLINE.equals(profile)) { |
1098 | // return false; |
1099 | // } |
1100 | // |
1101 | // try { |
1102 | // out.write("new "+extractor.getClass().getCanonicalName()+"("); |
1103 | // |
1104 | // out.write("777~~~Not implemented~~~777"); |
1105 | // |
1106 | // out.write(")"); |
1107 | // } catch (IOException e) { |
1108 | // throw new RenderingException(e); |
1109 | // } |
1110 | // |
1111 | // return true; |
1112 | // } |
1113 | // }; |
1114 | // } |
1115 | |
1116 | @ConverterMethod |
1117 | public WriterRenderer toWriterRenderer(final True extractor) { |
1118 | return new WriterRenderer() { |
1119 | |
1120 | @Override |
1121 | public boolean render( |
1122 | Writer out, |
1123 | Map<String, Object> environment, |
1124 | Context context, |
1125 | String profile, |
1126 | Locale locale, |
1127 | File outputDir) throws RenderingException { |
1128 | |
1129 | if (!JAVA_INLINE.equals(profile)) { |
1130 | return false; |
1131 | } |
1132 | |
1133 | try { |
1134 | out.write(True.class.getCanonicalName()+".getInstance()"); |
1135 | } catch (IOException e) { |
1136 | throw new RenderingException(e); |
1137 | } |
1138 | |
1139 | return true; |
1140 | } |
1141 | }; |
1142 | } |
1143 | |
1144 | @ConverterMethod |
1145 | public WriterRenderer toWriterRenderer(final JavaExtractor extractor) { |
1146 | return new WriterRenderer() { |
1147 | |
1148 | @Override |
1149 | public boolean render( |
1150 | Writer out, |
1151 | Map<String, Object> environment, |
1152 | Context context, |
1153 | String profile, |
1154 | Locale locale, |
1155 | File outputDir) throws RenderingException { |
1156 | |
1157 | if (!JAVA_INLINE.equals(profile)) { |
1158 | return false; |
1159 | } |
1160 | |
1161 | try { |
1162 | // Try to find existing compiled predicate/extractor |
1163 | BindHelper bindHelper = (BindHelper) environment.get(BIND_HELPER); |
1164 | String packageName = (String) environment.get(BINDER_CLASS_PACKAGE); |
1165 | for (JavaExtractorEntry jee: (Collection<JavaExtractorEntry>) bindHelper.getJavaExtractorEntries()) { |
1166 | if (packageName.equals(jee.getPackageName())) { |
1167 | ComparisonResult cr = jee.getJavaExtractor().compareTo(extractor); // TODO - verify right order. |
1168 | if (cr!=null) { |
1169 | switch (cr.getType()) { |
1170 | case OPPOSITE: |
1171 | out.write("new "+Not.class.getCanonicalName()+"("); |
1172 | case EQUAL: |
1173 | out.write("new "+jee.getClassName()+"("); |
1174 | if (cr.getIndexMap()==null) { |
1175 | out.write("null"); |
1176 | } else { |
1177 | out.write("new int[] {"); |
1178 | for (int i=0; i<cr.getIndexMap().length; ++i) { |
1179 | if (i>0) { |
1180 | out.write(", "); |
1181 | } |
1182 | out.write(String.valueOf(i)); |
1183 | } |
1184 | out.write("}"); |
1185 | } |
1186 | out.write(")"); |
1187 | |
1188 | if (ComparisonResult.Type.OPPOSITE.equals(cr.getType())) { |
1189 | out.write(")"); |
1190 | } |
1191 | |
1192 | return true; |
1193 | } |
1194 | } |
1195 | } |
1196 | } |
1197 | |
1198 | File outputFile; |
1199 | int i = 0; |
1200 | do { |
1201 | outputFile = new File(outputDir, packageName.replace('.', File.separatorChar)+File.separator+prefix(extractor)+(++i)+".java"); |
1202 | } while (outputFile.exists()); |
1203 | |
1204 | outputFile.getParentFile().mkdirs(); |
1205 | |
1206 | JavaExtractorEntry newJee = new JavaExtractorEntry(extractor, packageName, prefix(extractor)+i); |
1207 | bindHelper.getJavaExtractorEntries().add(newJee); |
1208 | |
1209 | File identityFile = new File(outputFile.getParentFile(), prefix(extractor)+i+".identity"); |
1210 | ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(identityFile)); |
1211 | try { |
1212 | oos.writeObject(extractor.getIdentity()); |
1213 | } finally { |
1214 | oos.close(); |
1215 | } |
1216 | |
1217 | String prefix = getClass().getPackage().getName().replace('.', '/'); |
1218 | JxpPageSource pageSource = new ResourceStreamPageSource("/"+prefix); |
1219 | JxpContext jxpContext = new JxpContext(pageSource); |
1220 | JxpProcessor processor = new JxpProcessor(jxpContext); |
1221 | Writer writer = new FileWriter(outputFile); |
1222 | CompileHelper compileHelper = new CompileHelper(packageName, prefix(extractor)+i, bindHelper.getContextType(), extractor); |
1223 | environment.put("compileHelper", compileHelper); |
1224 | processor.process("JavaExtractor.jxp", writer, environment); |
1225 | writer.close(); |
1226 | |
1227 | out.write("new "+prefix(extractor)+i+"(null)"); |
1228 | } catch (Exception e) { |
1229 | throw new RenderingException(e); |
1230 | } |
1231 | |
1232 | return true; |
1233 | } |
1234 | }; |
1235 | } |
1236 | |
1237 | private static String prefix(Extractor extractor) { |
1238 | return extractor instanceof Predicate ? "Predicate" : "Extractor"; |
1239 | } |
1240 | |
1241 | // /** |
1242 | // * For testing. |
1243 | // * @param extractor |
1244 | // * @return |
1245 | // */ |
1246 | // @ConverterMethod |
1247 | // public WriterRenderer toWriterRenderer(final Extractor extractor) { |
1248 | // return new WriterRenderer() { |
1249 | // |
1250 | // @Override |
1251 | // public boolean render( |
1252 | // Writer out, |
1253 | // Map<String, Object> environment, |
1254 | // Context context, |
1255 | // String profile, |
1256 | // Locale locale, |
1257 | // File outputDir) throws RenderingException { |
1258 | // |
1259 | // if (!JAVA_INLINE.equals(profile)) { |
1260 | // return false; |
1261 | // } |
1262 | // |
1263 | // try { |
1264 | // out.write(extractor.toString()); |
1265 | // } catch (IOException e) { |
1266 | // throw new RenderingException(e); |
1267 | // } |
1268 | // |
1269 | // return true; |
1270 | // } |
1271 | // }; |
1272 | // } |
1273 | |
1274 | } |
1275 | |
1276 | private static class CompileHelper { |
1277 | |
1278 | private String packageName; |
1279 | private String className; |
1280 | private _Type[] extractorTypeParameters; |
1281 | private JavaExtractor javaExtractor; |
1282 | |
1283 | public CompileHelper(String packageName, String className, Class<?> contextType, JavaExtractor javaExtractor) { |
1284 | this.packageName = packageName; |
1285 | this.className = className; |
1286 | this.javaExtractor = javaExtractor; |
1287 | |
1288 | TypeVariable<Class<Extractor>>[] etp = Extractor.class.getTypeParameters(); |
1289 | extractorTypeParameters = new _Type[etp.length]; |
1290 | for (int i=0; i< etp.length; ++i) { |
1291 | extractorTypeParameters[i] = _Type.createType(etp[i]); |
1292 | } |
1293 | |
1294 | List<List<PathEntry>> iPaths = inheritancePaths(javaExtractor.getClass(), Extractor.class); |
1295 | Collections.sort(iPaths, new Comparator<List<PathEntry>>() { |
1296 | |
1297 | @Override |
1298 | public int compare(List<PathEntry> o1, List<PathEntry> o2) { |
1299 | int sizeDelta = o1.size() - o2.size(); |
1300 | if (sizeDelta!=0) { |
1301 | return sizeDelta; |
1302 | } |
1303 | return o1.hashCode() - o2.hashCode(); |
1304 | } |
1305 | }); |
1306 | |
1307 | for (List<PathEntry> path: iPaths) { |
1308 | for (PathEntry pe: path) { |
1309 | pe.bind(extractorTypeParameters); |
1310 | } |
1311 | break; |
1312 | } |
1313 | |
1314 | _Type tType = extractorTypeParameters[0]; // T |
1315 | if (tType instanceof _TypeVariable) { |
1316 | _Class to = new _Class(Object.class); |
1317 | for (int i=0; i<extractorTypeParameters.length; ++i) { |
1318 | if (tType.equals(extractorTypeParameters[i])) { |
1319 | extractorTypeParameters[i] = to; |
1320 | } else { |
1321 | extractorTypeParameters[i].bind((_TypeVariable) tType, to); |
1322 | } |
1323 | } |
1324 | } |
1325 | |
1326 | _Type vType = extractorTypeParameters[1]; // V |
1327 | if (vType instanceof _TypeVariable) { // redundant |
1328 | _Class to = new _Class(javaExtractor instanceof Predicate ? Boolean.class : Object.class); |
1329 | for (int i=0; i<extractorTypeParameters.length; ++i) { |
1330 | if (vType.equals(extractorTypeParameters[i])) { |
1331 | extractorTypeParameters[i] = to; |
1332 | } else { |
1333 | extractorTypeParameters[i].bind((_TypeVariable) vType, to); |
1334 | } |
1335 | } |
1336 | } |
1337 | |
1338 | _Type cType = extractorTypeParameters[2]; // C |
1339 | if (cType instanceof _TypeVariable) { |
1340 | _Class to = new _Class(contextType); |
1341 | for (int i=0; i<extractorTypeParameters.length; ++i) { |
1342 | if (cType.equals(extractorTypeParameters[i])) { |
1343 | extractorTypeParameters[i] = to; |
1344 | } else { |
1345 | extractorTypeParameters[i].bind((_TypeVariable) cType, to); |
1346 | } |
1347 | } |
1348 | } |
1349 | } |
1350 | |
1351 | public String getPackageName() { |
1352 | return packageName; |
1353 | } |
1354 | |
1355 | public String getClassName() { |
1356 | return className; |
1357 | } |
1358 | |
1359 | public String getT() { |
1360 | return extractorTypeParameters[0].toJavaDeclaration(); |
1361 | } |
1362 | |
1363 | public String getV() { |
1364 | return extractorTypeParameters[1].toJavaDeclaration(); |
1365 | } |
1366 | |
1367 | public String getC() { |
1368 | return extractorTypeParameters[2].toJavaDeclaration(); |
1369 | } |
1370 | |
1371 | public boolean isPredicate() { |
1372 | return javaExtractor instanceof Predicate; |
1373 | } |
1374 | |
1375 | public boolean isContextDependent() { |
1376 | return javaExtractor.isContextDependent(); |
1377 | } |
1378 | |
1379 | public String getParameterIndicesCSV() { |
1380 | StringBuilder ret = new StringBuilder(); |
1381 | for (Object pi: javaExtractor.parameterIndices()) { |
1382 | if (ret.length()>0) { |
1383 | ret.append(", "); |
1384 | } |
1385 | ret.append(pi); |
1386 | } |
1387 | return ret.toString(); |
1388 | } |
1389 | |
1390 | public Iterable<JavaExtractor.Parameter> getParameters() { |
1391 | return javaExtractor.getParameters(); |
1392 | } |
1393 | |
1394 | public String maybeCastParameter(JavaExtractor.Parameter parameter) { |
1395 | Class<?> pType = parameter.getType(); |
1396 | Class<?> tType = ((_Class) extractorTypeParameters[0]).clazz; |
1397 | if (pType.isAssignableFrom(tType)) { |
1398 | return ""; |
1399 | } |
1400 | |
1401 | if (pType.isPrimitive()) { |
1402 | if (boolean.class.equals(pType)) { |
1403 | return "("+Boolean.class.getName()+")"; |
1404 | } else if (char.class.equals(pType)) { |
1405 | return "("+Character.class.getName()+")"; |
1406 | } else if (byte.class.equals(pType)) { |
1407 | return "("+Byte.class.getName()+")"; |
1408 | } else if (short.class.equals(pType)) { |
1409 | return "("+Short.class.getName()+")"; |
1410 | } else if (int.class.equals(pType)) { |
1411 | return "("+Integer.class.getName()+")"; |
1412 | } else if (long.class.equals(pType)) { |
1413 | return "("+Long.class.getName()+")"; |
1414 | } else if (float.class.equals(pType)) { |
1415 | return "("+Float.class.getName()+")"; |
1416 | } else if (double.class.equals(pType)) { |
1417 | return "("+Double.class.getName()+")"; |
1418 | } else if (void.class.equals(pType)) { |
1419 | throw new IllegalArgumentException("Parameter type cannot be void"); |
1420 | } |
1421 | } |
1422 | |
1423 | return "("+pType.getCanonicalName()+")"; |
1424 | } |
1425 | |
1426 | public String getExpression() { |
1427 | return javaExtractor.getExpression(); |
1428 | } |
1429 | |
1430 | public String getEscapedExpression() { |
1431 | return StringEscapeUtils.escapeJava(javaExtractor.getExpression()); |
1432 | } |
1433 | } |
1434 | |
1435 | /** |
1436 | * Compiles bindings for classes specified in the command line. Uses system properties for |
1437 | * token expansion. |
1438 | * @param args |
1439 | * @throws Exception |
1440 | */ |
1441 | @SuppressWarnings("unchecked") |
1442 | public static void main(String[] args) throws Exception { |
1443 | System.out.println("Parameters: <output directory> <bus class> <handler classes>"); |
1444 | JavaBinderCompiler compiler = new JavaBinderCompiler(new File(args[0])); |
1445 | Class<EventBus> busClass = (Class<EventBus>) Class.forName(args[1]); |
1446 | TokenExpander tokenExpander = new TokenExpander(new TokenSource() { |
1447 | |
1448 | @Override |
1449 | public String getToken(String name) { |
1450 | return System.getProperty(name); |
1451 | } |
1452 | |
1453 | }); |
1454 | for (int i=2; i<args.length; ++i) { |
1455 | Class<?> handlerClass = Class.forName(args[i]); |
1456 | System.out.println("Compiling Java binder for "+handlerClass.getName()); |
1457 | compiler.compileJavaBinder(handlerClass, busClass, null, tokenExpander); |
1458 | } |
1459 | } |
1460 | } |