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.adapter.ASTJDefinedClassType;
020import org.androidtransfuse.adapter.ASTMethod;
021import org.androidtransfuse.adapter.ASTParameter;
022import org.androidtransfuse.adapter.ASTType;
023import org.androidtransfuse.adapter.element.ASTElementFactory;
024import org.androidtransfuse.analysis.astAnalyzer.ListenerAspect;
025import org.androidtransfuse.experiment.*;
026import org.androidtransfuse.gen.ClassGenerationUtil;
027import org.androidtransfuse.gen.InvocationBuilder;
028import org.androidtransfuse.gen.UniqueVariableNamer;
029import org.androidtransfuse.model.InjectionNode;
030import org.androidtransfuse.model.MethodDescriptor;
031import org.androidtransfuse.model.TypedExpression;
032import org.androidtransfuse.util.AndroidLiterals;
033
034import javax.inject.Inject;
035import javax.inject.Named;
036import java.util.*;
037
038public class FragmentOnSaveInstanceStateMethodCallbackGenerator implements Generation {
039
040    private final ASTType eventAnnotation;
041    private final InvocationBuilder invocationBuilder;
042    private final ASTMethod eventMethod;
043    private final ASTMethod creationMethod;
044    private final UniqueVariableNamer namer;
045    private final ClassGenerationUtil generationUtil;
046    private final ASTElementFactory astElementFactory;
047
048    @Inject
049    public FragmentOnSaveInstanceStateMethodCallbackGenerator(/*@Assisted*/ ASTType eventAnnotation,
050                                                              /*@Assisted*/ @Named("eventMethod") ASTMethod eventMethod,
051                                                              /*@Assisted */ @Named("creationMethod")
052                                                              ASTMethod creationMethod,
053                                                              InvocationBuilder invocationBuilder,
054                                                              UniqueVariableNamer namer,
055                                                              ClassGenerationUtil generationUtil,
056                                                              ASTElementFactory astElementFactory) {
057        this.eventAnnotation = eventAnnotation;
058        this.invocationBuilder = invocationBuilder;
059        this.eventMethod = eventMethod;
060        this.creationMethod = creationMethod;
061        this.namer = namer;
062        this.generationUtil = generationUtil;
063        this.astElementFactory = astElementFactory;
064    }
065
066    @Override
067    public void schedule(final ComponentBuilder builder, final ComponentDescriptor descriptor) {
068
069        builder.add(creationMethod, GenerationPhase.POSTINJECTION, new ComponentMethodGenerator() {
070            @Override
071            public void generate(MethodDescriptor methodDescriptor, JBlock block) {
072                final Map<TypedExpression, Set<ASTMethod>> listenerAspects = new HashMap<TypedExpression, Set<ASTMethod>>();
073                for (Map.Entry<InjectionNode, TypedExpression> injectionNodeJExpressionEntry : builder.getExpressionMap().entrySet()) {
074                    ListenerAspect methodCallbackAspect = injectionNodeJExpressionEntry.getKey().getAspect(ListenerAspect.class);
075                    TypedExpression eventReceiverExpression = injectionNodeJExpressionEntry.getValue();
076
077                    if (methodCallbackAspect != null && methodCallbackAspect.contains(eventAnnotation)) {
078                        listenerAspects.put(eventReceiverExpression, methodCallbackAspect.getListeners(eventAnnotation));
079                    }
080                }
081
082                if(!listenerAspects.isEmpty()){
083
084                    final JVar bundleField = builder.getDefinedClass().field(JMod.PRIVATE, generationUtil.type(AndroidLiterals.BUNDLE), namer.generateName(AndroidLiterals.BUNDLE));
085                    //build onCreate to save bundle
086                    ASTMethod onCreateMethod = astElementFactory.findMethod(AndroidLiterals.FRAGMENT, "onCreate", AndroidLiterals.BUNDLE);
087                    builder.add(onCreateMethod, GenerationPhase.INIT, new ComponentMethodGenerator() {
088                        @Override
089                        public void generate(MethodDescriptor methodDescriptor, JBlock block) {
090                            //super call
091                            JExpression bundle = methodDescriptor.getExpression(AndroidLiterals.BUNDLE).getExpression();
092                            methodDescriptor.getMethod().body().add(JExpr._super().invoke("onCreate").arg(bundle));
093                            methodDescriptor.getMethod().body().assign(bundleField, bundle);
094                        }
095                    });
096
097                    builder.add(eventMethod, GenerationPhase.EVENT, new ComponentMethodGenerator() {
098                        @Override
099                        public void generate(MethodDescriptor methodDescriptor, JBlock block) {
100
101                            for(Map.Entry<TypedExpression, Set<ASTMethod>> methodCallbackEntry : listenerAspects.entrySet()) {
102                                Set<ASTMethod> methods = methodCallbackEntry.getValue();
103                                final TypedExpression eventReceiverExpression = methodCallbackEntry.getKey();
104
105                                //bundle addAll
106                                JExpression delegate = builder.getExpressionMap().get(descriptor.getRootInjectionNode()).getExpression();
107                                JConditional nullCondition = block._if(delegate.eq(JExpr._null()));
108                                nullCondition._then()._if(bundleField.ne(JExpr._null()))._then()
109                                        .add(methodDescriptor.getParameters().values().iterator().next().getExpression().invoke("putAll").arg(bundleField));
110
111                                JBlock body = nullCondition._else();
112
113                                for (final ASTMethod methodCallback : methods) {
114
115                                    List<ASTParameter> matchedParameters = matchMethodArguments(methodDescriptor.getASTMethod().getParameters(), methodCallback);
116                                    List<JExpression> matchedExpressions = new ArrayList<JExpression>();
117
118                                    for (ASTParameter matchedParameter : matchedParameters) {
119                                        matchedExpressions.add(methodDescriptor.getParameters().get(matchedParameter).getExpression());
120                                    }
121
122                                    JStatement methodCall = invocationBuilder.buildMethodCall(
123                                            new ASTJDefinedClassType(builder.getDefinedClass()),
124                                            new ASTJDefinedClassType(builder.getDefinedClass()),
125                                            methodCallback,
126                                            matchedExpressions,
127                                            eventReceiverExpression
128                                    );
129
130                                    body.add(methodCall);
131                                }
132                            }
133                        }
134                    });
135                }
136
137            }
138        });
139    }
140
141    private List<ASTParameter> matchMethodArguments(List<ASTParameter> parametersToMatch, ASTMethod methodToCall) {
142        List<ASTParameter> arguments = new ArrayList<ASTParameter>();
143
144        List<ASTParameter> overrideParameters = new ArrayList<ASTParameter>(parametersToMatch);
145
146        for (ASTParameter callParameter : methodToCall.getParameters()) {
147            Iterator<ASTParameter> overrideParameterIterator = overrideParameters.iterator();
148
149            while (overrideParameterIterator.hasNext()) {
150                ASTParameter overrideParameter = overrideParameterIterator.next();
151                if (overrideParameter.getASTType().equals(callParameter.getASTType())) {
152                    arguments.add(overrideParameter);
153                    overrideParameterIterator.remove();
154                    break;
155                }
156            }
157        }
158
159        return arguments;
160    }
161}