package architect.autostack.compiler;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.TypeSpec;
import org.parceler.Parcel;
import org.parceler.ParcelConstructor;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Generated;
import javax.lang.model.element.Modifier;
import architect.robot.DaggerService;
import dagger.Module;
import dagger.Provides;
import mortar.MortarScope;
import processorworkflow.AbstractComposer;
import processorworkflow.Logger;
/**
* @author Lukasz Piliszczuk - lukasz.pili@gmail.com
*/
public class ScopeComposer extends AbstractComposer<ScopeSpec> {
private static final ClassName STACKABLE_CLS = ClassName.get("architect", "Stackable");
private static final ClassName PATH_CLS = ClassName.get("architect", "StackablePath");
private static final ClassName DAGGERSERVICE_CLS = ClassName.get(DaggerService.class);
private static final ClassName CONTEXT_CLS = ClassName.get("android.content", "Context");
private static final ClassName VIEW_CLS = ClassName.get("android.view", "View");
private static final ClassName VIEWGROUP_CLS = ClassName.get("android.view", "ViewGroup");
private static final ClassName LAYOUTINFLATER_CLS = ClassName.get("android.view", "LayoutInflater");
public ScopeComposer(List<ScopeSpec> specs) {
super(specs);
}
@Override
protected JavaFile compose(ScopeSpec spec) {
TypeSpec typeSpec = build(spec);
return JavaFile.builder(spec.getClassName().packageName(), typeSpec).build();
}
private TypeSpec build(ScopeSpec spec) {
MethodSpec configureScopeSpec = MethodSpec.methodBuilder("configureScope")
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.addParameter(ClassName.get(MortarScope.Builder.class), "builder")
.addParameter(ClassName.get(MortarScope.class), "parentScope")
.addCode(CodeBlock.builder()
.add("builder.withService($T.SERVICE_NAME, $T.builder()\n", DAGGERSERVICE_CLS, spec.getDaggerComponentTypeName())
.indent()
.add(".$L(parentScope.<$T>getService($T.SERVICE_NAME))\n", spec.getDaggerComponentBuilderDependencyMethodName(), spec.getDaggerComponentBuilderDependencyTypeName(), DAGGERSERVICE_CLS)
.add(".module(new Module())\n")
.add(".build());\n")
.unindent()
.build())
.build();
List<FieldSpec> fieldSpecs = new ArrayList<>();
for (ParameterSpec parameterSpec : spec.getModuleSpec().getInternalParameters()) {
fieldSpecs.add(FieldSpec.builder(parameterSpec.type, parameterSpec.name)
.build());
}
MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addAnnotation(AnnotationSpec.builder(ParcelConstructor.class).build())
.addParameters(spec.getModuleSpec().getInternalParameters());
for (ParameterSpec parameterSpec : spec.getModuleSpec().getInternalParameters()) {
constructorBuilder.addStatement("this.$L = $L", parameterSpec.name, parameterSpec.name);
}
TypeSpec.Builder builder = TypeSpec.classBuilder(spec.getClassName().simpleName())
.addModifiers(Modifier.PUBLIC)
.addAnnotation(AnnotationSpec.builder(Generated.class).addMember("value", "$S", architect.autostack.compiler.AnnotationProcessor.class.getName()).build())
.addAnnotation(spec.getComponentAnnotationSpec())
.addAnnotation(AnnotationSpec.builder(Parcel.class).addMember("parcelsIndex", "false").build())
.addType(buildModule(spec.getModuleSpec()))
.addMethod(constructorBuilder.build())
.addMethod(configureScopeSpec)
.addFields(fieldSpecs);
if (spec.getScopeAnnotationSpec() != null) {
builder.addAnnotation(spec.getScopeAnnotationSpec());
}
if (spec.getPathViewTypeName() != null || spec.getPathLayout() != null) {
builder.addSuperinterface(PATH_CLS);
MethodSpec.Builder createViewSpecBuilder = MethodSpec.methodBuilder("createView")
.addModifiers(Modifier.PUBLIC)
.returns(spec.getPathViewTypeName() != null ? spec.getPathViewTypeName() : VIEW_CLS)
.addAnnotation(Override.class)
.addParameter(CONTEXT_CLS, "context")
.addParameter(VIEWGROUP_CLS, "parent");
if (spec.getPathViewTypeName() != null) {
createViewSpecBuilder.addStatement("return new $T(context)", spec.getPathViewTypeName());
} else {
createViewSpecBuilder.addStatement("return $T.from(context).inflate($L, parent, false)", LAYOUTINFLATER_CLS, spec.getPathLayout());
}
builder.addMethod(createViewSpecBuilder.build());
} else {
builder.addSuperinterface(STACKABLE_CLS);
}
return builder.build();
}
private TypeSpec buildModule(ModuleSpec spec) {
CodeBlock.Builder blockBuilder = CodeBlock.builder().add("return new $T(", spec.getPresenterTypeName());
int i = 0;
for (ParameterSpec parameterSpec : spec.getPresenterArgs()) {
blockBuilder.add(parameterSpec.name);
if (i++ < spec.getPresenterArgs().size() - 1) {
blockBuilder.add(", ");
}
}
blockBuilder.add(");\n");
MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder("providesPresenter")
.addModifiers(Modifier.PUBLIC)
.returns(spec.getPresenterTypeName())
.addAnnotation(Provides.class)
.addParameters(spec.getProvideParameters())
.addCode(blockBuilder.build());
if (spec.getScopeAnnotationSpec() != null) {
methodSpecBuilder.addAnnotation(spec.getScopeAnnotationSpec());
}
return TypeSpec.classBuilder(spec.getClassName().simpleName())
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Module.class)
.addMethod(methodSpecBuilder.build())
.build();
}
}