Package org.jboss.errai.ioc.rebind

Source Code of org.jboss.errai.ioc.rebind.IOCGenerator

/*
* Copyright 2010 JBoss, a divison Red Hat, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.jboss.errai.ioc.rebind;

import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.*;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import org.jboss.errai.bus.client.ErraiBus;
import org.jboss.errai.bus.client.api.MessageCallback;
import org.jboss.errai.bus.client.framework.MessageBus;
import org.jboss.errai.bus.rebind.ProcessingContext;
import org.jboss.errai.bus.rebind.ScannerSingleton;
import org.jboss.errai.bus.server.ErraiBootstrapFailure;
import org.jboss.errai.bus.server.service.metadata.MetaDataScanner;
import org.jboss.errai.ioc.client.InterfaceInjectionContext;
import org.jboss.errai.ioc.client.api.*;
import org.jboss.errai.ioc.rebind.ioc.*;

import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

/**
* The main generator class for the errai-ioc framework.
*/
public class IOCGenerator extends Generator {
    /**
     * Simple name of class to be generated
     */
    private String className = null;

    /**
     * Package name of class to be generated
     */
    private String packageName = null;

    private TypeOracle typeOracle;

    private ProcessingContext procContext;
    private InjectorFactory injectFactory;
    private ProcessorFactory procFactory;

    private List<Runnable> deferredTasks = new LinkedList<Runnable>();


    public IOCGenerator() {
    }

    public IOCGenerator(ProcessingContext processingContext) {
        this();
        this.procContext = processingContext;
        this.typeOracle = processingContext.getOracle();
        this.injectFactory = new InjectorFactory(processingContext);
        this.procFactory = new ProcessorFactory(injectFactory);
        defaultConfigureProcessor();
    }

    @Override
    public String generate(TreeLogger logger, GeneratorContext context, String typeName)
            throws UnableToCompleteException {
        typeOracle = context.getTypeOracle();

        try {
            // get classType and save instance variables

            JClassType classType = typeOracle.getType(typeName);
            packageName = classType.getPackage().getName();
            className = classType.getSimpleSourceName() + "Impl";

            logger.log(TreeLogger.INFO, "Generating Extensions Bootstrapper...");

            // Generate class source code
            generateIOCBootstrapClass(logger, context);
        }
        catch (Throwable e) {
            // record sendNowWith logger that Map generation threw an exception
            e.printStackTrace();
            logger.log(TreeLogger.ERROR, "Error generating extensions", e);
        }

        // return the fully qualified name of the class generated
        return packageName + "." + className;
    }

    /**
     * Generate source code for new class. Class extends
     * <code>HashMap</code>.
     *
     * @param logger  Logger object
     * @param context Generator context
     */
    private void generateIOCBootstrapClass(TreeLogger logger, GeneratorContext context) {
        // get print writer that receives the source code
        PrintWriter printWriter = context.tryCreate(logger, packageName, className);
        // print writer if null, source code has ALREADY been generated,

        if (printWriter == null) return;

        // init composer, set class properties, create source writer
        ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(packageName,
                className);

        composer.addImplementedInterface(Bootstrapper.class.getName());
        composer.addImport(InterfaceInjectionContext.class.getName());
        composer.addImport(Widget.class.getName());
        composer.addImport(List.class.getName());
        composer.addImport(ArrayList.class.getName());
        composer.addImport(Map.class.getName());
        composer.addImport(HashMap.class.getName());
        composer.addImport(com.google.gwt.user.client.ui.Panel.class.getName());
        composer.addImport(ErraiBus.class.getName());

        SourceWriter sourceWriter = composer.createSourceWriter(context, printWriter);

        procContext = new ProcessingContext(logger, context, sourceWriter, typeOracle);
        injectFactory = new InjectorFactory(procContext);
        procFactory = new ProcessorFactory(injectFactory);
        defaultConfigureProcessor();

        // generator constructor source code
        initializeProviders(context, logger, sourceWriter);
        generateExtensions(context, logger, sourceWriter);
        // close generated class
        sourceWriter.outdent();
        sourceWriter.println("}");

        // commit generated class
        context.commit(logger, printWriter);
    }

