/*
* Copyright 2010 kk-electronic a/s.
*
* This file is part of KKPortal.
*
* KKPortal is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* KKPortal is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with KKPortal. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kk_electronic.gwt.rebind;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.RunAsyncCallback;
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.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.inject.client.GinModule;
import com.google.gwt.inject.client.GinModules;
import com.google.gwt.inject.client.Ginjector;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import com.kk_electronic.kkportal.core.inject.ConstructFromLiteral;
import com.kk_electronic.kkportal.core.inject.FlexInjector;
public class FlexInjectorGenerator extends Generator {
private String packageName;
private String className;
private TypeOracle typeOracle;
private TreeLogger logger;
private GinModules modules;
private JClassType classType;
@Override
public String generate(TreeLogger logger, GeneratorContext context,
String typeName) throws UnableToCompleteException {
this.logger = logger;
try {
typeOracle = context.getTypeOracle();
classType = typeOracle.getType(typeName);
packageName = classType.getPackage().getName();
className = classType.getSimpleSourceName() + "Impl";
modules = classType.getAnnotation(GinModules.class);
generateClass(logger, context);
} catch (NotFoundException e) {
logger.log(TreeLogger.ERROR, "Exception during ModuleRegistry creation.", e);
throw new UnableToCompleteException();
}
return packageName + "." + className;
}
private void generateClass(TreeLogger logger, GeneratorContext context) throws UnableToCompleteException {
PrintWriter printWriter = context.tryCreate(logger, packageName, className);
if (printWriter == null){
return;
}
ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(packageName,className);
composer.addImport(GWT.class.getCanonicalName());
composer.addImport(RunAsyncCallback.class.getCanonicalName());
composer.addImport(Ginjector.class.getCanonicalName());
composer.addImport(GinModules.class.getCanonicalName());
composer.addImport(AsyncCallback.class.getCanonicalName());
composer.addImport(FlexInjector.class.getCanonicalName());
composer.addImport(Map.class.getCanonicalName());
composer.addImport(HashMap.class.getCanonicalName());
composer.addImplementedInterface(classType.getQualifiedSourceName());
SourceWriter sourceWriter = composer.createSourceWriter(context,printWriter);
writeStatic(sourceWriter);
writeGateways(sourceWriter);
// writeCreate(sourceWriter);
writeConstructor(sourceWriter);
sourceWriter.outdent();
sourceWriter.println("}");
context.commit(logger, printWriter);
}
private void writeFragment(SourceWriter sw,JClassType j){
sw.println("fragmentMap.put(" + j.getQualifiedSourceName() + ".class, new FlexInjector() {");
sw.indent();
sw.println("@Override");
sw.println("public <E> void create(Class<? extends E> type, AsyncCallback<E> callback) {");
sw.indentln("callback.onSuccess((E)injector.create$"+j.getQualifiedSourceName().replace('.', '$')+"());");
sw.println("}");
sw.outdent();
sw.println("});");
}
private void writeGateways(SourceWriter sw) throws UnableToCompleteException{
for(String key : getClasses().keySet()){
if(key == null) continue;
sw.println("private final class fragment$"+key+" implements FlexInjector {");
sw.indent();
sw.println();
sw.println("@Override");
sw.println("public <T> void create(final Class<? extends T> type, final AsyncCallback<T> callback) {");
sw.indent();
sw.println("GWT.runAsync(new RunAsyncCallback() {");
sw.indent();
sw.println("@Override");
sw.println("public void onSuccess() {");
for(JClassType j : getClasses().get(key)){
writeFragment(sw, j);
}
sw.indent();
sw.println(className+".this.create(type, callback);");
sw.outdent();
sw.println("}");
sw.println("public void onFailure(Throwable reason) {");
sw.indent();
for(JClassType j : getClasses().get(key)){
sw.println("fragmentMap.remove("+j.getQualifiedSourceName()+".class);");
}
sw.println("callback.onFailure(reason);");
sw.outdent();
sw.println("}");
sw.outdent();
sw.println("});");
sw.outdent();
sw.println("}");
sw.outdent();
sw.println("}");
}
}
private void writeConstructor(SourceWriter sw) throws UnableToCompleteException {
sw.println();
sw.println("@SuppressWarnings(\"unchecked\")");
sw.println("public "+className+"() {");
sw.indent();
sw.println("FlexInjector fragment;");
for(String key : getClasses().keySet()){
if(key != null){
sw.println("fragment = new fragment$"+key+"();");
for(JClassType j : getClasses().get(key)){
sw.println("fragmentMap.put(" + j.getQualifiedSourceName() + ".class, fragment);");
}
} else {
for(JClassType j : getClasses().get(key)){
writeFragment(sw, j);
}
}
}
sw.outdent();
sw.println("}");
}
private void writeStatic(SourceWriter sw) throws UnableToCompleteException {
modules.toString();
sw.print("@GinModules(");
for (Class<? extends GinModule> s:modules.value()){
sw.print(s.getCanonicalName());
sw.print(".class");
}
sw.println(")");
sw.println("static interface Injector extends Ginjector {");
sw.indent();
Set<JType> set = new HashSet<JType>();
for(Vector<JClassType> group : getClasses().values()){
set.addAll(group);
}
for(JMethod method:classType.getMethods()){
set.add(method.getReturnType());
}
for(JType j : set){
sw.println(j.getQualifiedSourceName() + " create$" + j.getQualifiedSourceName().replace('.', '$') + "();");
}
// for(Vector<JClassType> group : getClasses().values()){
// for(JClassType j : group){
// sw.println(j.getQualifiedSourceName() + " create$" + j.getQualifiedSourceName().replace('.', '$') + "();");
// }
// }
sw.outdent();
sw.println("}");
sw.println();
sw.println("private final Injector injector = GWT.create(Injector.class);");
sw.println("private Map<Class<?>, FlexInjector> fragmentMap = new HashMap<Class<?>, FlexInjector>();");
sw.println();
sw.println("@Override");
sw.println("public <T> void create(Class<? extends T> type, AsyncCallback<T> callback) {" );
sw.println(" FlexInjector x = fragmentMap.get(type);");
sw.println(" if (x != null){");
sw.println(" x.create(type, callback);");
sw.println(" } else {");
// sw.println(" callback.onFailure(new ClassNotFoundException());");
sw.println(" callback.onFailure(null);");
sw.println(" GWT.log(\"Class Creation of \" + type + \" failed\");");
sw.println(" }");
sw.println("}");
}
private Map<String, Vector<JClassType>> groups;
private void addClass(JClassType j){
assert(this.groups != null);
String key = getKeyFromClass(j);
Vector<JClassType> vector = groups.get(key);
if(vector == null){
vector = new Vector<JClassType>();
}
vector.add(j);
groups.put(key, vector);
}
private String getKeyFromClass(JClassType j){
ModuleGroup x = j.getAnnotation(ModuleGroup.class);
if(x != null){
return x.value().replaceAll("[- ]", "");
}
return null;
}
private Map<String, Vector<JClassType>> getClasses() throws UnableToCompleteException{
if (this.groups != null){
return this.groups;
}
this.groups = new HashMap<String, Vector<JClassType>>();
try {
typeOracle.getType(ConstructFromLiteral.class.getCanonicalName());
} catch (NotFoundException error) {
logger.log(TreeLogger.ERROR, "Can't find marker interface", error);
throw new UnableToCompleteException();
}
for(JClassType j : typeOracle.getTypes()){
ConstructFromLiteral a = j.getAnnotation(ConstructFromLiteral.class);
if(a != null){
if(a.includeConcreteClasses() && (j.isClass() != null && !j.isAbstract())){
addClass(j);
}
if(a.includeInterfaces() && (j.isInterface() != null)){
addClass(j);
}
if (a.recursive()) {
for (JClassType e : j.getSubtypes()) {
if (a.includeConcreteClasses() && (e.isClass() != null && !e.isAbstract())) {
addClass(e);
}
if (a.includeInterfaces() && (e.isInterface() != null)) {
addClass(e);
}
}
}
}
}
return groups;
}
}