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; 017 018import com.sun.codemodel.*; 019import org.androidtransfuse.TransfuseAnalysisException; 020import org.androidtransfuse.adapter.ASTMethod; 021import org.androidtransfuse.adapter.ASTParameter; 022import org.androidtransfuse.adapter.MethodSignature; 023import org.androidtransfuse.analysis.AnalysisContext; 024import org.androidtransfuse.gen.ClassGenerationUtil; 025import org.androidtransfuse.gen.UniqueVariableNamer; 026import org.androidtransfuse.model.InjectionNode; 027import org.androidtransfuse.model.MethodDescriptor; 028import org.androidtransfuse.model.MethodDescriptorBuilder; 029import org.androidtransfuse.model.TypedExpression; 030 031import javax.inject.Inject; 032import java.util.*; 033 034/** 035 * @author John Ericksen 036 */ 037public class ComponentBuilder { 038 039 private class MethodMetaData{ 040 private final Map<GenerationPhase, List<ComponentMethodGenerator>> generators = new HashMap<GenerationPhase, List<ComponentMethodGenerator>>(); 041 private final List<ComponentMethodGenerator> lazyGenerators = new ArrayList<ComponentMethodGenerator>(); 042 private final ASTMethod methodDefinition; 043 private MethodDescriptor descriptor; 044 045 private MethodMetaData(ASTMethod methodDefinition) { 046 this.methodDefinition = methodDefinition; 047 } 048 049 public void addLazy(ComponentMethodGenerator partGenerator) { 050 lazyGenerators.add(partGenerator); 051 } 052 053 public void put(GenerationPhase phase, ComponentMethodGenerator partGenerator) { 054 if(!generators.containsKey(phase)){ 055 generators.put(phase, new ArrayList<ComponentMethodGenerator>()); 056 } 057 generators.get(phase).add(partGenerator); 058 } 059 060 public void build() { 061 for (GenerationPhase phase : GenerationPhase.values()) { 062 if(generators.containsKey(phase)){ 063 for (ComponentMethodGenerator componentMethodGenerator : generators.get(phase)) { 064 componentMethodGenerator.generate(getMethod(), descriptor.getMethod().body()); 065 } 066 } 067 } 068 } 069 070 public void setDescriptor(MethodDescriptor descriptor){ 071 this.descriptor = descriptor; 072 } 073 074 private MethodDescriptor getMethod() { 075 if (descriptor == null) { 076 JMethod method = getDefinedClass().method(JMod.PUBLIC, generationUtil.ref(methodDefinition.getReturnType()), methodDefinition.getName()); 077 method.annotate(Override.class); 078 079 MethodDescriptorBuilder methodDescriptorBuilder = new MethodDescriptorBuilder(method, methodDefinition); 080 081 //parameters 082 for (ASTParameter astParameter : methodDefinition.getParameters()) { 083 JVar param = method.param(generationUtil.ref(astParameter.getASTType()), variableNamer.generateName(astParameter.getASTType())); 084 methodDescriptorBuilder.putParameter(astParameter, new TypedExpression(astParameter.getASTType(), param)); 085 } 086 087 descriptor = methodDescriptorBuilder.build(); 088 089 //todo: move to be properly lazy 090 for (ComponentMethodGenerator generator : lazyGenerators) { 091 generator.generate(descriptor, method.body()); 092 } 093 } 094 return descriptor; 095 } 096 } 097 098 private final Map<InjectionNode, TypedExpression> expressionMap = new HashMap<InjectionNode, TypedExpression>(); 099 private final ClassGenerationUtil generationUtil; 100 private final ComponentDescriptor descriptor; 101 private final UniqueVariableNamer variableNamer; 102 103 private final Set<ComponentPartGenerator> generators = new HashSet<ComponentPartGenerator>(); 104 private final Map<MethodSignature, MethodMetaData> methodData = new HashMap<MethodSignature, MethodMetaData>(); 105 private JDefinedClass definedClass = null; 106 private JVar scopes; 107 108 @Inject 109 public ComponentBuilder(ClassGenerationUtil generationUtil, 110 ComponentDescriptor descriptor, 111 UniqueVariableNamer variableNamer) { 112 this.generationUtil = generationUtil; 113 this.descriptor = descriptor; 114 this.variableNamer = variableNamer; 115 } 116 117 public void build(){ 118 for (MethodSignature methodSignature : descriptor.getGenerateFirst()) { 119 if(methodData.containsKey(methodSignature)) { 120 methodData.get(methodSignature).build(); 121 } 122 } 123 124 for (Map.Entry<MethodSignature, MethodMetaData> entry : methodData.entrySet()) { 125 if(!descriptor.getGenerateFirst().contains(entry.getKey())){ 126 entry.getValue().build(); 127 } 128 } 129 130 for (ComponentPartGenerator generator : generators) { 131 generator.generate(descriptor); 132 } 133 } 134 135 public void add(ComponentPartGenerator partGenerator) { 136 generators.add(partGenerator); 137 } 138 139 public void addLazy(ASTMethod method, ComponentMethodGenerator partGenerator){ 140 MethodSignature methodSignature = new MethodSignature(method); 141 if(!methodData.containsKey(methodSignature)){ 142 methodData.put(methodSignature, new MethodMetaData(method)); 143 } 144 methodData.get(methodSignature).addLazy(partGenerator); 145 } 146 147 public void add(ASTMethod method, GenerationPhase phase, ComponentMethodGenerator partGenerator){ 148 MethodSignature methodSignature = new MethodSignature(method); 149 if(!methodData.containsKey(methodSignature)){ 150 methodData.put(methodSignature, new MethodMetaData(method)); 151 } 152 methodData.get(methodSignature).put(phase, partGenerator); 153 } 154 155 public JDefinedClass getDefinedClass() { 156 if (definedClass == null && descriptor.getType() != null) { 157 try { 158 definedClass = generationUtil.defineClass(descriptor.getPackageClass()); 159 definedClass._extends(generationUtil.ref(descriptor.getType())); 160 definedClass.annotate(SuppressWarnings.class).param("value", "unchecked"); 161 } catch (JClassAlreadyExistsException e) { 162 throw new TransfuseAnalysisException("Class Already Exists ", e); 163 } 164 } 165 return definedClass; 166 } 167 168 public AnalysisContext getAnalysisContext() { 169 return descriptor.getAnalysisContext(); 170 } 171 172 public void setScopes(JVar scopes) { 173 this.scopes = scopes; 174 } 175 176 public JVar getScopes() { 177 return scopes; 178 } 179 180 public Map<InjectionNode, TypedExpression> getExpressionMap() { 181 return expressionMap; 182 } 183}