    public void initializeProviders(final GeneratorContext context, final TreeLogger logger, final SourceWriter sourceWriter) {

        final JClassType typeProviderCls;
        MetaDataScanner scanner = ScannerSingleton.getOrCreateInstance();

        try {
            typeProviderCls = typeOracle.getType(TypeProvider.class.getName());
        }
        catch (NotFoundException e) {
            throw new RuntimeException(e);
        }

        /**
         * IOCExtension.class
         */
        Set<Class<?>> iocExtensions = scanner.getTypesAnnotatedWith(IOCExtension.class);
        for (Class<?> clazz : iocExtensions) {
            try {
                Class<? extends IOCExtensionConfigurator> configuratorClass = clazz.asSubclass(IOCExtensionConfigurator.class);
                configuratorClass.newInstance().configure(procContext, injectFactory, procFactory);
            }
            catch (Exception e) {
                throw new ErraiBootstrapFailure("unable to load IOC Extension Configurator: " + e.getMessage(), e);
            }
        }

        /**
         * CodeDecorator.class
         */
        Set<Class<?>> decorators = scanner.getTypesAnnotatedWith(CodeDecorator.class);
        for (Class<?> clazz : decorators) {
            try {
                Class<? extends Decorator> decoratorClass = clazz.asSubclass(Decorator.class);

                Class<? extends Annotation> annoType = null;
                Type t = decoratorClass.getGenericSuperclass();
                if (!(t instanceof ParameterizedType)) {
                    throw new ErraiBootstrapFailure("code decorator must extend Decorator<@AnnotationType>");
                }

                ParameterizedType pType = (ParameterizedType) t;
                if (Decorator.class.equals(pType.getRawType())) {
                    if (pType.getActualTypeArguments().length == 0 || !Annotation.class.isAssignableFrom((Class) pType.getActualTypeArguments()[0])) {
                        throw new ErraiBootstrapFailure("code decorator must extend Decorator<@AnnotationType>");
                    }

                    annoType = ((Class) pType.getActualTypeArguments()[0]).asSubclass(Annotation.class);
                }

                injectFactory.getInjectionContext().registerDecorator(decoratorClass.getConstructor(new Class[]{Class.class}).newInstance(annoType));
            }
            catch (Exception e) {
                throw new ErraiBootstrapFailure("unable to load code decorator: " + e.getMessage(), e);
            }
        }

        /**
         * Provider.class
         */
        Set<Class<?>> providers = scanner.getTypesAnnotatedWith(Provider.class);
        for (Class<?> clazz : providers) {
            JClassType bindType = null;
            JClassType type = loadType(typeOracle, clazz);

            boolean contextual = false;
            for (JClassType iface : type.getImplementedInterfaces()) {
                if (iface.getQualifiedSourceName().equals(ContextualTypeProvider.class.getName())) {
                    contextual = true;

                    JParameterizedType pType = iface.isParameterized();

                    if (pType == null) {
                        throw new InjectionFailure("could not determine the bind type for the Provider class: " + type.getQualifiedSourceName());
                    }

                    bindType = pType.getTypeArgs()[0];
                    break;
                }
            }

            if (bindType == null) {
                for (JClassType iface : type.getImplementedInterfaces()) {
                    if (!typeProviderCls.isAssignableFrom(iface)) {
                        continue;
                    }

                    JParameterizedType pType = iface.isParameterized();

                    if (pType == null) {
                        throw new InjectionFailure("could not determine the bind type for the Provider class: " + type.getQualifiedSourceName());
                    }

                    bindType = pType.getTypeArgs()[0];
                }
            }

            if (bindType == null) {
                throw new InjectionFailure("the annotated provider class does not appear to implement " +
                        TypeProvider.class.getName() + ": " + type.getQualifiedSourceName());
            }

            final JClassType finalBindType = bindType;

            if (contextual) {
                injectFactory.addInjector(new ContextualProviderInjector(finalBindType, type));

            } else {
                injectFactory.addInjector(new ProviderInjector(finalBindType, type));
            }
        }


        /**
         * GeneratedBy.class
         */
        Set<Class<?>> generatedBys = scanner.getTypesAnnotatedWith(GeneratedBy.class);
        for (Class<?> clazz : generatedBys) {
            JClassType type = loadType(typeOracle, clazz);
            GeneratedBy anno = type.getAnnotation(GeneratedBy.class);
            Class<? extends ContextualTypeProvider> injectorClass = anno.value();

            try {
                injectFactory.addInjector(new ContextualProviderInjector(type, getJClassType(injectorClass)));
            }
            catch (Exception e) {
                throw new ErraiBootstrapFailure("could not load injector: " + e.getMessage(), e);
            }
        }

    }

