/**
* 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.sun.codemodel.*;
import org.androidtransfuse.adapter.ASTJDefinedClassType;
import org.androidtransfuse.adapter.ASTMethod;
import org.androidtransfuse.adapter.ASTParameter;
import org.androidtransfuse.adapter.ASTType;
import org.androidtransfuse.adapter.element.ASTElementFactory;
import org.androidtransfuse.analysis.astAnalyzer.ListenerAspect;
import org.androidtransfuse.experiment.*;
import org.androidtransfuse.gen.ClassGenerationUtil;
import org.androidtransfuse.gen.InvocationBuilder;
import org.androidtransfuse.gen.UniqueVariableNamer;
import org.androidtransfuse.model.InjectionNode;
import org.androidtransfuse.model.MethodDescriptor;
import org.androidtransfuse.model.TypedExpression;
import org.androidtransfuse.util.AndroidLiterals;
import javax.inject.Inject;
import javax.inject.Named;
import java.util.*;
public class FragmentOnSaveInstanceStateMethodCallbackGenerator implements Generation {
private final ASTType eventAnnotation;
private final InvocationBuilder invocationBuilder;
private final ASTMethod eventMethod;
private final ASTMethod creationMethod;
private final UniqueVariableNamer namer;
private final ClassGenerationUtil generationUtil;
private final ASTElementFactory astElementFactory;
@Inject
public FragmentOnSaveInstanceStateMethodCallbackGenerator(/*@Assisted*/ ASTType eventAnnotation,
/*@Assisted*/ @Named("eventMethod") ASTMethod eventMethod,
/*@Assisted */ @Named("creationMethod")
ASTMethod creationMethod,
InvocationBuilder invocationBuilder,
UniqueVariableNamer namer,
ClassGenerationUtil generationUtil,
ASTElementFactory astElementFactory) {
this.eventAnnotation = eventAnnotation;
this.invocationBuilder = invocationBuilder;
this.eventMethod = eventMethod;
this.creationMethod = creationMethod;
this.namer = namer;
this.generationUtil = generationUtil;
this.astElementFactory = astElementFactory;
}
@Override
public String getName() {
return "Fragment OnSaveInstanceState Generator";
}
@Override
public void schedule(final ComponentBuilder builder, final ComponentDescriptor descriptor) {
builder.add(creationMethod, GenerationPhase.POSTINJECTION, new ComponentMethodGenerator() {
@Override
public void generate(MethodDescriptor methodDescriptor, JBlock block) {
final Map<TypedExpression, Set<ASTMethod>> listenerAspects = new HashMap<TypedExpression, Set<ASTMethod>>();
for (Map.Entry<InjectionNode, TypedExpression> injectionNodeJExpressionEntry : builder.getExpressionMap().entrySet()) {
ListenerAspect methodCallbackAspect = injectionNodeJExpressionEntry.getKey().getAspect(ListenerAspect.class);
TypedExpression eventReceiverExpression = injectionNodeJExpressionEntry.getValue();
if (methodCallbackAspect != null && methodCallbackAspect.contains(eventAnnotation)) {
listenerAspects.put(eventReceiverExpression, methodCallbackAspect.getListeners(eventAnnotation));
}
}
if(!listenerAspects.isEmpty()){
final JVar bundleField = builder.getDefinedClass().field(JMod.PRIVATE, generationUtil.type(AndroidLiterals.BUNDLE), namer.generateName(AndroidLiterals.BUNDLE));
//build onCreate to save bundle
ASTMethod onCreateMethod = astElementFactory.findMethod(AndroidLiterals.FRAGMENT, "onCreate", AndroidLiterals.BUNDLE);
builder.add(onCreateMethod, GenerationPhase.INIT, new ComponentMethodGenerator() {
@Override
public void generate(MethodDescriptor methodDescriptor, JBlock block) {
//super call
JExpression bundle = methodDescriptor.getExpression(AndroidLiterals.BUNDLE).getExpression();
methodDescriptor.getBody().add(JExpr._super().invoke("onCreate").arg(bundle));
methodDescriptor.getBody().assign(bundleField, bundle);
}
});
builder.add(eventMethod, GenerationPhase.EVENT, new ComponentMethodGenerator() {
@Override
public void generate(MethodDescriptor methodDescriptor, JBlock block) {
for(Map.Entry<TypedExpression, Set<ASTMethod>> methodCallbackEntry : listenerAspects.entrySet()) {
Set<ASTMethod> methods = methodCallbackEntry.getValue();
final TypedExpression eventReceiverExpression = methodCallbackEntry.getKey();
//bundle addAll
JExpression delegate = builder.getExpressionMap().get(descriptor.getRootInjectionNode()).getExpression();
JConditional nullCondition = block._if(delegate.eq(JExpr._null()));
nullCondition._then()._if(bundleField.ne(JExpr._null()))._then()
.add(methodDescriptor.getParameters().values().iterator().next().getExpression().invoke("putAll").arg(bundleField));
JBlock body = nullCondition._else();
for (final ASTMethod methodCallback : methods) {
List<ASTParameter> matchedParameters = matchMethodArguments(methodDescriptor.getASTMethod().getParameters(), methodCallback);
List<JExpression> matchedExpressions = new ArrayList<JExpression>();
for (ASTParameter matchedParameter : matchedParameters) {
matchedExpressions.add(methodDescriptor.getParameters().get(matchedParameter).getExpression());
}
JStatement methodCall = invocationBuilder.buildMethodCall(
new ASTJDefinedClassType(builder.getDefinedClass()),
new ASTJDefinedClassType(builder.getDefinedClass()),
methodCallback,
matchedExpressions,
eventReceiverExpression
);
body.add(methodCall);
}
}
}
});
}
}
});
}
private List<ASTParameter> matchMethodArguments(List<ASTParameter> parametersToMatch, ASTMethod methodToCall) {
List<ASTParameter> arguments = new ArrayList<ASTParameter>();
List<ASTParameter> overrideParameters = new ArrayList<ASTParameter>(parametersToMatch);
for (ASTParameter callParameter : methodToCall.getParameters()) {
Iterator<ASTParameter> overrideParameterIterator = overrideParameters.iterator();
while (overrideParameterIterator.hasNext()) {
ASTParameter overrideParameter = overrideParameterIterator.next();
if (overrideParameter.getASTType().equals(callParameter.getASTType())) {
arguments.add(overrideParameter);
overrideParameterIterator.remove();
break;
}
}
}
return arguments;
}
}