001/** 002 * Copyright 2013 John Ericksen 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.androidtransfuse.experiment.generators; 017 018import com.sun.codemodel.*; 019import org.androidtransfuse.TransfuseAnalysisException; 020import org.androidtransfuse.adapter.ASTJDefinedClassType; 021import org.androidtransfuse.adapter.ASTMethod; 022import org.androidtransfuse.adapter.ASTType; 023import org.androidtransfuse.adapter.classes.ASTClassFactory; 024import org.androidtransfuse.analysis.InjectionPointFactory; 025import org.androidtransfuse.analysis.astAnalyzer.ObservesAspect; 026import org.androidtransfuse.annotations.Factory; 027import org.androidtransfuse.event.EventManager; 028import org.androidtransfuse.event.EventObserver; 029import org.androidtransfuse.experiment.*; 030import org.androidtransfuse.gen.*; 031import org.androidtransfuse.model.InjectionNode; 032import org.androidtransfuse.model.MethodDescriptor; 033import org.androidtransfuse.model.TypedExpression; 034 035import javax.inject.Inject; 036import javax.inject.Named; 037import java.util.ArrayList; 038import java.util.HashMap; 039import java.util.List; 040import java.util.Map; 041 042/** 043 * @author John Ericksen 044 */ 045public class ObservesExpressionGenerator implements Generation { 046 047 private final ASTMethod creationMethod; 048 private final ASTMethod registerMethod; 049 private final ASTMethod unregisterMethod; 050 private final JCodeModel codeModel; 051 private final ClassGenerationUtil generationUtil; 052 private final UniqueVariableNamer variableNamer; 053 private final ClassNamer classNamer; 054 private final InvocationBuilder invocationBuilder; 055 private final ASTClassFactory astClassFactory; 056 private final InjectionPointFactory injectionPointFactory; 057 private final InjectionFragmentGenerator injectionFragmentGenerator; 058 private final InstantiationStrategyFactory instantiationStrategyFactory; 059 060 @Factory 061 public interface ObservesExpressionGeneratorFactory { 062 ObservesExpressionGenerator build(@Named("creationMethod") ASTMethod creationMethod, 063 @Named("registerMethod") ASTMethod registerMethod, 064 @Named("unregisterMethod") ASTMethod unregisterMethod); 065 } 066 067 @Inject 068 public ObservesExpressionGenerator(@Named("creationMethod") ASTMethod creationMethod, 069 @Named("registerMethod") ASTMethod registerMethod, 070 @Named("unregisterMethod") ASTMethod unregisterMethod, 071 JCodeModel codeModel, 072 ClassGenerationUtil generationUtil, 073 UniqueVariableNamer variableNamer, 074 ClassNamer classNamer, 075 InvocationBuilder invocationBuilder, 076 ASTClassFactory astClassFactory, 077 InjectionPointFactory injectionPointFactory, 078 InjectionFragmentGenerator injectionFragmentGenerator, 079 InstantiationStrategyFactory instantiationStrategyFactory) { 080 this.creationMethod = creationMethod; 081 this.registerMethod = registerMethod; 082 this.unregisterMethod = unregisterMethod; 083 this.codeModel = codeModel; 084 this.generationUtil = generationUtil; 085 this.variableNamer = variableNamer; 086 this.classNamer = classNamer; 087 this.invocationBuilder = invocationBuilder; 088 this.astClassFactory = astClassFactory; 089 this.injectionPointFactory = injectionPointFactory; 090 this.injectionFragmentGenerator = injectionFragmentGenerator; 091 this.instantiationStrategyFactory = instantiationStrategyFactory; 092 } 093 094 @Override 095 public void schedule(final ComponentBuilder builder, ComponentDescriptor descriptor) { 096 builder.add(creationMethod, GenerationPhase.POSTINJECTION, new ComponentMethodGenerator() { 097 @Override 098 public void generate(MethodDescriptor methodDescriptor, JBlock block) { 099 try { 100 //mapping from event type -> observer 101 Map<JClass, JVar> observerTuples = getObservers(builder, builder.getExpressionMap()); 102 103 if (!observerTuples.isEmpty()) { 104 final JVar eventManager = getEventManager(builder, builder.getExpressionMap(), builder.getScopes()); 105 106 for (final Map.Entry<JClass, JVar> tupleEntry : observerTuples.entrySet()) { 107 108 builder.add(registerMethod, GenerationPhase.REGISTRATION, new ComponentMethodGenerator() { 109 @Override 110 public void generate(MethodDescriptor methodDescriptor, JBlock block) { 111 block.invoke(eventManager, "register") 112 .arg(tupleEntry.getKey().dotclass()) 113 .arg(tupleEntry.getValue()); 114 } 115 }); 116 117 builder.add(unregisterMethod, GenerationPhase.REGISTRATION, new ComponentMethodGenerator() { 118 @Override 119 public void generate(MethodDescriptor methodDescriptor, JBlock block) { 120 block.invoke(eventManager, "unregister") 121 .arg(tupleEntry.getValue()); 122 } 123 }); 124 } 125 } 126 127 } catch (JClassAlreadyExistsException e) { 128 throw new TransfuseAnalysisException("Tried to generate a class that already exists", e); 129 } 130 } 131 }); 132 } 133 134 private JVar getEventManager(final ComponentBuilder builder, final Map<InjectionNode, TypedExpression> expressionMap, final JExpression scopes) { 135 136 ASTType eventManagerType = astClassFactory.getType(EventManager.class); 137 final InjectionNode eventManagerInjectionNode = injectionPointFactory.buildInjectionNode(eventManagerType, builder.getAnalysisContext()); 138 final JVar eventManagerVar = builder.getDefinedClass().field(JMod.PRIVATE, generationUtil.type(eventManagerType), variableNamer.generateName(eventManagerType)); 139 140 builder.add(creationMethod, GenerationPhase.REGISTRATION, new ComponentMethodGenerator() { 141 @Override 142 public void generate(MethodDescriptor methodDescriptor, JBlock block) { 143 144 try { 145 Map<InjectionNode, TypedExpression> eventManagerExpressionMap = injectionFragmentGenerator.buildFragment(block, 146 instantiationStrategyFactory.buildMethodStrategy(block, scopes), 147 builder.getDefinedClass(), 148 eventManagerInjectionNode, 149 scopes, 150 expressionMap); 151 TypedExpression expression = eventManagerExpressionMap.get(eventManagerInjectionNode); 152 block.assign(eventManagerVar, expression.getExpression()); 153 154 } catch (JClassAlreadyExistsException e) { 155 throw new TransfuseAnalysisException("Tried to generate a class that already exists", e); 156 } 157 158 } 159 }); 160 161 return eventManagerVar; 162 } 163 164 private Map<JClass, JVar> getObservers(final ComponentBuilder builder, Map<InjectionNode, TypedExpression> expressionMap) throws JClassAlreadyExistsException { 165 Map<JClass, JVar> observerTuples = new HashMap<JClass, JVar>(); 166 167 for (Map.Entry<InjectionNode, TypedExpression> expressionEntry : expressionMap.entrySet()) { 168 169 if (expressionEntry.getKey().containsAspect(ObservesAspect.class)) { 170 ObservesAspect aspect = expressionEntry.getKey().getAspect(ObservesAspect.class); 171 TypedExpression typedExpression = expressionEntry.getValue(); 172 final JExpression observerExpression = expressionEntry.getValue().getExpression(); 173 174 for (ASTType event : aspect.getEvents()) { 175 176 //generate inner class EventObserver<E> (E = event) 177 JClass eventRef = generationUtil.ref(event); 178 JClass targetRef = generationUtil.ref(typedExpression.getType()); 179 180 final JDefinedClass observerClass = builder.getDefinedClass()._class(JMod.PRIVATE | JMod.STATIC | JMod.FINAL, classNamer.numberedClassName(typedExpression.getType()).build().getClassName()); 181 182 //target variable 183 JFieldVar targetField = observerClass.field(JMod.PRIVATE, targetRef, variableNamer.generateName(typedExpression.getType())); 184 185 //match default constructor public WeakObserver(T target){ 186 JMethod constructor = observerClass.constructor(JMod.PUBLIC); 187 JVar constTargetParam = constructor.param(targetRef, variableNamer.generateName(targetRef)); 188 constructor.body().assign(targetField, constTargetParam); 189 190 observerClass._implements(generationUtil.ref(EventObserver.class).narrow(eventRef)); 191 192 JMethod triggerMethod = observerClass.method(JMod.PUBLIC, codeModel.VOID, EventObserver.TRIGGER); 193 triggerMethod.annotate(Override.class); 194 JVar eventParam = triggerMethod.param(eventRef, variableNamer.generateName(event)); 195 JBlock triggerBody = triggerMethod.body(); 196 197 List<JExpression> parameters = new ArrayList<JExpression>(); 198 parameters.add(eventParam); 199 200 for (ASTMethod observerMethod : aspect.getObserverMethods(event)) { 201 triggerBody.add(invocationBuilder.buildMethodCall( 202 new ASTJDefinedClassType(observerClass), 203 expressionEntry.getKey().getASTType(), 204 observerMethod, 205 parameters, 206 new TypedExpression(typedExpression.getType(), targetField))); 207 } 208 209 final JFieldVar observerField = builder.getDefinedClass().field(JMod.PRIVATE, observerClass, variableNamer.generateName(EventObserver.class)); 210 211 observerTuples.put(eventRef, observerField); 212 213 builder.add(creationMethod, GenerationPhase.REGISTRATION, new ComponentMethodGenerator() { 214 @Override 215 public void generate(MethodDescriptor methodDescriptor, JBlock block) { 216 block.assign(observerField, JExpr._new(observerClass).arg(observerExpression)); 217 } 218 }); 219 220 } 221 } 222 } 223 224 return observerTuples; 225 } 226}