    private void generateExtensions(final GeneratorContext context, final TreeLogger logger, final SourceWriter sourceWriter) {
        // start constructor source generation
        sourceWriter.println("public " + className + "() { ");
        sourceWriter.indent();
        sourceWriter.println("super();");
        sourceWriter.outdent();
        sourceWriter.println("}");

        sourceWriter.println("public InterfaceInjectionContext bootstrapContainer() { ");
        sourceWriter.outdent();
        sourceWriter.println("InterfaceInjectionContext ctx = new InterfaceInjectionContext();");

        MetaDataScanner scanner = ScannerSingleton.getOrCreateInstance();
        procFactory.process(scanner, procContext);

        runAllDeferred();

        sourceWriter.println("return ctx;");
        sourceWriter.outdent();
        sourceWriter.println("}");

        List<JField> privateFields = injectFactory.getInjectionContext().getPrivateFieldsToExpose();

        if (!privateFields.isEmpty()) {
            sourceWriter.println();
        }

        for (JField f : privateFields) {
            sourceWriter.print("private static native void ");
            sourceWriter.print(InjectUtil.getPrivateFieldInjectorName(f));
            sourceWriter.print("(");
            sourceWriter.print(f.getEnclosingType().getQualifiedSourceName());
            sourceWriter.print(" instance, ");
            sourceWriter.print(f.getType().getQualifiedBinaryName());
            sourceWriter.print(" value) /*-{");
            sourceWriter.println();
            sourceWriter.outdent();
            // begin JSNI call
            sourceWriter.print("instance.@");
            sourceWriter.print(f.getEnclosingType().getQualifiedSourceName());
            sourceWriter.print("::");
            sourceWriter.print(f.getName());
            sourceWriter.print(" = ");
            sourceWriter.print("value;");
            sourceWriter.println();
            sourceWriter.outdent();;
            sourceWriter.println("}-*/;\n");
        }
    }

    public void addType(final JClassType type) {
        injectFactory.addType(type);
    }

    public String generateWithSingletonSemantics(final JClassType visit) {
        return injectFactory.generateSingleton(visit);
    }

    public String generateInjectors(final JClassType visit) {
        return injectFactory.generate(visit);
    }

    public String generateAllProviders() {
        return injectFactory.generateAllProviders();
    }

    public void addDeferred(Runnable task) {
        deferredTasks.add(task);
    }

    private void runAllDeferred() {
        for (Runnable r : deferredTasks)
            r.run();
    }

    public JClassType getJClassType(Class cls) {
        try {
            return typeOracle.getType(cls.getName());
        }
        catch (NotFoundException e) {
            return null;
        }
    }

    private void defaultConfigureProcessor() {
        final JClassType widgetType = getJClassType(Widget.class);
        final JClassType messageCallbackType = getJClassType(MessageCallback.class);
        final JClassType messageBusType = getJClassType(MessageBus.class);

        procFactory.registerHandler(EntryPoint.class, new AnnotationHandler<EntryPoint>() {
            public void handle(final JClassType type, EntryPoint annotation, ProcessingContext context) {
                addDeferred(new Runnable() {
                    public void run() {
                        generateWithSingletonSemantics(type);
                    }
                });
            }
        });

        procFactory.registerHandler(ToRootPanel.class, new AnnotationHandler<ToRootPanel>() {
            public void handle(final JClassType type, final ToRootPanel annotation, final ProcessingContext context) {
                if (widgetType.isAssignableFrom(type)) {

                    addDeferred(new Runnable() {
                        public void run() {
                            context.getWriter().println("ctx.addToRootPanel(" + generateWithSingletonSemantics(type) + ");");
                        }
                    });


                } else {
                    throw new InjectionFailure("type declares @" + annotation.getClass().getSimpleName()
                            + "  but does not extend type Widget: " + type.getQualifiedSourceName());
                }
            }
        });

        procFactory.registerHandler(CreatePanel.class, new AnnotationHandler<CreatePanel>() {
            public void handle(final JClassType type, final CreatePanel annotation, final ProcessingContext context) {
                if (widgetType.isAssignableFrom(type)) {

                    addDeferred(new Runnable() {
                        public void run() {
                            context.getWriter().println("ctx.registerPanel(\"" + (annotation.value().equals("")
                                    ? type.getName() : annotation.value()) + "\", " + generateInjectors(type) + ");");
                        }
                    });
                } else {
                    throw new InjectionFailure("type declares @" + annotation.getClass().getSimpleName()
                            + "  but does not extend type Widget: " + type.getQualifiedSourceName());
                }
            }
        });

        procFactory.registerHandler(ToPanel.class, new AnnotationHandler<ToPanel>() {
            public void handle(final JClassType type, final ToPanel annotation, final ProcessingContext context) {
                if (widgetType.isAssignableFrom(type)) {

                    addDeferred(new Runnable() {
                        public void run() {
                            context.getWriter()
                                    .println("ctx.widgetToPanel(" + generateWithSingletonSemantics(type) + ", \"" + annotation.value() + "\");");
                        }
                    });
                } else {
                    throw new InjectionFailure("type declares @" + annotation.getClass().getSimpleName()
                            + "  but does not extend type Widget: " + type.getQualifiedSourceName());
                }
            }
        });
    }

    private JClassType loadType(TypeOracle oracle, Class<?> entity) {
        try {
            JClassType visit = oracle.getType(entity.getName());
            return visit;
        }
        catch (NotFoundException e) {
            throw new RuntimeException("Failed to load type " + entity.getName(), e);
        }
    }
}
TOP

Related Classes of org.jboss.errai.ioc.rebind.IOCGenerator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.