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.google.common.collect.ImmutableList; 019import com.sun.codemodel.*; 020import org.androidtransfuse.adapter.*; 021import org.androidtransfuse.adapter.classes.ASTClassFactory; 022import org.androidtransfuse.analysis.ManualSuperGenerator; 023import org.androidtransfuse.analysis.astAnalyzer.ListenerAspect; 024import org.androidtransfuse.analysis.astAnalyzer.ManualSuperAspect; 025import org.androidtransfuse.event.SuperCaller; 026import org.androidtransfuse.experiment.*; 027import org.androidtransfuse.gen.ClassGenerationUtil; 028import org.androidtransfuse.gen.InvocationBuilder; 029import org.androidtransfuse.gen.UniqueVariableNamer; 030import org.androidtransfuse.model.InjectionNode; 031import org.androidtransfuse.model.MethodDescriptor; 032import org.androidtransfuse.model.TypedExpression; 033 034import javax.inject.Inject; 035import javax.inject.Named; 036import java.util.*; 037 038public class MethodCallbackGenerator 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 ASTType superCallerType; 045 private final JCodeModel codeModel; 046 private final UniqueVariableNamer namer; 047 private final ClassGenerationUtil generationUtil; 048 049 @Inject 050 public MethodCallbackGenerator(/*@Assisted*/ ASTType eventAnnotation, /*@Assisted*/ @Named("eventMethod") ASTMethod eventMethod, /*@Assisted */ @Named("creationMethod") ASTMethod creationMethod, InvocationBuilder invocationBuilder, ASTClassFactory astClassFactory, JCodeModel codeModel, UniqueVariableNamer namer, ClassGenerationUtil generationUtil) { 051 this.eventAnnotation = eventAnnotation; 052 this.invocationBuilder = invocationBuilder; 053 this.eventMethod = eventMethod; 054 this.creationMethod = creationMethod; 055 this.codeModel = codeModel; 056 this.namer = namer; 057 this.generationUtil = generationUtil; 058 this.superCallerType = astClassFactory.getType(SuperCaller.class); 059 } 060 061 @Override 062 public void schedule(final ComponentBuilder builder, ComponentDescriptor descriptor) { 063 064 builder.add(creationMethod, GenerationPhase.POSTINJECTION, new ComponentMethodGenerator() { 065 @Override 066 public void generate(MethodDescriptor methodDescriptor, JBlock block) { 067 for (Map.Entry<InjectionNode, TypedExpression> injectionNodeJExpressionEntry : builder.getExpressionMap().entrySet()) { 068 ListenerAspect methodCallbackAspect = injectionNodeJExpressionEntry.getKey().getAspect(ListenerAspect.class); 069 final TypedExpression eventReceiverExpression = injectionNodeJExpressionEntry.getValue(); 070 071 if (methodCallbackAspect != null && methodCallbackAspect.contains(eventAnnotation)) { 072 Set<ASTMethod> methods = methodCallbackAspect.getListeners(eventAnnotation); 073 074 for (final ASTMethod methodCallback : methods) { 075 076 final boolean containsSuperCaller = containsSuperCaller(methodCallback.getParameters()); 077 if(containsSuperCaller){ 078 if(!injectionNodeJExpressionEntry.getKey().containsAspect(ManualSuperAspect.class)){ 079 injectionNodeJExpressionEntry.getKey().addAspect(new ManualSuperAspect()); 080 } 081 082 ManualSuperAspect manualSuperAspect = injectionNodeJExpressionEntry.getKey().getAspect(ManualSuperAspect.class); 083 manualSuperAspect.add(eventMethod); 084 } 085 086 builder.add(eventMethod, GenerationPhase.EVENT, new ComponentMethodGenerator() { 087 @Override 088 public void generate(MethodDescriptor methodDescriptor, JBlock block) { 089 090 Map<ASTType, Queue<JExpression>> methodArgumentExpressions = buildMethodArgumentExpressions(methodDescriptor); 091 if(containsSuperCaller) { 092 methodArgumentExpressions.put(superCallerType, new LinkedList<JExpression>(ImmutableList.of(buildSuperCaller(eventMethod)))); 093 } 094 095 List<JExpression> matchedExpressions = matchMethodArguments(methodCallback.getParameters(), methodArgumentExpressions); 096 097 JStatement methodCall = invocationBuilder.buildMethodCall( 098 new ASTJDefinedClassType(builder.getDefinedClass()), 099 new ASTJDefinedClassType(builder.getDefinedClass()), 100 methodCallback, 101 matchedExpressions, 102 eventReceiverExpression 103 ); 104 105 block.add(methodCall); 106 } 107 }); 108 } 109 } 110 } 111 } 112 }); 113 } 114 115 private JExpression buildSuperCaller(ASTMethod eventMethod) { 116 117 JDefinedClass superCallerClass = codeModel.anonymousClass(SuperCaller.class); 118 119 JMethod callMethod = superCallerClass.method(JMod.PUBLIC, Object.class, SuperCaller.CALL_METHOD); 120 JVar objectVarargs = callMethod.varParam(Object.class, namer.generateName(Object.class)); 121 122 JInvocation superInvocation = JExpr.ref(ManualSuperGenerator.SUPER_NAME).invoke(eventMethod.getName()); 123 124 for(int i = 0; i < eventMethod.getParameters().size(); i++){ 125 ASTParameter parameter = eventMethod.getParameters().get(i); 126 superInvocation = superInvocation.arg(JExpr.cast(generationUtil.ref(parameter.getASTType()), objectVarargs.component(JExpr.lit(i)))); 127 } 128 129 JBlock body = callMethod.body(); 130 if(eventMethod.getReturnType().equals(ASTVoidType.VOID)) { 131 body.add(superInvocation); 132 body._return(JExpr._null()); 133 } 134 else{ 135 body._return(superInvocation); 136 } 137 138 return JExpr._new(superCallerClass); 139 } 140 141 private boolean containsSuperCaller(ImmutableList<ASTParameter> parameters) { 142 143 for (ASTParameter parameter : parameters) { 144 if(parameter.getASTType().equals(superCallerType)){ 145 return true; 146 } 147 } 148 return false; 149 } 150 151 private List<JExpression> matchMethodArguments(List<ASTParameter> parametersToMatch, Map<ASTType, Queue<JExpression>> expressions) { 152 List<JExpression> arguments = new ArrayList<JExpression>(); 153 154 for (ASTParameter callParameter : parametersToMatch) { 155 ASTType type = callParameter.getASTType(); 156 if(expressions.containsKey(type) && !expressions.get(type).isEmpty()){ 157 arguments.add(expressions.get(type).remove()); 158 } 159 else{ 160 //todo: validation error 161 } 162 } 163 164 return arguments; 165 } 166 167 private Map<ASTType, Queue<JExpression>> buildMethodArgumentExpressions(MethodDescriptor method){ 168 Map<ASTType, Queue<JExpression>> argumentExpressions = new HashMap<ASTType, Queue<JExpression>>(); 169 for (ASTParameter parameter : method.getASTMethod().getParameters()) { 170 ASTType type = parameter.getASTType(); 171 if(!argumentExpressions.containsKey(type)){ 172 argumentExpressions.put(type, new LinkedList<JExpression>()); 173 } 174 175 argumentExpressions.get(type).add(method.getParameter(parameter).getExpression()); 176 } 177 return argumentExpressions; 178 } 179}