/* * Copyright (C) 2015 Red Hat, Inc. and/or its affiliates. * * 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 org.jboss.errai.ioc.rebind.ioc.bootstrapper; import static org.jboss.errai.codegen.meta.MetaClassFactory.parameterizedAs; import static org.jboss.errai.codegen.meta.MetaClassFactory.typeParametersOf; import static org.jboss.errai.codegen.util.Stmt.invokeStatic; import java.io.PrintWriter; import java.lang.annotation.Annotation; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import org.jboss.errai.codegen.ArithmeticOperator; import org.jboss.errai.codegen.Parameter; import org.jboss.errai.codegen.Statement; import org.jboss.errai.codegen.builder.AnonymousClassStructureBuilder; import org.jboss.errai.codegen.builder.BlockBuilder; import org.jboss.errai.codegen.builder.ClassStructureBuilder; import org.jboss.errai.codegen.builder.ConstructorBlockBuilder; import org.jboss.errai.codegen.builder.MethodBlockBuilder; import org.jboss.errai.codegen.builder.impl.ClassBuilder; import org.jboss.errai.codegen.builder.impl.ObjectBuilder; import org.jboss.errai.codegen.meta.MetaClass; import org.jboss.errai.codegen.meta.MetaMethod; import org.jboss.errai.codegen.meta.impl.java.JavaReflectionClass; import org.jboss.errai.codegen.util.Arith; import org.jboss.errai.codegen.util.CDIAnnotationUtils; import org.jboss.errai.codegen.util.If; import org.jboss.errai.codegen.util.Refs; import org.jboss.errai.codegen.util.Stmt; import org.jboss.errai.common.metadata.RebindUtils; import org.jboss.errai.ioc.client.AnnotationComparator; import org.jboss.errai.ioc.client.QualifierEqualityFactory; import org.jboss.errai.ioc.client.QualifierUtil; import org.jboss.errai.ioc.util.TranslatableAnnotationUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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.TypeOracle; /** * @author Mike Brock */ public class QualifierEqualityFactoryGenerator extends Generator { private static final Logger log = LoggerFactory.getLogger(QualifierEqualityFactoryGenerator.class); @Override public String generate(final TreeLogger logger, final GeneratorContext context, final String typeName) throws UnableToCompleteException { try { final JClassType classType = context.getTypeOracle().getType(typeName); final String packageName = classType.getPackage().getName(); final String className = classType.getSimpleSourceName() + "Impl"; // Generate class source code generateQualifierEqualityFactory(packageName, className, logger, context); // return the fully qualified name of the class generated return packageName + "." + className; } catch (Throwable e) { log.error("Error generating QualifierEqualityFactory", e); throw new UnableToCompleteException(); } } private static final String COMPARATOR_MAP_VAR = "comparatorMap"; private void generateQualifierEqualityFactory(final String packageName, final String className, final TreeLogger logger, final GeneratorContext generatorContext) { final PrintWriter printWriter = generatorContext.tryCreate(logger, packageName, className); if (printWriter == null) { return; } final long start = System.currentTimeMillis(); log.info("Generating QualifierEqualityFactory..."); final TypeOracle oracle = generatorContext.getTypeOracle(); final ClassStructureBuilder<? extends ClassStructureBuilder<?>> builder = ClassBuilder.define(packageName + "." + className).publicScope() .implementsInterface(QualifierEqualityFactory.class) .body(); builder.getClassDefinition().getContext().setPermissiveMode(true); final MetaClass mapStringAnnoComp = parameterizedAs(HashMap.class, typeParametersOf(String.class, AnnotationComparator.class)); builder.privateField(COMPARATOR_MAP_VAR, mapStringAnnoComp) .initializesWith(Stmt.newObject(mapStringAnnoComp)).finish(); final ConstructorBlockBuilder<? extends ClassStructureBuilder<?>> constrBuilder = builder.publicConstructor(); for (final MetaClass MC_annotationClass : TranslatableAnnotationUtils.getTranslatableQualifiers(oracle)) { final Collection<MetaMethod> methods = CDIAnnotationUtils.getAnnotationAttributes(MC_annotationClass); if (methods.isEmpty()) continue; constrBuilder._(Stmt.loadVariable(COMPARATOR_MAP_VAR) .invoke("put", MC_annotationClass.getFullyQualifiedName(), generateComparatorFor(MC_annotationClass, methods))); } // finish constructor constrBuilder.finish(); final MetaClass annotationClazz = JavaReflectionClass.newUncachedInstance(Annotation.class); builder.publicMethod(boolean.class, "isEqual", Parameter.of(annotationClazz, "a1"), Parameter.of(annotationClazz, "a2")) .body() ._(If.cond(invokeStatic(QualifierUtil.class, "isSameType", Refs.get("a1"), Refs.get("a2"))) ._( If.cond(Stmt.loadVariable(COMPARATOR_MAP_VAR).invoke("containsKey", Stmt.loadVariable("a1").invoke("annotationType").invoke("getName"))) ._(Stmt.castTo(AnnotationComparator.class, Stmt.loadVariable(COMPARATOR_MAP_VAR) .invoke("get", Stmt.loadVariable("a1").invoke("annotationType").invoke("getName")) ).invoke("isEqual", Refs.get("a1"), Refs.get("a2")).returnValue()) .finish() .else_() ._(Stmt.load(true).returnValue()) .finish() ) .finish() .else_() ._(Stmt.load(false).returnValue()) .finish()) .finish(); builder.publicMethod(int.class, "hashCodeOf", Parameter.of(Annotation.class, "a1")) .body() ._( If.cond(Stmt.loadVariable(COMPARATOR_MAP_VAR).invoke("containsKey", Stmt.loadVariable("a1").invoke("annotationType").invoke("getName"))) ._(Stmt.castTo(AnnotationComparator.class, Stmt.loadVariable(COMPARATOR_MAP_VAR) .invoke("get", Stmt.loadVariable("a1").invoke("annotationType").invoke("getName")) ).invoke("hashCodeOf", Refs.get("a1")).returnValue()) .finish() .else_() ._(Stmt.loadVariable("a1").invoke("annotationType").invoke("hashCode").returnValue()) .finish()).finish(); final String csq = builder.toJavaString(); RebindUtils.writeStringToJavaSourceFileInErraiCacheDir(packageName, className, csq); printWriter.append(csq); log.info("Generated QualifierEqualityFactory in " + (System.currentTimeMillis() - start) + "ms"); generatorContext.commit(logger, printWriter); } private Statement generateComparatorFor(final MetaClass MC_annotationClass, final Collection<MetaMethod> methods) { final MetaClass MC_annoComparator = parameterizedAs(AnnotationComparator.class, typeParametersOf(MC_annotationClass)); final AnonymousClassStructureBuilder clsBuilder = ObjectBuilder.newInstanceOf(MC_annoComparator).extend(); final MethodBlockBuilder<AnonymousClassStructureBuilder> isEqualBuilder = clsBuilder .publicMethod(boolean.class, "isEqual", Parameter.of(MC_annotationClass, "a1"), Parameter.of(MC_annotationClass, "a2")) .annotatedWith(new Override() { @Override public Class<? extends Annotation> annotationType() { return Override.class; } }); for (final MetaMethod method : methods) { if (method.getReturnType().isPrimitive()) { isEqualBuilder._( If.notEquals(Stmt.loadVariable("a1").invoke(method), Stmt.loadVariable("a2").invoke(method)) ._(Stmt.load(false).returnValue()) .finish() ); } else if (method.getReturnType().isArray()) { isEqualBuilder._( If.not(Stmt.invokeStatic(Arrays.class, "equals", Stmt.loadVariable("a1").invoke(method), Stmt.loadVariable("a2").invoke(method)) ) ._(Stmt.load(false).returnValue()) .finish() ); } else { isEqualBuilder._( If.not(Stmt.loadVariable("a1").invoke(method).invoke("equals", Stmt.loadVariable("a2").invoke(method))) ._(Stmt.load(false).returnValue()) .finish() ); } } isEqualBuilder._(Stmt.load(true).returnValue()); final BlockBuilder<AnonymousClassStructureBuilder> hashCodeOfBuilder = clsBuilder.publicOverridesMethod("hashCodeOf", Parameter.of(MC_annotationClass, "a1")); hashCodeOfBuilder._(Stmt.declareVariable(int.class).named("hash") .initializeWith(Stmt.loadVariable("a1").invoke("annotationType").invoke("hashCode"))); for (final MetaMethod method : methods) { hashCodeOfBuilder._(Stmt.loadVariable("hash") .assignValue(hashArith(method))); } hashCodeOfBuilder._(Stmt.loadVariable("hash").returnValue()); hashCodeOfBuilder.finish(); final AnonymousClassStructureBuilder classStructureBuilder = isEqualBuilder.finish(); return classStructureBuilder.finish(); } private static Statement hashArith(final MetaMethod method) { return Arith.expr( Arith.expr(31, ArithmeticOperator.Multiplication, Refs.get("hash")), ArithmeticOperator.Addition, Stmt.invokeStatic(QualifierUtil.class, "hashValueFor", Stmt.loadVariable("a1").invoke(method)) ); } }