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}