/*
* Copyright 2010 Gal Dolber.
*
* 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 com.guit.rebind.gin;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.ext.CachedGeneratorResult;
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.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JPackage;
import com.google.gwt.inject.client.AsyncProvider;
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.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import com.google.inject.Provider;
import com.guit.rebind.common.AbstractGenerator;
import java.io.PrintWriter;
import java.util.HashSet;
import java.util.List;
/**
* This one is static to be accesible from any Generator, event outsiders.
*/
public class GinInjectorGenerator extends AbstractGenerator implements GinContext {
private static final String SINGLETON_DECLARATION = "public static " + GinOracle.fullName
+ " SINGLETON = " + GWT.class.getCanonicalName() + ".create(" + GinOracle.fullName
+ ".class);";
private HashSet<String> injectedClasses = new HashSet<String>();
private HashSet<String> providedClasses = new HashSet<String>();
private HashSet<String> asyncProvidedClasses = new HashSet<String>();
private final HashSet<Class<? extends GinModule>> gmodules =
new HashSet<Class<? extends GinModule>>();
@Override
public void addAsyncProvidedType(String classType) {
asyncProvidedClasses.add(classType);
}
@Override
public void addInjectedType(String classType) {
injectedClasses.add(classType);
}
@SuppressWarnings("unchecked")
@Override
public void addModule(String module) {
try {
gmodules.add((Class<? extends GinModule>) Class.forName(module));
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e);
}
}
@Override
public void addProvidedType(String classType) {
providedClasses.add(classType);
}
@Override
protected void generate(SourceWriter writer) throws UnableToCompleteException {
}
@Override
public RebindResult generateIncrementally(TreeLogger logger,
GeneratorContext context, String typeName)
throws UnableToCompleteException {
saveVariables(logger, context, typeName);
if (typeOracle.findType(GinOracle.packageName, GinOracle.className) != null) {
return new RebindResult(RebindMode.USE_EXISTING, GinOracle.packageName + "." + GinOracle.className);
}
// Clear
injectedClasses.clear();
providedClasses.clear();
asyncProvidedClasses.clear();
gmodules.clear();
// Call gin contributors
List<String> contributors = getConfigurationProperty("app.gin.contributor").getValues();
for (String c : contributors) {
GinContributor contributor = instantiateContributor(c);
contributor.collaborate(this, logger, context);
}
// Generate the modules string
StringBuilder sb = new StringBuilder();
sb.append("({");
for (Class<?> m : gmodules) {
if (sb.length() > 2) {
sb.append(", ");
}
sb.append(m.getCanonicalName() + ".class");
}
sb.append("})");
GinOracle.setModules(gmodules);
ClassSourceFileComposerFactory composer =
new ClassSourceFileComposerFactory(GinOracle.packageName, GinOracle.className);
composer.makeInterface();
composer.addImplementedInterface(Ginjector.class.getCanonicalName());
composer.addAnnotationDeclaration("@" + GinModules.class.getCanonicalName() + sb.toString());
PrintWriter printWriter = context.tryCreate(logger, GinOracle.packageName, GinOracle.className);
// Convert to linked to remove possible duplicated entries
injectedClasses = findClassOrLinkedInjectionKey(injectedClasses);
providedClasses = findClassOrLinkedInjectionKey(providedClasses);
asyncProvidedClasses = findClassOrLinkedInjectionKey(asyncProvidedClasses);
if (printWriter != null) {
SourceWriter writer = composer.createSourceWriter(context, printWriter);
writer.println(SINGLETON_DECLARATION);
for (String classType : injectedClasses) {
load(classType);
writer.println(classType + " " + GinOracle.getGetterMethodName(classType) + "();");
}
for (String classType : providedClasses) {
load(classType);
writer.println(Provider.class.getCanonicalName() + "<" + classType + "> "
+ GinOracle.getProviderGetterMethodName(classType) + "();");
}
for (String classType : asyncProvidedClasses) {
load(classType);
writer.println(AsyncProvider.class.getCanonicalName() + "<" + classType + "> "
+ GinOracle.getAsyncProviderGetterMethodName(classType) + "();");
}
writer.commit(logger);
}
return new RebindResult(RebindMode.USE_PARTIAL_CACHED, GinOracle.packageName + "." + GinOracle.className);
}
private void load(String classType) {
try {
GinInjectorGenerator.class.getClassLoader().loadClass(classType);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private HashSet<String> findClassOrLinkedInjectionKey(HashSet<String> original) {
HashSet<String> set = new HashSet<String>();
for (String c : original) {
set.add(GinOracle.getClassOrLinkedInjectionKey(c));
}
return set;
}
@Override
public HashSet<String> getAsyncProvidedClasses() {
return asyncProvidedClasses;
}
@Override
public HashSet<Class<? extends GinModule>> getGModules() {
return gmodules;
}
@Override
public HashSet<String> getInjectedClasses() {
return injectedClasses;
}
@Override
public HashSet<String> getProvidedClasses() {
return providedClasses;
}
private GinContributor instantiateContributor(String c) {
try {
Class<?> clazz = Class.forName(c);
return (GinContributor) clazz.newInstance();
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
@Override
protected void processComposer(ClassSourceFileComposerFactory composer) {
composer.addImplementedInterface(typeName);
}
}