xapi.dev.generators.SyncInjectionGenerator.java Source code

Java tutorial

Introduction

Here is the source code for xapi.dev.generators.SyncInjectionGenerator.java

Source

/*
 * Copyright 2012, We The Internet Ltd.
 *
 * All rights reserved.
 *
 * Distributed under a modified BSD License as follow:
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution, unless otherwise
 * agreed to in a written document signed by a director of We The Internet Ltd.
 *
 * Neither the name of We The Internet nor the names of its contributors may
 * be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
package xapi.dev.generators;

import java.io.PrintWriter;

import xapi.annotation.inject.SingletonDefault;
import xapi.annotation.inject.SingletonOverride;
import xapi.dev.util.CurrentGwtPlatform;
import xapi.dev.util.InjectionUtils;
import xapi.dev.util.PlatformSet;
import xapi.inject.impl.SingletonInitializer;
import xapi.log.X_Log;
import xapi.source.read.SourceUtil;
import xapi.util.api.ReceivesValue;

import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.RebindMode;
import com.google.gwt.core.ext.RebindResult;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;

public class SyncInjectionGenerator extends AbstractInjectionGenerator {

    /**
     * @throws ClassNotFoundException
     */
    public static RebindResult execImpl(TreeLogger logger, GeneratorContext context, JClassType type)
            throws ClassNotFoundException {
        PlatformSet allowed = CurrentGwtPlatform.getPlatforms(context);
        JClassType targetType = type;
        String simpleName = "SingletonFor_" + type.getSimpleSourceName();
        SingletonOverride winningOverride = null;
        JClassType winningType = null;
        boolean trace = logger.isLoggable(Type.TRACE);
        for (JClassType subtype : type.getSubtypes()) {
            if (winningType == null) {
                SingletonDefault singletonDefault = subtype.getAnnotation(SingletonDefault.class);
                if (singletonDefault != null) {
                    if (allowed.isAllowedType(subtype))
                        winningType = subtype;
                    continue;
                }
            }
            SingletonOverride override = subtype.getAnnotation(SingletonOverride.class);
            if (override != null) {
                if (trace)
                    logger.log(Type.DEBUG,
                            "Got subtype " + subtype + " : " + " - prodMode: " + context.isProdMode());

                if (allowed.isAllowedType(subtype)) {
                    if (winningOverride != null) {
                        if (winningOverride.priority() > override.priority())
                            continue;
                    }
                    winningOverride = override;
                    winningType = subtype;
                }
            }
        }
        if (winningType == null) {
            winningType = targetType;//no matches, resort to instantiate the class sent.
            //TODO sanity check here
        }
        if (trace)
            X_Log.info("Singleton Injection Winner: " + winningType.getName());
        String packageName = type.getPackage().getName();
        ensureProviderClass(logger, packageName, type.getSimpleSourceName(), type.getQualifiedSourceName(),
                SourceUtil.toSourceName(winningType.getQualifiedSourceName()), context);
        packageName = packageName + ".impl";
        PrintWriter printWriter = context.tryCreate(logger, packageName, simpleName);
        if (printWriter == null) {
            return new RebindResult(RebindMode.USE_EXISTING, packageName + "." + simpleName);
        }
        ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(packageName, simpleName);
        composer.setSuperclass(SingletonInitializer.class.getName() + "<" + type.getQualifiedSourceName() + ">");
        composer.addImport(ReceivesValue.class.getName());

        SourceWriter sw = composer.createSourceWriter(context, printWriter);
        sw.println();
        //TODO: also check if compile is set to async = false

        //if async is already generated when this singleton access occurs,
        //but we haven't yet injected the callback which accesses the global singleton,
        //we'll have to route our get() through the existing async callback block
        //to prevents code splitting from falling apart. This will, at worst,
        //cause providers to return null until the service is actually initialized.
        if (context.isProdMode() && isAsyncProvided(logger, packageName, type.getSimpleSourceName(), context)
                && !isCallbackInjected(logger, packageName, type.getSimpleSourceName(), context)) {

            //this edge case happens when a service is accessed synchronously
            //inside of its own asynchronous callback
            //TODO use GWT's AsyncProxy class to queue up requests...
            logger.log(Type.WARN, "Generating interim singleton provider");
            sw.indent();
            sw.println("private static " + type.getQualifiedSourceName() + " value = null;");
            sw.println("@Override");
            sw.println("public final " + type.getQualifiedSourceName() + " initialValue(){");
            sw.indent();
            sw.println("if (value!=null)return value;");
            sw.println(packageName + "." + InjectionUtils.generatedAsyncProviderName(type.getSimpleSourceName()));
            sw.indent();

            sw.print(".request(new ReceivesValue<");
            sw.println(type.getQualifiedSourceName() + ">(){");
            sw.indent();
            sw.println("@Override");
            sw.print("public void set(");
            sw.println(type.getQualifiedSourceName() + " x){");
            sw.indent();
            sw.println("value=x;");
            sw.outdent();
            sw.println("}");
            sw.outdent();

            sw.println("});");
            sw.outdent();
            sw.println("return value;");
        } else {
            //all non-prod or non-async providers can safely access the singleton directly
            sw.println("@Override");
            sw.println("public final " + type.getQualifiedSourceName() + " initialValue(){");
            sw.indent();
            //normal operation; just wrap the static final singleton provider.
            sw.print("return " + type.getPackage().getName() + "."
                    + InjectionUtils.generatedProviderName(type.getSimpleSourceName()));
            sw.println(".theProvider.get();");
        }
        sw.outdent();
        sw.println("}");
        sw.println();

        sw.commit(logger);
        //TODO: implement caching once this code is finalized
        return new RebindResult(RebindMode.USE_ALL_NEW_WITH_NO_CACHING, packageName + "." + simpleName);

    }

    @Override
    public RebindResult generateIncrementally(TreeLogger logger, GeneratorContext context, String typeName)
            throws UnableToCompleteException {
        TypeOracle oracle = context.getTypeOracle();
        logger.log(Type.TRACE, "Generating singleton for " + typeName);

        try {
            return execImpl(logger, context, oracle.getType(SourceUtil.toSourceName(typeName)));
        } catch (NotFoundException e) {
            logger.log(Type.ERROR, "Could not find class for " + typeName, e);
        } catch (ClassNotFoundException e) {
            logger.log(Type.ERROR, "Could not find class for " + typeName, e);
        }
        throw new UnableToCompleteException();
    }

}