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.gen.componentBuilder;
017
018import com.sun.codemodel.*;
019import org.androidtransfuse.TransfuseAnalysisException;
020import org.androidtransfuse.adapter.ASTJDefinedClassType;
021import org.androidtransfuse.adapter.ASTMethod;
022import org.androidtransfuse.adapter.PackageClass;
023import org.androidtransfuse.adapter.element.ASTElementFactory;
024import org.androidtransfuse.analysis.astAnalyzer.NonConfigurationAspect;
025import org.androidtransfuse.annotations.Factory;
026import org.androidtransfuse.experiment.*;
027import org.androidtransfuse.gen.ClassGenerationUtil;
028import org.androidtransfuse.gen.ClassNamer;
029import org.androidtransfuse.gen.InvocationBuilder;
030import org.androidtransfuse.gen.UniqueVariableNamer;
031import org.androidtransfuse.gen.variableDecorator.TypedExpressionFactory;
032import org.androidtransfuse.model.FieldInjectionPoint;
033import org.androidtransfuse.model.InjectionNode;
034import org.androidtransfuse.model.MethodDescriptor;
035import org.androidtransfuse.model.TypedExpression;
036import org.androidtransfuse.util.AndroidLiterals;
037
038import javax.inject.Inject;
039import java.util.ArrayList;
040import java.util.HashMap;
041import java.util.List;
042import java.util.Map;
043
044/**
045 * @author John Ericksen
046 */
047public class NonConfigurationInstanceGenerator implements Generation {
048
049    private final UniqueVariableNamer variableNamer;
050    private final ClassNamer classNamer;
051    private final ClassGenerationUtil generationUtil;
052    private final InvocationBuilder invocationBuilder;
053    private final TypedExpressionFactory typeExpressionFactory;
054    private final ASTElementFactory astElementFactory;
055    private final ASTMethod creationMethod;
056
057    @Factory public interface NonconfigurationInstanceGeneratorFactory{
058        NonConfigurationInstanceGenerator build(ASTMethod creationMethod);
059    }
060
061    @Inject
062    public NonConfigurationInstanceGenerator(/*@Assisted*/ ASTMethod creationMethod, UniqueVariableNamer variableNamer, ClassNamer classNamer, ClassGenerationUtil generationUtil, InvocationBuilder invocationBuilder, TypedExpressionFactory typeExpressionFactory, ASTElementFactory astElementFactory) {
063        this.variableNamer = variableNamer;
064        this.classNamer = classNamer;
065        this.generationUtil = generationUtil;
066        this.invocationBuilder = invocationBuilder;
067        this.typeExpressionFactory = typeExpressionFactory;
068        this.astElementFactory = astElementFactory;
069        this.creationMethod = creationMethod;
070    }
071
072    @Override
073    public void schedule(final ComponentBuilder builder, ComponentDescriptor descriptor) {
074
075        builder.add(creationMethod, GenerationPhase.POSTINJECTION, new ComponentMethodGenerator() {
076            @Override
077            public void generate(MethodDescriptor methodDescriptor, JBlock block) {
078                final List<InjectionNode> nonConfigurationComponents = buildNonConfigurationComponents(builder.getExpressionMap());
079
080                if (!nonConfigurationComponents.isEmpty()) {
081                    try {
082                        //generate holder type
083                        final JDefinedClass nonConfigurationInstance = builder.getDefinedClass()._class(JMod.PRIVATE | JMod.STATIC | JMod.FINAL, classNamer.numberedClassName(new PackageClass(null, "NonConfigurationInstance")).build().getClassName());
084
085                        JMethod constructor = nonConfigurationInstance.constructor(JMod.PRIVATE);
086                        final Map<FieldInjectionPoint, JFieldVar> fieldMap = configureConstructor(constructor, nonConfigurationInstance, nonConfigurationComponents);
087
088                        builder.add(creationMethod, GenerationPhase.REGISTRATION, new ComponentMethodGenerator() {
089                            @Override
090                            public void generate(MethodDescriptor methodDescriptor, JBlock block) {
091
092                                //add on create init
093                                //super.getLastNonConfigurationInstance()
094                                JVar bodyVar = block.decl(nonConfigurationInstance, variableNamer.generateName(nonConfigurationInstance), JExpr.cast(nonConfigurationInstance, JExpr.invoke("getLastNonConfigurationInstance")));
095                                JBlock conditional = block._if(bodyVar.ne(JExpr._null()))._then();
096
097                                //assign variables
098                                for (InjectionNode nonConfigurationComponent : nonConfigurationComponents) {
099
100                                    NonConfigurationAspect aspect = nonConfigurationComponent.getAspect(NonConfigurationAspect.class);
101                                    for (FieldInjectionPoint nonConfigurationField : aspect.getFields()) {
102                                        TypedExpression fieldExpression = typeExpressionFactory.build(nonConfigurationField.getInjectionNode().getASTType(), JExpr.ref(bodyVar, fieldMap.get(nonConfigurationField)));
103                                        conditional.add(
104                                                invocationBuilder.buildFieldSet(
105                                                        new ASTJDefinedClassType(builder.getDefinedClass()),
106                                                        fieldExpression,
107                                                        nonConfigurationField,
108                                                        builder.getExpressionMap().get(nonConfigurationComponent).getExpression())
109                                        );
110                                    }
111                                }
112                            }
113                        });
114
115                        ASTMethod onRetainNonConfigurationInstanceMethod = astElementFactory.findMethod(AndroidLiterals.ACTIVITY, "onRetainNonConfigurationInstance");
116                        builder.add(onRetainNonConfigurationInstanceMethod, GenerationPhase.REGISTRATION, new ComponentMethodGenerator() {
117                            @Override
118                            public void generate(MethodDescriptor methodDescriptor, JBlock block) {
119                                JInvocation construction = JExpr._new(nonConfigurationInstance);
120                                JVar instanceDecl = block.decl(nonConfigurationInstance, variableNamer.generateName(nonConfigurationInstance)
121                                        , construction);
122
123                                for (InjectionNode injectionNode : nonConfigurationComponents) {
124
125                                    NonConfigurationAspect aspect = injectionNode.getAspect(NonConfigurationAspect.class);
126                                    for (FieldInjectionPoint fieldInjectionPoint : aspect.getFields()) {
127                                        construction.arg(invocationBuilder.buildFieldGet(
128                                                new ASTJDefinedClassType(builder.getDefinedClass()),
129                                                fieldInjectionPoint.getField(),
130                                                injectionNode.getASTType(),
131                                                builder.getExpressionMap().get(injectionNode)
132                                        ));
133                                    }
134                                }
135
136                                block._return(instanceDecl);
137                            }
138                        });
139
140
141                    } catch (JClassAlreadyExistsException e) {
142                        throw new TransfuseAnalysisException("Class already defined", e);
143                    }
144                }
145            }
146        });
147    }
148
149    private Map<FieldInjectionPoint, JFieldVar> configureConstructor(JMethod constructor, JDefinedClass nonConfigurationInstance, List<InjectionNode> nonConfigurationComponents) {
150        Map<FieldInjectionPoint, JFieldVar> fieldMap = new HashMap<FieldInjectionPoint, JFieldVar>();
151        for (InjectionNode injectionNode : nonConfigurationComponents) {
152            NonConfigurationAspect aspect = injectionNode.getAspect(NonConfigurationAspect.class);
153            for (FieldInjectionPoint fieldInjectionPoint : aspect.getFields()) {
154                //add all fields to constructor in order
155                JClass fieldNodeType = generationUtil.ref(fieldInjectionPoint.getInjectionNode().getASTType());
156                JVar param = constructor.param(fieldNodeType, variableNamer.generateName(fieldInjectionPoint.getInjectionNode()));
157                JFieldVar field = nonConfigurationInstance.field(JMod.PRIVATE, fieldNodeType, variableNamer.generateName(injectionNode));
158                constructor.body().assign(field, param);
159                fieldMap.put(fieldInjectionPoint, field);
160            }
161        }
162        return fieldMap;
163    }
164
165    private List<InjectionNode> buildNonConfigurationComponents(Map<InjectionNode, TypedExpression> expressionMap) {
166        List<InjectionNode> nonConfigurationComponents = new ArrayList<InjectionNode>();
167        for (Map.Entry<InjectionNode, TypedExpression> expressionEntry : expressionMap.entrySet()) {
168
169            if (expressionEntry.getKey().containsAspect(NonConfigurationAspect.class)) {
170                nonConfigurationComponents.add(expressionEntry.getKey());
171            }
172
173        }
174        return nonConfigurationComponents;
175    }
176}