/**
* 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.experiment.generators;
import com.sun.codemodel.*;
import org.androidtransfuse.TransfuseAnalysisException;
import org.androidtransfuse.adapter.ASTMethod;
import org.androidtransfuse.adapter.ASTParameter;
import org.androidtransfuse.adapter.ASTType;
import org.androidtransfuse.adapter.element.ASTElementFactory;
import org.androidtransfuse.adapter.element.ASTTypeBuilderVisitor;
import org.androidtransfuse.analysis.AnalysisContext;
import org.androidtransfuse.analysis.InjectionPointFactory;
import org.androidtransfuse.annotations.FragmentLayoutHandler;
import org.androidtransfuse.annotations.Layout;
import org.androidtransfuse.experiment.*;
import org.androidtransfuse.gen.ClassGenerationUtil;
import org.androidtransfuse.gen.InjectionFragmentGenerator;
import org.androidtransfuse.gen.InstantiationStrategyFactory;
import org.androidtransfuse.gen.UniqueVariableNamer;
import org.androidtransfuse.gen.variableBuilder.InjectionBindingBuilder;
import org.androidtransfuse.layout.FragmentLayoutHandlerDelegate;
import org.androidtransfuse.model.InjectionNode;
import org.androidtransfuse.model.MethodDescriptor;
import org.androidtransfuse.model.TypedExpression;
import org.androidtransfuse.model.r.RResourceReferenceBuilder;
import org.androidtransfuse.util.AndroidLiterals;
import org.androidtransfuse.validation.Validator;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.lang.model.type.TypeMirror;
import java.util.Map;
import static org.androidtransfuse.util.TypeMirrorUtil.getTypeMirror;
/**
* @author John Ericksen
*/
public class FragmentLayoutGenerator implements Generation {
private final RResourceReferenceBuilder rResourceReferenceBuilder;
private final ASTElementFactory astElementFactory;
private final UniqueVariableNamer namer;
private final ClassGenerationUtil generationUtil;
private final InjectionBindingBuilder injectionBindingBuilder;
private final InjectionFragmentGenerator injectionFragmentGenerator;
private final InstantiationStrategyFactory instantiationStrategyFactory;
private final InjectionPointFactory injectionPointFactory;
private final Provider<ASTTypeBuilderVisitor> astTypeBuilderVisitorProvider;
private final Validator validator;
@Inject
public FragmentLayoutGenerator(RResourceReferenceBuilder rResourceReferenceBuilder,
ASTElementFactory astElementFactory,
UniqueVariableNamer namer,
ClassGenerationUtil generationUtil,
InjectionBindingBuilder injectionBindingBuilder,
InjectionFragmentGenerator injectionFragmentGenerator,
InstantiationStrategyFactory instantiationStrategyFactory,
InjectionPointFactory injectionPointFactory,
Provider<ASTTypeBuilderVisitor> astTypeBuilderVisitorProvider,
Validator validator) {
this.rResourceReferenceBuilder = rResourceReferenceBuilder;
this.astElementFactory = astElementFactory;
this.namer = namer;
this.generationUtil = generationUtil;
this.injectionBindingBuilder = injectionBindingBuilder;
this.injectionFragmentGenerator = injectionFragmentGenerator;
this.instantiationStrategyFactory = instantiationStrategyFactory;
this.injectionPointFactory = injectionPointFactory;
this.astTypeBuilderVisitorProvider = astTypeBuilderVisitorProvider;
this.validator = validator;
}
@Override
public String getName() {
return "Fragment Layout Generator";
}
@Override
public void schedule(final ComponentBuilder builder, final ComponentDescriptor descriptor) {
final InjectionNode layoutHandlerInjectionNode;
ASTType target = descriptor.getTarget();
if(target.isAnnotated(FragmentLayoutHandler.class)) {
FragmentLayoutHandler layoutHandlerAnnotation = target.getAnnotation(FragmentLayoutHandler.class);
layoutHandlerInjectionNode = buildLayoutHandlerInjectionNode(layoutHandlerAnnotation, builder.getAnalysisContext());
}
else {
layoutHandlerInjectionNode = null;
}
final ASTMethod onCreateViewMethod = astElementFactory.findMethod(AndroidLiterals.FRAGMENT, "onCreateView", AndroidLiterals.LAYOUT_INFLATER, AndroidLiterals.VIEW_GROUP, AndroidLiterals.BUNDLE);
builder.add(onCreateViewMethod, GenerationPhase.POSTSCOPES, new ComponentMethodGenerator() {
@Override
public void generate(MethodDescriptor methodDescriptor, JBlock block) {
ASTType target = descriptor.getTarget();
final JVar viewDeclaration = builder.getDefinedClass().field(JMod.PRIVATE, generationUtil.ref(AndroidLiterals.VIEW), namer.generateName(AndroidLiterals.VIEW));
builder.getAnalysisContext().getInjectionNodeBuilders().putType(AndroidLiterals.VIEW, injectionBindingBuilder.buildExpression(new TypedExpression(AndroidLiterals.VIEW, viewDeclaration)));
JBlock isNullconditionalBlock = block._if(viewDeclaration.eq(JExpr._null()))._then();
methodDescriptor.pushBody(isNullconditionalBlock);
boolean handled = false;
if (target.isAnnotated(Layout.class)) {
Layout layoutAnnotation = target.getAnnotation(Layout.class);
Integer layout = layoutAnnotation == null ? null : layoutAnnotation.value();
isNullconditionalBlock.assign(viewDeclaration, methodDescriptor.getExpression(AndroidLiterals.LAYOUT_INFLATER).getExpression()
.invoke("inflate")
.arg(rResourceReferenceBuilder.buildReference(layout))
.arg(methodDescriptor.getExpression(AndroidLiterals.VIEW_GROUP).getExpression())
.arg(JExpr.lit(false)));
handled = true;
}
if (layoutHandlerInjectionNode != null) {
if(handled){
validator.error("Layout is already defined with @Layout")
.element(target)
.annotation(target.getASTAnnotation(FragmentLayoutHandler.class))
.build();
}
try {
Map<InjectionNode, TypedExpression> expressionMap = injectionFragmentGenerator.buildFragment(isNullconditionalBlock,
instantiationStrategyFactory.buildMethodStrategy(isNullconditionalBlock, builder.getScopes()),
builder.getDefinedClass(),
layoutHandlerInjectionNode,
builder.getScopes());
//FragmentLayoutHandlerDelegate.invokeLayout(layoutInflater, viewGroup)
JExpression layoutHandlerDelegate = expressionMap.get(layoutHandlerInjectionNode).getExpression();
isNullconditionalBlock.invoke(layoutHandlerDelegate, FragmentLayoutHandlerDelegate.INVOKE_LAYOUT_METHOD)
.arg(methodDescriptor.getParameter(methodDescriptor.getASTMethod().getParameters().get(0)).getExpression())
.arg(methodDescriptor.getParameter(methodDescriptor.getASTMethod().getParameters().get(1)).getExpression());
} catch (JClassAlreadyExistsException e) {
throw new TransfuseAnalysisException("Class Already Exists ", e);
}
handled = true;
}
if (!handled) {
JInvocation onCreateView = JExpr._super().invoke("onCreateView");
for (ASTParameter astParameter : methodDescriptor.getASTMethod().getParameters()) {
onCreateView.arg(methodDescriptor.getExpression(astParameter.getASTType()).getExpression());
}
isNullconditionalBlock.assign(viewDeclaration, onCreateView);
}
builder.add(onCreateViewMethod, GenerationPhase.RETURN, new ComponentMethodGenerator() {
@Override
public void generate(MethodDescriptor methodDescriptor, JBlock block) {
methodDescriptor.popBody();
methodDescriptor.getBody()._return(viewDeclaration);
}
});
}
});
}
private InjectionNode buildLayoutHandlerInjectionNode(final FragmentLayoutHandler layoutHandlerAnnotation, AnalysisContext context) {
if (layoutHandlerAnnotation != null) {
TypeMirror layoutHandlerType = getTypeMirror(layoutHandlerAnnotation, "value");
if (layoutHandlerType != null) {
ASTType layoutHandlerASTType = layoutHandlerType.accept(astTypeBuilderVisitorProvider.get(), null);
return injectionPointFactory.buildInjectionNode(layoutHandlerASTType, context);
}
}
return null;
}
}