Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.google.gwt.reflect.rebind.generators.MagicClassGenerator Maven / Gradle / Ivy
package com.google.gwt.reflect.rebind.generators;
import static com.google.gwt.reflect.client.ConstPool.ArrayConsts.EMPTY_ANNOTATIONS;
import static com.google.gwt.reflect.client.strategy.ReflectionStrategy.ALL_ANNOTATIONS;
import static com.google.gwt.reflect.client.strategy.ReflectionStrategy.COMPILE;
import static com.google.gwt.reflect.client.strategy.ReflectionStrategy.INHERITED;
import static com.google.gwt.reflect.client.strategy.ReflectionStrategy.NONE;
import static com.google.gwt.reflect.client.strategy.ReflectionStrategy.RUNTIME;
import static com.google.gwt.reflect.rebind.ReflectionManifest.getReflectionManifest;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.UnsafeNativeLong;
import com.google.gwt.core.ext.GeneratorContext;
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.HasAnnotations;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JConstructor;
import com.google.gwt.core.ext.typeinfo.JEnumType;
import com.google.gwt.core.ext.typeinfo.JField;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JRealClassType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.dev.javac.StandardGeneratorContext;
import com.google.gwt.dev.jjs.UnifyAstView;
import com.google.gwt.reflect.client.strategy.NewInstanceStrategy;
import com.google.gwt.reflect.client.strategy.ReflectionStrategy;
import com.google.gwt.reflect.client.strategy.UseGwtCreate;
import com.google.gwt.reflect.rebind.ReflectionManifest;
import com.google.gwt.reflect.rebind.ReflectionUnit;
import com.google.gwt.reflect.rebind.ReflectionUtilJava;
import com.google.gwt.reflect.rebind.injectors.DeclaredConstructorInjector;
import com.google.gwt.reflect.rebind.injectors.DeclaredFieldInjector;
import com.google.gwt.reflect.rebind.injectors.DeclaredMethodInjector;
import com.google.gwt.reflect.shared.ClassMap;
import com.google.gwt.reflect.shared.GwtReflect;
import com.google.gwt.reflect.shared.JsMemberPool;
import com.google.gwt.reflect.shared.ReflectUtil;
import com.google.gwt.thirdparty.xapi.dev.source.ClassBuffer;
import com.google.gwt.thirdparty.xapi.dev.source.MethodBuffer;
import com.google.gwt.thirdparty.xapi.dev.source.SourceBuilder;
import com.google.gwt.thirdparty.xapi.source.read.SourceUtil;
import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@ReflectionStrategy
public class MagicClassGenerator {
public static interface MemberFilter {
boolean keepMember(T type, boolean isDeclared, boolean isPublic, ReflectionStrategy strategy);
boolean keepAnnotation(T type, Annotation anno, ReflectionStrategy strategy);
@SuppressWarnings("rawtypes")
final MemberFilter DEFAULT_FILTER = new MemberFilter() {
@Override
public boolean keepAnnotation(final HasAnnotations type, final Annotation anno, final ReflectionStrategy strategy) {
final Retention retention = anno.annotationType().getAnnotation(Retention.class);
if (retention == null) {
return (strategy.annotationRetention() & COMPILE) == COMPILE;
}
switch (retention.value()) {
case RUNTIME:
return (strategy.annotationRetention() & RUNTIME) == RUNTIME;
case CLASS:
return (strategy.annotationRetention() & COMPILE) == COMPILE;
case SOURCE:
// gwt doesn't have the source annos anyway
default:
return false;
}
};
@Override
public boolean keepMember(final HasAnnotations type, final boolean isDeclared, final boolean isPublic, final ReflectionStrategy strategy) {
return isDeclared ? true : strategy.magicSupertypes() ? isPublic : true;
};
};
}
private final ConstructorGenerator constructorGenerator = newConstructorGenerator();
private final MethodGenerator methodGenerator = newMethodGenerator();
private final FieldGenerator fieldGenerator = newFieldGenerator();
private final HashMap newInstanceStrategies = new HashMap();
private static final ThreadLocal GENERATOR = new ThreadLocal() {
@Override
protected MagicClassGenerator initialValue() {
return new MagicClassGenerator();
};
};
public static void cleanup() {
GENERATOR.remove();
}
protected ConstructorGenerator newConstructorGenerator() {
return new DeclaredConstructorInjector();
}
protected MethodGenerator newMethodGenerator() {
return new DeclaredMethodInjector();
}
protected FieldGenerator newFieldGenerator() {
return new DeclaredFieldInjector();
}
private boolean logOnce = true;
private final HashSet done = new HashSet();
public static String generate(final TreeLogger logger, final ReflectionGeneratorContext reflectionCtx, final JClassType type)
throws UnableToCompleteException {
return GENERATOR.get().execImpl(logger, reflectionCtx, type);
}
public String execImpl(final TreeLogger logger, final ReflectionGeneratorContext reflectionCtx, final JClassType type)
throws UnableToCompleteException {
final StandardGeneratorContext context = reflectionCtx.getGeneratorContext();
final String simpleName = SourceUtil.toSourceName(type.getSimpleSourceName());
String generatedName = ReflectionUtilJava.generatedMagicClassName(simpleName);
final String packageName = type.getPackage().getName();
final String pkg = packageName.length() == 0 ? "" : packageName+".";
if (!done.add(type.getQualifiedSourceName())) {
return pkg+generatedName;
}
ReflectionManifest manifest;
PrintWriter printWriter = context.tryCreate(logger, packageName, generatedName);
int unique = 0;
String next = generatedName;
while(printWriter == null){
next = generatedName+"_"+unique++;
printWriter = context.tryCreate(logger, packageName, next);
System.err.println("Trying for new name: "+packageName+"."+next);
}
generatedName = next;
final UnifyAstView ast = reflectionCtx.getAst();
final JClassType targetType = type;
final String clsToEnhance = type.getQualifiedSourceName();
final JClassType injectionType = getInjectionType(logger, targetType, context);
manifest = getReflectionManifest(logger, injectionType.getQualifiedSourceName(), context);
final ReflectionStrategy strategy = manifest.getStrategy();
final boolean keepHierarchy = strategy.magicSupertypes();
final boolean keepCodesource = strategy.keepCodeSource();
final boolean keepPackageName = strategy.keepPackage();
final boolean keepAnnos = strategy.annotationRetention() > 0 ||
injectionType.getQualifiedSourceName().equals(Package.class.getName()+".PackageInfoProxy");
if (logger.isLoggable(Type.TRACE)) {
logger.log(Type.TRACE,"Writing magic class instance for " + type.getQualifiedSourceName() + " -> " +
injectionType.getQualifiedSourceName());
}
if ((strategy.debug() & ReflectionStrategy.SOURCE) > 0) {
logger.log(Type.INFO, "Source Dump For " +clsToEnhance+":");
logger.log(Type.INFO, type.toString());
}
final String typeName = injectionType.isPrivate() ? "?" : simpleName;
final SourceBuilder classBuilder = new SourceBuilder(
"public class "+generatedName +" extends ClassMap"
+ (injectionType.isPrivate()? "" : "<"+simpleName+">"))
.setPackage(packageName);
if (!injectionType.isPrivate()){
classBuilder.getImports().addImports(clsToEnhance);
}
classBuilder.getImports()
.addImports(
ClassMap.class, JavaScriptObject.class, UnsafeNativeLong.class,
ReflectUtil.class, Annotation.class,
Constructor.class,Method.class,Field.class,CodeSource.class)
.addStatics(GwtReflect.class.getName()+".constId")
;
final ClassBuffer cls = classBuilder.getClassBuffer();
cls.createMethod("private " +generatedName+"()");
if (keepHierarchy) {
injectionType.getNestedTypes();
JClassType superType = injectionType;
while (superType != superType.getSuperclass()) {
superType = superType.getSuperclass();
if (superType == null) {
break;
}
superType.getNestedTypes();
}
}
// This is the method that fills in all of the extra class data
final String memberPool = cls.addImport(JsMemberPool.class);
final String getMemberPool = cls.addImportStatic(JsMemberPool.class, "getMembers");
final MethodBuffer enhanceMethod = cls.createMethod
("private static void doEnhance(final Class<" +typeName+"> toEnhance)")
.indent()
.println(generatedName + " classMap = new "+generatedName+"();")
.println("ReflectUtil.setClassData(toEnhance, classMap);")
.println("remember(constId(toEnhance), classMap);")
.println(memberPool + " members = "+getMemberPool+"(toEnhance);")
;
// Print the public method that will conditionally enhance the class in question
cls.createMethod
("public static Class<"+typeName+"> enhanceClass(final Class<" +typeName+"> toEnhance)")
.println("if (Class.needsEnhance(toEnhance))")
.indentln("doEnhance(toEnhance);")
.returnValue("toEnhance");
;
final String returnName = injectionType.isPrivate() ? "Object" : simpleName;
if (injectionType.isDefaultInstantiable() && !(injectionType instanceof JEnumType)) {
final NewInstanceStrategy newInst = getNewInstanceStrategy(logger, strategy, injectionType, context);
implementNewInstance(logger, newInst, cls, clsToEnhance, cls.createMethod("public "+returnName+" newInstance()"), context);
} else {
cls
.createMethod("public "+returnName+" newInstance()")
.returnValue("throw new "+UnsupportedOperationException.class.getName()+"()");
}
// keep any declared methods
if (generateMethods(logger, manifest, classBuilder, reflectionCtx)) {
enhanceMethod.println("enhanceMethods(toEnhance);");
}
// now, do the fields
if (generateFields(logger, manifest, classBuilder, reflectionCtx)) {
enhanceMethod.println("enhanceFields(toEnhance);");
}
// constructors...
if (generateConstructors(logger, manifest, classBuilder, reflectionCtx)) {
enhanceMethod.println("enhanceConstructors(toEnhance);");
}
if (logger.isLoggable(Type.DEBUG)) {
logger.log(Type.DEBUG, "Keep Annos: "+keepAnnos+ "; type: "+injectionType+" with strategy"+strategy);
}
if (keepAnnos) {
final Annotation[] allAnnotations = extractAnnotations(strategy, injectionType);
GwtAnnotationGenerator.printAnnotationEnhancer(logger, classBuilder, injectionType, ast.getGeneratorContext(), allAnnotations);
enhanceMethod.println("enhanceAnnotations(members);");
}
if (extractClasses(logger, strategy, classFilter(logger, injectionType), injectionType, manifest)) {
final MethodBuffer enhanceClasses = cls.createMethod("private static void enhanceClasses" +
"(final Class cls)")
.setUseJsni(true)
.println("var map = [email protected] ::classData;")
;
for (final JClassType subclass : manifest.innerClasses.keySet()) {
enhanceClasses
.print("[email protected] ::addClass(")
.print("Ljava/lang/Class;Lcom/google/gwt/core/client/JavaScriptObject;)(@")
.print(subclass.getQualifiedSourceName()+"::class")
.println(", [email protected] ::classes);");
}
for (final JClassType iface : injectionType.getImplementedInterfaces()) {
enhanceClasses
.print("[email protected] ::addClass(")
.print("Ljava/lang/Class;Lcom/google/gwt/core/client/JavaScriptObject;)(@")
.print(iface.getQualifiedSourceName()+"::class")
.println(", [email protected] ::ifaces);");
}
if (injectionType.getEnclosingType() != null) {
enhanceClasses
.print("[email protected] ::enclosingClass = @")
.print(injectionType.getEnclosingType().getQualifiedSourceName()+"::class;");
}
enhanceMethod.println("enhanceClasses(toEnhance);");
}
if (keepCodesource) {
logger.log(Type.TRACE, "Processing request to keep codesource for "
+injectionType.getClass().getName()+" : "+injectionType);
if (injectionType instanceof JRealClassType) {
injectLocation(logger, cls, (JRealClassType)injectionType);
} else {
logger.log(Type.WARN, "Requested code location for "+injectionType.getQualifiedSourceName()
+" was not found; expected JRealClassType, got "+injectionType.getClass().getName());
}
}
if (keepPackageName) {
cls.createMethod("public Package getPackage()")
.println("return Package.getPackage(\""+packageName+"\");");
}
if ((strategy.debug() & ReflectionStrategy.TYPE) > 0) {
logger.log(Type.INFO, "Class Enhancer source dump for " +clsToEnhance+":");
logger.log(Type.INFO, classBuilder.toString());
}
// Actually write the file
printWriter.append(classBuilder.toString());
ConstPoolGenerator.maybeCommit(logger, context);
context.commit(logger, printWriter);
if (keepHierarchy) {
JClassType superType = injectionType.getSuperclass();
while (superType != null) {
execImpl(logger, reflectionCtx, superType);
superType = injectionType.getSuperclass();
}
}
return packageName+"."+generatedName;
}
/**
* Returns the array of annotations to support for the given type.
*
* If the reflection strategy is set to {@link ReflectionStrategy#NONE}, then an empty array is returned.
*
* If the reflection strategy includes {@link ReflectionStrategy#COMPILE} or {@link ReflectionStrategy#RUNTIME},
* then annotations with those retention policies are included.
*
* If the reflection strategy includes {@link ReflectionStrategy#INHERITED}, then the supertype chain will
* be searched for default annotation values.
*/
private Annotation[] extractAnnotations(final ReflectionStrategy strategy,
final JClassType injectionType) {
// First, short-circuit for the simple options.
switch (strategy.annotationRetention()) {
case NONE:
return EMPTY_ANNOTATIONS;
case ALL_ANNOTATIONS:
return injectionType.getAnnotations();
case COMPILE | RUNTIME:
return injectionType.getDeclaredAnnotations();
}
// If we get here, then we are including some combination of compile, runtime and inherited.
// So, we must filter the available results
final boolean inherit = (strategy.annotationRetention() & INHERITED) == INHERITED;
final boolean runtime = (strategy.annotationRetention() & RUNTIME) == RUNTIME;
final boolean compile = (strategy.annotationRetention() & COMPILE) == COMPILE;
final List supported = new ArrayList();
for (final Annotation anno : inherit ? injectionType.getAnnotations() : injectionType.getDeclaredAnnotations()) {
final Retention retention = anno.annotationType().getAnnotation(Retention.class);
if (retention == null) {
if (compile) {
supported.add(anno);
}
} else {
switch (retention.value()) {
case RUNTIME:
if (runtime) {
supported.add(anno);
}
break;
case CLASS:
if (compile) {
supported.add(anno);
}
break;
default: // Ignore source annotations; we don't have access to them anyway
}
}
}
return supported.toArray(new Annotation[supported.size()]);
}
private boolean generateMethods(final TreeLogger logger,
final ReflectionManifest manifest,
final SourceBuilder classBuilder,
final ReflectionGeneratorContext ctx
) throws UnableToCompleteException {
final Collection> methods = manifest.getMethods();
if (methods.size() > 0) {
final MethodBuffer initMethod =
classBuilder.getClassBuffer().createMethod("public static void enhanceMethods(Class cls)")
.addAnnotation(UnsafeNativeLong.class);
initMethod
.println("JsMemberPool map = JsMemberPool.getMembers(cls);")
.addImport("com.google.gwt.reflect.shared.JsMemberPool");
final GeneratorContext context = ctx.getGeneratorContext();
final TypeOracle oracle = context.getTypeOracle();
for (final ReflectionUnit unit : methods) {
final JMethod method = unit.getNode();
final String methodFactoryName = MethodGenerator.getMethodFactoryName(method.getEnclosingType(), method.getName(), method.getParameters());
JClassType existing;
String factory;
synchronized (GENERATOR) {
existing = oracle.findType(method.getEnclosingType().getPackage().getName(), methodFactoryName);
if (existing == null) {
factory = methodGenerator.generateMethodFactory(logger, ctx, method, methodFactoryName, manifest);
} else {
factory = existing.getQualifiedSourceName();
}
}
factory = initMethod.addImport(factory);
initMethod.println("map.addMethod("+factory+".instantiate());");
}
return true;
}
return false;
}
private boolean generateConstructors(final TreeLogger logger,
final ReflectionManifest manifest, final SourceBuilder classBuilder,
final ReflectionGeneratorContext context) throws UnableToCompleteException {
final Collection> ctors = manifest.getConstructors();
if (ctors.size() > 0) {
final MethodBuffer initMethod = classBuilder.getClassBuffer()
.createMethod("public static void enhanceConstructors(Class cls)")
.println("JsMemberPool map = JsMemberPool.getMembers(cls);")
.addImports("com.google.gwt.reflect.shared.JsMemberPool");
final TypeOracle oracle = context.getTypeOracle();
for (final ReflectionUnit unit : ctors) {
final JConstructor ctor = unit.getNode();
final String ctorFactoryName = ConstructorGenerator.getConstructorFactoryName(ctor.getEnclosingType(), ctor.getParameters());
JClassType existing;
String factory;
existing = oracle.findType(ctor.getEnclosingType().getPackage().getName(), ctorFactoryName);
if (existing == null) {
factory = constructorGenerator.generateConstructorFactory(logger, context, ctor, ctorFactoryName, manifest);
} else {
factory = existing.getQualifiedSourceName();
}
factory = initMethod.addImport(factory);
initMethod.println("map.addConstructor("+factory+".instantiate());");
}
return true;
}
return false;
}
private boolean generateFields(final TreeLogger logger,
final ReflectionManifest manifest, final SourceBuilder classBuilder, final ReflectionGeneratorContext ctx) throws UnableToCompleteException {
final Collection> fields = manifest.getFields();
if (fields.size() > 0) {
final MethodBuffer initMethod = classBuilder.getClassBuffer()
.createMethod("public static void enhanceFields(Class cls)")
.println("JsMemberPool map = JsMemberPool.getMembers(cls);")
.addImports(JsMemberPool.class);
final TypeOracle oracle = ctx.getTypeOracle();
for (final ReflectionUnit unit : fields) {
final JField field = unit.getNode();
final String fieldFactoryName = FieldGenerator.getFieldFactoryName(field.getEnclosingType(), field.getName());
JClassType existing;
String factory;
synchronized(GENERATOR) {
existing = oracle.findType(field.getEnclosingType().getPackage().getName(), fieldFactoryName);
if (existing == null) {
factory = fieldGenerator.generateFieldFactory(logger, ctx, field, fieldFactoryName, manifest);
} else {
factory = existing.getQualifiedSourceName();
}
}
factory = initMethod.addImport(factory);
initMethod.println("map.addField("+factory+".instantiate());");
}
return true;
}
return false;
}
protected JClassType getInjectionType(final TreeLogger logger, final JClassType targetType, final GeneratorContext context) {
return targetType;
}
protected NewInstanceStrategy getNewInstanceStrategy(final TreeLogger logger, final ReflectionStrategy strategy,
final JClassType injectionType, final GeneratorContext context) throws UnableToCompleteException {
final Class newInst = strategy.newInstanceStrategy();
final String name = newInst.getName();
NewInstanceStrategy template;
if (newInstanceStrategies.containsKey(name)) {
template = newInstanceStrategies.get(newInst.getName());
} else {
try {
template = newInst.newInstance();
} catch (final Exception e) {
logger.log(Type.ERROR, "Unable to create new instance of "+name, e);
template = null;
}
newInstanceStrategies.put(name, template);
}
if (template == null) {
logger.log(Type.ERROR, "Ensure New Instance Strategy " +name+
" is on the classpath and has a zero-arg public constructor");
throw new UnableToCompleteException();
}
return template;
}
protected void injectLocation(final TreeLogger logger, final ClassBuffer cls, final JRealClassType injectionType) {
String location;
try {
location = (String)injectionType.getClass().getMethod("getLocation").invoke(injectionType);
} catch (final Exception e) {
if (logOnce) {
logOnce = false;
logger.log(Type.ERROR, "Unable to call "+injectionType.getClass().getName()+".getLocation on "+injectionType.getJNISignature());
logger.log(Type.ERROR, "Ensure that you have the jar/artifact net.wetheinter:gwt-reflect before gwt-dev on your classpath.");
logger.log(Type.TRACE, "The artifact net.wetheinter:com.google.gwt.thirdparty.xapi-gwt-api contains a class, ClasspathFixer, which can help you.");
logger.log(Type.TRACE, "For unit tests, com.google.gwt.thirdparty.xapi-gwt-test overrides JUnitShell to fix the classpath for you.", e);
}
return;
}
cls.addImports(ProtectionDomain.class);
cls
.println("private ProtectionDomain domain;")
.createMethod("public ProtectionDomain getProtectionDomain()")
.println("if (domain == null) ")
.indentln("domain = new ProtectionDomain(\"" +location+"\");")
.println("return domain;");
}
@SuppressWarnings("unchecked")
private MemberFilter classFilter(final TreeLogger logger, final JClassType injectionType) {
return MemberFilter.DEFAULT_FILTER;
}
protected void implementNewInstance(final TreeLogger logger, final NewInstanceStrategy strategy, final ClassBuffer cls, final String clsToEnhance, final MethodBuffer newInstance, final GeneratorContext context) {
if (UseGwtCreate.class.isAssignableFrom(strategy.getClass())) {
cls.addImport(GWT.class);
}
newInstance.println(strategy.generate(clsToEnhance));
}
private static boolean extractClasses(final TreeLogger logger, final ReflectionStrategy strategy, final MemberFilter keepClass, final JClassType injectionType,
final ReflectionManifest manifest) {
final Set seen = new HashSet();
final Set allTypes = injectionType.getFlattenedSupertypeHierarchy();
for(JClassType nextClass : allTypes) {
for (final JClassType cls : nextClass.getNestedTypes()) {
if (keepClass.keepMember(cls, cls.getEnclosingType() == injectionType, cls.isPublic(), strategy)){
// do not include overridden methods
// TODO check for covariance?
if (seen.add(cls.getQualifiedSourceName())) {
final Annotation[] annos;
// only keep annotations annotated with KeepAnnotation.
final List keepers = new ArrayList();
for (final Annotation anno : cls.getAnnotations()) {
if (keepClass.keepAnnotation(cls, anno, strategy)) {
keepers.add(anno);
}
}
annos = keepers.toArray(new Annotation[keepers.size()]);
manifest.innerClasses.put(cls, annos);
}
}
}
nextClass = nextClass.getSuperclass();
}
return true;
}
}