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}