package pocketknife.internal.codegen.binding;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeVariableName;
import pocketknife.internal.IntentBinding;
import pocketknife.internal.codegen.Access;
import pocketknife.internal.codegen.BaseGenerator;
import pocketknife.internal.codegen.IntentFieldBinding;
import pocketknife.internal.codegen.TypeUtil;
import javax.lang.model.type.TypeMirror;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import static javax.lang.model.element.Modifier.PUBLIC;
public class IntentBindingAdapterGenerator extends BaseGenerator {
public static final String BIND_EXTRAS_METHOD = "bindExtras";
private static final String TARGET = "target";
private static final String INTENT = "intent";
private final Set<IntentFieldBinding> fields = new LinkedHashSet<IntentFieldBinding>();
private final String classPackage;
private final String className;
private final TypeMirror targetType;
private ClassName parentAdapter;
public IntentBindingAdapterGenerator(String classPackage, String className, TypeMirror targetType, TypeUtil typeUtil) {
super(typeUtil);
this.classPackage = classPackage;
this.className = className;
this.targetType = targetType;
}
public void addField(IntentFieldBinding binding) {
fields.add(binding);
}
public JavaFile generate() throws IOException {
TypeVariableName t = TypeVariableName.get("T");
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className)
.addTypeVariable(TypeVariableName.get(t.name, ClassName.get(targetType)))
.addModifiers(PUBLIC)
.addJavadoc(getGeneratedComment(IntentBindingAdapterGenerator.class));
// Add Interface or Parent Class
if (parentAdapter != null) {
classBuilder.superclass(ParameterizedTypeName.get(parentAdapter, t));
} else {
classBuilder.addSuperinterface(ParameterizedTypeName.get(ClassName.get(IntentBinding.class), t));
}
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(BIND_EXTRAS_METHOD)
.addModifiers(PUBLIC)
.addParameter(ParameterSpec.builder(t, TARGET).build())
.addParameter(ParameterSpec.builder(ClassName.get(typeUtil.intentType), INTENT).build());
if (parentAdapter != null) {
methodBuilder.addStatement("super.$L($N, $N)", BIND_EXTRAS_METHOD, TARGET, INTENT);
}
methodBuilder.beginControlFlow("if ($N == null)", INTENT);
methodBuilder.addStatement("throw new $T($S)", IllegalStateException.class, "intent is null");
methodBuilder.endControlFlow();
for (IntentFieldBinding field : fields) {
addBindExtraField(methodBuilder, field);
}
classBuilder.addMethod(methodBuilder.build());
return JavaFile.builder(classPackage, classBuilder.build()).build();
}
private void addBindExtraField(MethodSpec.Builder methodBuilder, IntentFieldBinding field) {
Access access = field.getAccess();
boolean method = access.getType() == Access.Type.METHOD;
if (field.getIntentSerializer() == null) {
methodBuilder.beginControlFlow("if ($N.hasExtra($S))", INTENT, field.getKey().getValue());
List<Object> stmtArgs = new ArrayList<Object>();
String stmt;
if (method) {
stmt = "$N.$N(";
} else {
stmt = "$N.$N = ";
}
stmtArgs.add(TARGET);
stmtArgs.add(access.getSetter());
if (field.needsToBeCast()) {
stmt = stmt.concat("($T)");
stmtArgs.add(field.getType());
}
stmt = stmt.concat("$N.get$LExtra($S");
stmtArgs.add(INTENT);
stmtArgs.add(field.getIntentType());
stmtArgs.add(field.getKey().getValue());
if (field.hasDefault()) {
if (method) {
stmt = stmt.concat(", $N.$N()");
} else {
stmt = stmt.concat(", $N.$N");
}
stmtArgs.add(TARGET);
stmtArgs.add(access.getGetter());
}
stmt = stmt.concat(")");
if (method) {
stmt = stmt.concat(")");
}
methodBuilder.addStatement(stmt, stmtArgs.toArray(new Object[stmtArgs.size()]));
if (field.isRequired()) {
methodBuilder.nextControlFlow("else")
.addStatement("throw new $T($S)", IllegalStateException.class, String.format("Required Extra with key '%s' was not found for '%s'."
+ "If this is not required add '@NotRequired' annotation.", field.getKey().getValue(), field.getName()));
}
methodBuilder.endControlFlow();
} else {
String stmt;
if (method) {
stmt = "$N.$N(new $T().get($N, $N.$N(), $S))";
} else {
stmt = "$N.$N = new $T().get($N, $N.$N, $S)";
}
methodBuilder.addStatement(stmt, TARGET, access.getSetter(), field.getIntentSerializer(), INTENT, TARGET,
access.getGetter(), field.getKey().getValue());
}
}
public void setParentAdapter(String packageName, String className) {
this.parentAdapter = ClassName.get(packageName, className);
}
}