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}