/**
* Copyright 2011-2015 John Ericksen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.androidtransfuse.gen.componentBuilder;
import com.sun.codemodel.*;
import org.androidtransfuse.TransfuseAnalysisException;
import org.androidtransfuse.adapter.ASTJDefinedClassType;
import org.androidtransfuse.adapter.ASTMethod;
import org.androidtransfuse.adapter.ASTType;
import org.androidtransfuse.adapter.PackageClass;
import org.androidtransfuse.adapter.element.ASTElementFactory;
import org.androidtransfuse.analysis.astAnalyzer.NonConfigurationAspect;
import org.androidtransfuse.annotations.Factory;
import org.androidtransfuse.experiment.*;
import org.androidtransfuse.gen.ClassGenerationUtil;
import org.androidtransfuse.gen.ClassNamer;
import org.androidtransfuse.gen.InvocationBuilder;
import org.androidtransfuse.gen.UniqueVariableNamer;
import org.androidtransfuse.gen.variableDecorator.TypedExpressionFactory;
import org.androidtransfuse.model.FieldInjectionPoint;
import org.androidtransfuse.model.InjectionNode;
import org.androidtransfuse.model.MethodDescriptor;
import org.androidtransfuse.model.TypedExpression;
import javax.inject.Inject;
import javax.inject.Named;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author John Ericksen
*/
public class NonConfigurationInstanceGenerator implements Generation {
private final UniqueVariableNamer variableNamer;
private final ClassNamer classNamer;
private final ClassGenerationUtil generationUtil;
private final InvocationBuilder invocationBuilder;
private final TypedExpressionFactory typeExpressionFactory;
private final ASTElementFactory astElementFactory;
private final ASTMethod creationMethod;
private final ASTType componentType;
private final String getMethod;
private final String setMethod;
@Factory public interface NonconfigurationInstanceGeneratorFactory{
NonConfigurationInstanceGenerator build(ASTMethod creationMethod, ASTType componentType, @Named("getMethod") String getMethod, @Named("setMethod") String setMethod);
}
@Inject
public NonConfigurationInstanceGenerator(/*@Assisted*/ ASTMethod creationMethod, ASTType componentType, /*@Assisted*/ @Named("getMethod") String getMethod, /*@Assisted*/ @Named("setMethod") String setMethod, UniqueVariableNamer variableNamer, ClassNamer classNamer, ClassGenerationUtil generationUtil, InvocationBuilder invocationBuilder, TypedExpressionFactory typeExpressionFactory, ASTElementFactory astElementFactory) {
this.variableNamer = variableNamer;
this.classNamer = classNamer;
this.generationUtil = generationUtil;
this.invocationBuilder = invocationBuilder;
this.typeExpressionFactory = typeExpressionFactory;
this.astElementFactory = astElementFactory;
this.creationMethod = creationMethod;
this.setMethod = setMethod;
this.getMethod = getMethod;
this.componentType = componentType;
}
@Override
public String getName() {
return "NonConfigurationInstance Generator";
}
@Override
public void schedule(final ComponentBuilder builder, ComponentDescriptor descriptor) {
builder.add(creationMethod, GenerationPhase.POSTINJECTION, new ComponentMethodGenerator() {
@Override
public void generate(MethodDescriptor methodDescriptor, JBlock block) {
final List<InjectionNode> nonConfigurationComponents = buildNonConfigurationComponents(builder.getExpressionMap());
if (!nonConfigurationComponents.isEmpty()) {
try {
//generate holder type
final JDefinedClass nonConfigurationInstance = builder.getDefinedClass()._class(JMod.PRIVATE | JMod.STATIC | JMod.FINAL, classNamer.numberedClassName(new PackageClass(null, "NonConfigurationInstance")).build().getClassName());
JMethod constructor = nonConfigurationInstance.constructor(JMod.PRIVATE);
final Map<FieldInjectionPoint, JFieldVar> fieldMap = configureConstructor(constructor, nonConfigurationInstance, nonConfigurationComponents);
builder.add(creationMethod, GenerationPhase.REGISTRATION, new ComponentMethodGenerator() {
@Override
public void generate(MethodDescriptor methodDescriptor, JBlock block) {
//add on create init
//super.getLastNonConfigurationInstance()
JVar bodyVar = block.decl(nonConfigurationInstance, variableNamer.generateName(nonConfigurationInstance), JExpr.cast(nonConfigurationInstance, JExpr.invoke(getMethod)));
JBlock conditional = block._if(bodyVar.ne(JExpr._null()))._then();
//assign variables
for (InjectionNode nonConfigurationComponent : nonConfigurationComponents) {
NonConfigurationAspect aspect = nonConfigurationComponent.getAspect(NonConfigurationAspect.class);
for (FieldInjectionPoint nonConfigurationField : aspect.getFields()) {
TypedExpression fieldExpression = typeExpressionFactory.build(nonConfigurationField.getInjectionNode().getASTType(), JExpr.ref(bodyVar, fieldMap.get(nonConfigurationField)));
conditional.add(
invocationBuilder.buildFieldSet(
new ASTJDefinedClassType(builder.getDefinedClass()),
fieldExpression,
nonConfigurationField,
builder.getExpressionMap().get(nonConfigurationComponent).getExpression())
);
}
}
}
});
ASTMethod onRetainNonConfigurationInstanceMethod = astElementFactory.findMethod(componentType, setMethod);
builder.add(onRetainNonConfigurationInstanceMethod, GenerationPhase.REGISTRATION, new ComponentMethodGenerator() {
@Override
public void generate(MethodDescriptor methodDescriptor, JBlock block) {
JInvocation construction = JExpr._new(nonConfigurationInstance);
JVar instanceDecl = block.decl(nonConfigurationInstance, variableNamer.generateName(nonConfigurationInstance)
, construction);
for (InjectionNode injectionNode : nonConfigurationComponents) {
NonConfigurationAspect aspect = injectionNode.getAspect(NonConfigurationAspect.class);
for (FieldInjectionPoint fieldInjectionPoint : aspect.getFields()) {
construction.arg(invocationBuilder.buildFieldGet(
new ASTJDefinedClassType(builder.getDefinedClass()),
fieldInjectionPoint.getField(),
injectionNode.getASTType(),
builder.getExpressionMap().get(injectionNode)
));
}
}
block._return(instanceDecl);
}
});
} catch (JClassAlreadyExistsException e) {
throw new TransfuseAnalysisException("Class already defined", e);
}
}
}
});
}
private Map<FieldInjectionPoint, JFieldVar> configureConstructor(JMethod constructor, JDefinedClass nonConfigurationInstance, List<InjectionNode> nonConfigurationComponents) {
Map<FieldInjectionPoint, JFieldVar> fieldMap = new HashMap<FieldInjectionPoint, JFieldVar>();
for (InjectionNode injectionNode : nonConfigurationComponents) {
NonConfigurationAspect aspect = injectionNode.getAspect(NonConfigurationAspect.class);
for (FieldInjectionPoint fieldInjectionPoint : aspect.getFields()) {
//add all fields to constructor in order
JClass fieldNodeType = generationUtil.ref(fieldInjectionPoint.getInjectionNode().getASTType());
JVar param = constructor.param(fieldNodeType, variableNamer.generateName(fieldInjectionPoint.getInjectionNode()));
JFieldVar field = nonConfigurationInstance.field(JMod.PRIVATE, fieldNodeType, variableNamer.generateName(injectionNode));
constructor.body().assign(field, param);
fieldMap.put(fieldInjectionPoint, field);
}
}
return fieldMap;
}
private List<InjectionNode> buildNonConfigurationComponents(Map<InjectionNode, TypedExpression> expressionMap) {
List<InjectionNode> nonConfigurationComponents = new ArrayList<InjectionNode>();
for (Map.Entry<InjectionNode, TypedExpression> expressionEntry : expressionMap.entrySet()) {
if (expressionEntry.getKey().containsAspect(NonConfigurationAspect.class)) {
nonConfigurationComponents.add(expressionEntry.getKey());
}
}
return nonConfigurationComponents;
}
}