xapi.dev.generators.SyncInjectionGenerator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xapi-gwt Show documentation
Show all versions of xapi-gwt Show documentation
This module exists solely to package all other gwt modules into a single
uber jar. This makes deploying to non-mavenized targets much easier.
Of course, you would be wise to inherit your dependencies individually;
the uber jar is intended for projects like collide,
which have complex configuration, and adding many jars would be a pain.
The newest version!
/*
* 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();
}
}