    001    package com.hammurapi.eventbus;
    002
    003    import java.io.File;
    004    import java.io.FileOutputStream;
    005    import java.io.FileWriter;
    006    import java.io.IOException;
    007    import java.io.ObjectOutputStream;
    008    import java.io.StringWriter;
    009    import java.io.Writer;
    010    import java.lang.reflect.Method;
    011    import java.lang.reflect.ParameterizedType;
    012    import java.lang.reflect.Type;
    013    import java.lang.reflect.TypeVariable;
    014    import java.util.ArrayList;
    015    import java.util.Arrays;
    016    import java.util.Collection;
    017    import java.util.Collections;
    018    import java.util.Comparator;
    019    import java.util.HashMap;
    020    import java.util.HashSet;
    021    import java.util.Iterator;
    022    import java.util.List;
    023    import java.util.Locale;
    024    import java.util.Map;
    025    import java.util.Set;
    026
    027    import org.apache.commons.lang.StringEscapeUtils;
    028    import org.onemind.jxp.JxpContext;
    029    import org.onemind.jxp.JxpPageSource;
    030    import org.onemind.jxp.JxpProcessor;
    031    import org.onemind.jxp.ResourceStreamPageSource;
    032
    033    import com.hammurapi.common.Context;
    034    import com.hammurapi.common.TokenExpander;
    035    import com.hammurapi.common.TokenExpander.TokenSource;
    036    import com.hammurapi.convert.AbstractReflectiveAtomicConvertersBundle;
    037    import com.hammurapi.convert.ConverterMethod;
    038    import com.hammurapi.convert.ConvertingService;
    039    import com.hammurapi.eventbus.IntrospectorBase.IntrospectionResult;
    040    import com.hammurapi.extract.BinaryExtractor;
    041    import com.hammurapi.extract.ComparisonResult;
    042    import com.hammurapi.extract.CompositePredicate;
    043    import com.hammurapi.extract.Constant;
    044    import com.hammurapi.extract.Equal;
    045    import com.hammurapi.extract.Extractor;
    046    import com.hammurapi.extract.False;
    047    import com.hammurapi.extract.IndexedExtractor;
    048    import com.hammurapi.extract.InstanceOfPredicate;
    049    import com.hammurapi.extract.MappedExtractor;
    050    import com.hammurapi.extract.Not;
    051    import com.hammurapi.extract.NotEqual;
    052    import com.hammurapi.extract.Predicate;
    053    import com.hammurapi.extract.True;
    054    import com.hammurapi.extract.java.JavaExtractor;
    055    import com.hammurapi.render.RenderingException;
    056    import com.hammurapi.render.WriterRenderer;
    057
    058    /**
    059     * This class generates source code for Java binding class.
    060     * By default binder classes are put to the same package as handler classes and have postfix ''JavaBinder''.
    061     * This behavior can be changed by subclassing the compiler. The class keeps track of generated predicate
    062     * classes and attempts to reuse them.
    063     *
    064     * @author Pavel Vlasov
    065     *
    066     */
    067    public class JavaBinderCompiler {
    068
    069            protected static final String BINDER_CLASS_PACKAGE = "binderClassPackage";
    070            private static final String JAVA_INLINE = "java_inline";
    071            private static final String JAVA = "java";
    072            private static final String BIND_HELPER = "bindHelper";
    073
    074            private class EventDispatchContextDescriptor {
    075
    076                    private Class<EventDispatchContext> contextClass;
    077                    private _Type[] contextTypeParameters;
    078
    079                    public EventDispatchContextDescriptor(Class<EventDispatchContext> contextClass, _Type[] eventBusTypeParameters) {
    080                            this.contextClass = contextClass;
    081
    082                            TypeVariable<Class<EventDispatchContext>>[] cctp = EventDispatchContext.class.getTypeParameters();
    083                            contextTypeParameters = new _Type[cctp.length];
    084                            for (int i=0; i< cctp.length; ++i) {
    085                                    contextTypeParameters[i] = _Type.createType(cctp[i]);
    086                            }
    087
    088                            List<List<PathEntry>> iPaths = inheritancePaths(contextClass, EventDispatchContext.class);
    089                            Collections.sort(iPaths, new Comparator<List<PathEntry>>() {
    090
    091                                    @Override
    092                                    public int compare(List<PathEntry> o1, List<PathEntry> o2) {
    093                                            int sizeDelta =  o1.size() - o2.size();
    094                                            if (sizeDelta!=0) {
    095                                                    return sizeDelta;
    096                                            }
    097                                            return o1.hashCode() - o2.hashCode();
    098                                    }
    099                            });
    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    }