Java tutorial
/** * Copyright 2011-2015 John Ericksen * * 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.androidtransfuse.experiment.generators; import com.google.common.base.Predicate; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.sun.codemodel.*; import org.androidtransfuse.TransfuseAnalysisException; import org.androidtransfuse.adapter.ASTJDefinedClassType; import org.androidtransfuse.adapter.ASTMethod; import org.androidtransfuse.adapter.ASTType; import org.androidtransfuse.adapter.classes.ASTClassFactory; import org.androidtransfuse.analysis.InjectionPointFactory; import org.androidtransfuse.analysis.astAnalyzer.ObservesAspect; import org.androidtransfuse.annotations.Factory; import org.androidtransfuse.event.EventManager; import org.androidtransfuse.event.EventObserver; import org.androidtransfuse.experiment.*; import org.androidtransfuse.gen.*; import org.androidtransfuse.model.InjectionNode; import org.androidtransfuse.model.MethodDescriptor; import org.androidtransfuse.model.TypedExpression; import javax.inject.Inject; import javax.inject.Named; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author John Ericksen */ public class ObservesExpressionGenerator implements Generation { private final ASTMethod creationMethod; private final ASTMethod destroyMethod; private final ASTMethod registerMethod; private final ASTMethod unregisterMethod; private final JCodeModel codeModel; private final ClassGenerationUtil generationUtil; private final UniqueVariableNamer variableNamer; private final ClassNamer classNamer; private final InvocationBuilder invocationBuilder; private final ASTClassFactory astClassFactory; private final InjectionPointFactory injectionPointFactory; private final InjectionFragmentGenerator injectionFragmentGenerator; private final InstantiationStrategyFactory instantiationStrategyFactory; @Factory public interface ObservesExpressionGeneratorFactory { ObservesExpressionGenerator build(@Named("creationMethod") ASTMethod creationMethod, @Named("destroyMethod") ASTMethod destroyMethod, @Named("registerMethod") ASTMethod registerMethod, @Named("unregisterMethod") ASTMethod unregisterMethod); } @Inject public ObservesExpressionGenerator(@Named("creationMethod") ASTMethod creationMethod, @Named("destroyMethod") ASTMethod destroyMethod, @Named("registerMethod") ASTMethod registerMethod, @Named("unregisterMethod") ASTMethod unregisterMethod, JCodeModel codeModel, ClassGenerationUtil generationUtil, UniqueVariableNamer variableNamer, ClassNamer classNamer, InvocationBuilder invocationBuilder, ASTClassFactory astClassFactory, InjectionPointFactory injectionPointFactory, InjectionFragmentGenerator injectionFragmentGenerator, InstantiationStrategyFactory instantiationStrategyFactory) { this.creationMethod = creationMethod; this.destroyMethod = destroyMethod; this.registerMethod = registerMethod; this.unregisterMethod = unregisterMethod; this.codeModel = codeModel; this.generationUtil = generationUtil; this.variableNamer = variableNamer; this.classNamer = classNamer; this.invocationBuilder = invocationBuilder; this.astClassFactory = astClassFactory; this.injectionPointFactory = injectionPointFactory; this.injectionFragmentGenerator = injectionFragmentGenerator; this.instantiationStrategyFactory = instantiationStrategyFactory; } @Override public String getName() { return "Observes Generator"; } @Override public void schedule(final ComponentBuilder builder, ComponentDescriptor descriptor) { builder.add(creationMethod, GenerationPhase.POSTINJECTION, new ComponentMethodGenerator() { @Override public void generate(MethodDescriptor methodDescriptor, JBlock block) { try { ImmutableList<InjectionNode> observableInjectionNodes = FluentIterable .from(builder.getExpressionMap().keySet()).filter(new Predicate<InjectionNode>() { @Override public boolean apply(InjectionNode injectionNode) { return injectionNode.containsAspect(ObservesAspect.class); } }).toList(); if (!observableInjectionNodes.isEmpty()) { //mapping from event type -> observer final JVar observerCollection = getObserversCollection(builder, observableInjectionNodes); if (observerCollection != null) { final JVar eventManager = getEventManager(builder, builder.getExpressionMap(), builder.getScopes()); builder.add(registerMethod, GenerationPhase.REGISTRATION, new ComponentMethodGenerator() { @Override public void generate(MethodDescriptor methodDescriptor, JBlock block) { final JClass mapEntryType = generationUtil.ref(Map.Entry.class) .narrow(EventObserver.class, Class.class); JForEach forEachLoop = block.forEach(mapEntryType, variableNamer.generateName(EventObserver.class), observerCollection.invoke("entrySet")); forEachLoop.body().invoke(eventManager, "register") .arg(forEachLoop.var().invoke("getValue")) .arg(forEachLoop.var().invoke("getKey")); } }); builder.add(unregisterMethod, GenerationPhase.REGISTRATION, new ComponentMethodGenerator() { @Override public void generate(MethodDescriptor methodDescriptor, JBlock block) { JForEach forEachLoop = block.forEach( generationUtil.ref(EventObserver.class), variableNamer.generateName(EventObserver.class), observerCollection.invoke("keySet")); forEachLoop.body().invoke(eventManager, "unregister") .arg(forEachLoop.var()); } }); builder.add(destroyMethod, GenerationPhase.TEARDOWN, new ComponentMethodGenerator() { @Override public void generate(MethodDescriptor methodDescriptor, JBlock block) { block.invoke(observerCollection, "clear"); } }); } } } catch (JClassAlreadyExistsException e) { throw new TransfuseAnalysisException("Tried to generate a class that already exists", e); } } }); } private JVar getEventManager(final ComponentBuilder builder, final Map<InjectionNode, TypedExpression> expressionMap, final JExpression scopes) { ASTType eventManagerType = astClassFactory.getType(EventManager.class); final InjectionNode eventManagerInjectionNode = injectionPointFactory.buildInjectionNode(eventManagerType, builder.getAnalysisContext()); final JVar eventManagerVar = builder.getDefinedClass().field(JMod.PRIVATE, generationUtil.type(eventManagerType), variableNamer.generateName(eventManagerType)); builder.add(creationMethod, GenerationPhase.REGISTRATION, new ComponentMethodGenerator() { @Override public void generate(MethodDescriptor methodDescriptor, JBlock block) { try { Map<InjectionNode, TypedExpression> eventManagerExpressionMap = injectionFragmentGenerator .buildFragment(block, instantiationStrategyFactory.buildMethodStrategy(block, scopes), builder.getDefinedClass(), eventManagerInjectionNode, scopes, expressionMap); TypedExpression expression = eventManagerExpressionMap.get(eventManagerInjectionNode); block.assign(eventManagerVar, expression.getExpression()); } catch (JClassAlreadyExistsException e) { throw new TransfuseAnalysisException("Tried to generate a class that already exists", e); } } }); return eventManagerVar; } private JVar getObserversCollection(final ComponentBuilder builder, ImmutableList<InjectionNode> observableInjectionNodes) throws JClassAlreadyExistsException { JClass mapType = generationUtil.ref(Map.class).narrow(EventObserver.class, Class.class); JClass hashMapType = generationUtil.ref(HashMap.class).narrow(EventObserver.class, Class.class); final JVar observersCollection = builder.getDefinedClass().field(Modifier.PRIVATE, mapType, variableNamer.generateName("observesMap"), JExpr._new(hashMapType)); for (InjectionNode observableInjectionNode : observableInjectionNodes) { TypedExpression typedExpression = builder.getExpressionMap().get(observableInjectionNode); final JExpression observerExpression = builder.getExpressionMap().get(observableInjectionNode) .getExpression(); ObservesAspect aspect = observableInjectionNode.getAspect(ObservesAspect.class); for (ASTType event : aspect.getEvents()) { //generate inner class EventObserver<E> (E = event) final JClass eventRef = generationUtil.ref(event); JClass targetRef = generationUtil.ref(typedExpression.getType()); final JDefinedClass observerClass = builder.getDefinedClass()._class( JMod.PRIVATE | JMod.STATIC | JMod.FINAL, classNamer.numberedClassName(typedExpression.getType()).build().getClassName()); //target variable JFieldVar targetField = observerClass.field(JMod.PRIVATE, targetRef, variableNamer.generateName(typedExpression.getType())); //match default constructor public WeakObserver(T target){ JMethod constructor = observerClass.constructor(JMod.PUBLIC); JVar constTargetParam = constructor.param(targetRef, variableNamer.generateName(targetRef)); constructor.body().assign(targetField, constTargetParam); observerClass._implements(generationUtil.ref(EventObserver.class).narrow(eventRef)); JMethod triggerMethod = observerClass.method(JMod.PUBLIC, codeModel.VOID, EventObserver.TRIGGER); triggerMethod.annotate(Override.class); JVar eventParam = triggerMethod.param(eventRef, variableNamer.generateName(event)); JBlock triggerBody = triggerMethod.body(); List<JExpression> parameters = new ArrayList<JExpression>(); parameters.add(eventParam); for (ASTMethod observerMethod : aspect.getObserverMethods(event)) { triggerBody.add(invocationBuilder.buildMethodCall(new ASTJDefinedClassType(observerClass), typedExpression.getType(), observerMethod, parameters, new TypedExpression(typedExpression.getType(), targetField))); } builder.add(creationMethod, GenerationPhase.REGISTRATION, new ComponentMethodGenerator() { @Override public void generate(MethodDescriptor methodDescriptor, JBlock block) { block.invoke(observersCollection, "put") .arg(JExpr._new(observerClass).arg(observerExpression)).arg(eventRef.dotclass()); } }); } } return observersCollection; } }