package architect.autopath.compiler.composer;
import com.hannesdorfmann.parcelableplease.annotation.ParcelablePlease;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ArrayTypeName;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Generated;
import javax.lang.model.element.Modifier;
import architect.autopath.compiler.spec.ConstructorSpec;
import architect.autopath.compiler.spec.ParamSpec;
import architect.autopath.compiler.spec.PathSpec;
import processorworkflow.AbstractComposer;
/**
* @author Lukasz Piliszczuk - lukasz.pili@gmail.com
*/
public class PathComposer extends AbstractComposer<PathSpec> {
private static final ClassName NAVIGATIONPATH_CLS = ClassName.get("architect", "StackPath");
private static final ClassName PARCEL_CLS = ClassName.get("android.os", "Parcel");
private static final ClassName PARCEL_CREATOR_CLS = ClassName.get("android.os.Parcelable", "Creator");
private static final ClassName CONTEXT_CLS = ClassName.get("android.content", "Context");
public PathComposer(List<architect.autopath.compiler.spec.PathSpec> specs) {
super(specs);
}
@Override
protected JavaFile compose(PathSpec spec) {
TypeSpec typeSpec = build(spec);
return JavaFile.builder(spec.getClassName().packageName(), typeSpec).build();
}
private TypeSpec build(PathSpec spec) {
StringBuilder constructorParamsStringBuilder = new StringBuilder();
List<FieldSpec> fieldSpecs = new ArrayList<>();
for (ParamSpec paramSpec : spec.getFields()) {
fieldSpecs.add(FieldSpec.builder(paramSpec.getTypeName(), paramSpec.getName()).build());
constructorParamsStringBuilder.append(paramSpec.getName()).append(", ");
}
if (constructorParamsStringBuilder.length() > 0) {
constructorParamsStringBuilder.delete(constructorParamsStringBuilder.length() - 2, constructorParamsStringBuilder.length());
}
String constructorParamsString = constructorParamsStringBuilder.toString();
List<MethodSpec> methodSpecs = new ArrayList<>();
for (ConstructorSpec constructorSpec : spec.getConstructors()) {
MethodSpec.Builder builder = MethodSpec.constructorBuilder();
builder.addModifiers(Modifier.PUBLIC);
for (architect.autopath.compiler.spec.ParamSpec paramSpec : constructorSpec.getFields()) {
builder.addParameter(paramSpec.getTypeName(), paramSpec.getName());
builder.addStatement("this.$L = $L", paramSpec.getName(), paramSpec.getName());
}
methodSpecs.add(builder.build());
}
MethodSpec parcelConstructorMethodSpec = MethodSpec.constructorBuilder()
.addModifiers(Modifier.PRIVATE)
.addParameter(PARCEL_CLS, "parcel")
.addStatement("super(parcel)")
.build();
methodSpecs.add(parcelConstructorMethodSpec);
MethodSpec withScopeSpec = MethodSpec.methodBuilder("withScope")
.addModifiers(Modifier.PUBLIC)
.returns(spec.getTargetTypeName())
.addAnnotation(Override.class)
.addStatement("return new $T($L)", spec.getTargetTypeName(), constructorParamsString)
.build();
methodSpecs.add(withScopeSpec);
MethodSpec withViewSpec = MethodSpec.methodBuilder("withView")
.addModifiers(Modifier.PUBLIC)
.returns(spec.getViewTypeName())
.addAnnotation(Override.class)
.addParameter(CONTEXT_CLS, "context")
.addStatement("return new $T(context)", spec.getViewTypeName())
.build();
methodSpecs.add(withViewSpec);
ClassName parcelablePleaseClassName = ClassName.get(spec.getClassName().packageName(), spec.getClassName().simpleName() + "ParcelablePlease");
MethodSpec readParcelSpec = MethodSpec.methodBuilder("readParcel")
.addModifiers(Modifier.PROTECTED)
.addAnnotation(Override.class)
.addParameter(PARCEL_CLS, "parcel")
.addStatement("$T.readFromParcel(this, parcel)", parcelablePleaseClassName)
.build();
methodSpecs.add(readParcelSpec);
MethodSpec writeParcelSpec = MethodSpec.methodBuilder("writeParcel")
.addModifiers(Modifier.PROTECTED)
.addAnnotation(Override.class)
.addParameter(PARCEL_CLS, "parcel")
.addStatement("$T.writeToParcel(this, parcel, 0)", parcelablePleaseClassName)
.build();
methodSpecs.add(writeParcelSpec);
MethodSpec creatorCreateFromParcelSpec = MethodSpec.methodBuilder("createFromParcel")
.addModifiers(Modifier.PUBLIC)
.returns(spec.getClassName())
.addAnnotation(Override.class)
.addParameter(PARCEL_CLS, "in")
.addStatement("return new $T(in)", spec.getClassName())
.build();
MethodSpec creatorNewArraySpec = MethodSpec.methodBuilder("newArray")
.addModifiers(Modifier.PUBLIC)
.returns(ArrayTypeName.of(spec.getClassName()))
.addAnnotation(Override.class)
.addParameter(TypeName.INT, "size")
.addStatement("return new $T[size]", spec.getClassName())
.build();
TypeName creatorTypeName = ParameterizedTypeName.get(PARCEL_CREATOR_CLS, spec.getClassName());
TypeSpec creatorTypeSpec = TypeSpec.anonymousClassBuilder("")
.superclass(creatorTypeName)
.addMethod(creatorCreateFromParcelSpec)
.addMethod(creatorNewArraySpec)
.build();
FieldSpec creatorSpec = FieldSpec.builder(creatorTypeName, "CREATOR")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
.initializer("$L", creatorTypeSpec)
.build();
fieldSpecs.add(creatorSpec);
TypeSpec typeSpec = TypeSpec.classBuilder(spec.getClassName().simpleName())
.addModifiers(Modifier.PUBLIC)
.superclass(ParameterizedTypeName.get(NAVIGATIONPATH_CLS, spec.getTargetTypeName()))
.addAnnotation(AnnotationSpec.builder(Generated.class).addMember("value", "$S", architect.autopath.compiler.AnnotationProcessor.class.getName()).build())
.addAnnotation(AnnotationSpec.builder(ParcelablePlease.class).build())
.addFields(fieldSpecs)
.addMethods(methodSpecs)
.build();
return typeSpec;
}
}