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}