package com.rejasupotaro.android.kvs.internal; import com.rejasupotaro.android.kvs.PrefsSchema; 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.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.annotation.processing.Filer; import javax.lang.model.element.Modifier; public class SchemaWriter { private SchemaModel model; public SchemaWriter(SchemaModel model) { this.model = model; } public void write(Filer filer) throws IOException { TypeSpec.Builder classBuilder = TypeSpec.classBuilder(model.getClassName().simpleName()); classBuilder.addModifiers(Modifier.PUBLIC, Modifier.FINAL); ClassName superClassName = ClassName.get(PrefsSchema.class); classBuilder.superclass(superClassName); List<FieldSpec> fieldSpecs = createFields(); classBuilder.addFields(fieldSpecs); List<MethodSpec> methodSpecs = new ArrayList<>(); methodSpecs.addAll(createConstructors()); methodSpecs.add(createInitializeMethod()); methodSpecs.addAll(createMethods()); classBuilder.addMethods(methodSpecs); TypeSpec outClass = classBuilder.build(); JavaFile.builder(model.getClassName().packageName(), outClass) .build() .writeTo(filer); } private List<FieldSpec> createFields() { List<FieldSpec> fieldSpecs = new ArrayList<>(); fieldSpecs.add(FieldSpec.builder(String.class, "TABLE_NAME", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) .initializer("$S", model.getTableName()) .build()); fieldSpecs.add(FieldSpec.builder(model.getClassName(), "singleton", Modifier.PRIVATE, Modifier.STATIC) .build()); return fieldSpecs; } private List<MethodSpec> createConstructors() { List<MethodSpec> methodSpecs = new ArrayList<>(); methodSpecs.add(MethodSpec.constructorBuilder() .addModifiers(Modifier.PUBLIC) .addParameter(ClassName.get("android.content", "Context"), "context") .addStatement("init(context, TABLE_NAME)") .build()); methodSpecs.add(MethodSpec.constructorBuilder() .addModifiers(Modifier.PUBLIC) .addParameter(ClassName.get("android.content", "SharedPreferences"), "prefs") .addStatement("init(prefs)") .build()); return methodSpecs; } private MethodSpec createInitializeMethod() { if ("java.lang.Object".equals(model.getBuilderClassFqcn())) { return MethodSpec.methodBuilder("get") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(model.getClassName()) .addParameter(ClassName.get("android.content", "Context"), "context") .addStatement("if (singleton != null) return singleton") .addStatement("synchronized ($N.class) { if (singleton == null) singleton = new $N(context); }", model.getClassName().simpleName(), model.getClassName().simpleName()) .addStatement("return singleton") .build(); } else { return MethodSpec.methodBuilder("get") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(model.getClassName()) .addParameter(ClassName.get("android.content", "Context"), "context") .addStatement("if (singleton != null) return singleton") .addStatement("synchronized ($N.class) { if (singleton == null) singleton = new $N().build(context); }", model.getClassName().simpleName(), model.getBuilderClassFqcn()) .addStatement("return singleton") .build(); } } private List<MethodSpec> createMethods() { List<MethodSpec> methodSpecs = new ArrayList<>(); for (Field field : model.getKeys()) { methodSpecs.addAll(createMethods(field)); } return methodSpecs; } private List<MethodSpec> createMethods(Field field) { List<MethodSpec> methodSpecs = new ArrayList<>(); if (TypeName.BOOLEAN.equals(field.getFieldType())) { String argTypeOfSuperMethod = "boolean"; String defaultValue = field.getValue() == null ? "false" : field.getValue().toString(); methodSpecs.add(createGetterWithDefaultValue(field, argTypeOfSuperMethod)); methodSpecs.add(createGetter(field, argTypeOfSuperMethod, defaultValue)); methodSpecs.addAll(createSetter(field, argTypeOfSuperMethod)); methodSpecs.add(createHasMethod(field)); methodSpecs.add(createRemoveMethod(field)); } else if (ClassName.get(String.class).equals(field.getFieldType())) { String argTypeOfSuperMethod = "String"; String defaultValue = field.getValue() == null ? "" : field.getValue().toString(); String methodName = "get" + StringUtils.capitalize(field.getName()); String superMethodName = "get" + StringUtils.capitalize(argTypeOfSuperMethod); if (field.hasSerializer()) { methodSpecs.add(MethodSpec.methodBuilder(methodName) .addModifiers(Modifier.PUBLIC) .returns(field.getSerializeType()) .addStatement("return new $T().deserialize($N($S, \"\"))", field.getSerializerType(), superMethodName, field.getPrefKeyName()) .build()); } else { methodSpecs.add(MethodSpec.methodBuilder(methodName) .addModifiers(Modifier.PUBLIC) .returns(field.getFieldType()) .addStatement("return $N($S, $S)", superMethodName, field.getPrefKeyName(), defaultValue) .build()); } methodSpecs.addAll(createSetter(field, argTypeOfSuperMethod)); methodSpecs.add(createGetterWithDefaultValue(field, argTypeOfSuperMethod)); methodSpecs.add(createHasMethod(field)); methodSpecs.add(createRemoveMethod(field)); } else if (TypeName.FLOAT.equals(field.getFieldType())) { String argTypeOfSuperMethod = "float"; String defaultValue = field.getValue() == null ? "0.0F" : field.getValue().toString() + "f"; methodSpecs.add(createGetterWithDefaultValue(field, argTypeOfSuperMethod)); methodSpecs.add(createGetter(field, argTypeOfSuperMethod, defaultValue)); methodSpecs.addAll(createSetter(field, argTypeOfSuperMethod)); methodSpecs.add(createHasMethod(field)); methodSpecs.add(createRemoveMethod(field)); } else if (TypeName.INT.equals(field.getFieldType())) { String argTypeOfSuperMethod = "int"; String defaultValue = field.getValue() == null ? "0" : field.getValue().toString(); methodSpecs.add(createGetterWithDefaultValue(field, argTypeOfSuperMethod)); methodSpecs.add(createGetter(field, argTypeOfSuperMethod, defaultValue)); methodSpecs.addAll(createSetter(field, argTypeOfSuperMethod)); methodSpecs.add(createHasMethod(field)); methodSpecs.add(createRemoveMethod(field)); } else if (TypeName.LONG.equals(field.getFieldType())) { String argTypeOfSuperMethod = "long"; String defaultValue = field.getValue() == null ? "0L" : field.getValue().toString() + "L"; methodSpecs.add(createGetterWithDefaultValue(field, argTypeOfSuperMethod)); methodSpecs.add(createGetter(field, argTypeOfSuperMethod, defaultValue)); methodSpecs.addAll(createSetter(field, argTypeOfSuperMethod)); methodSpecs.add(createHasMethod(field)); methodSpecs.add(createRemoveMethod(field)); } else if (ParameterizedTypeName.get(Set.class, String.class).equals(field.getFieldType())) { String argTypeOfSuperMethod = "StringSet"; String methodName = "get" + StringUtils.capitalize(field.getName()); String superMethodName = "get" + StringUtils.capitalize(argTypeOfSuperMethod); methodSpecs.add(MethodSpec.methodBuilder(methodName) .addModifiers(Modifier.PUBLIC) .returns(field.getFieldType()) .addStatement("return $N($S, new $T<String>())", superMethodName, field.getPrefKeyName(), ClassName.get(HashSet.class)) .build()); methodSpecs.add(createGetterWithDefaultValue(field, argTypeOfSuperMethod)); methodSpecs.addAll(createSetter(field, argTypeOfSuperMethod)); methodSpecs.add(createHasMethod(field)); methodSpecs.add(createRemoveMethod(field)); } else { throw new IllegalArgumentException(field.getFieldType() + " is not supported"); } return methodSpecs; } private MethodSpec createGetterWithDefaultValue(Field field, String argTypeOfSuperMethod) { String methodName = "get" + StringUtils.capitalize(field.getName()); String superMethodName = "get" + StringUtils.capitalize(argTypeOfSuperMethod); String parameterName = "defValue"; if (field.hasSerializer()) { return MethodSpec.methodBuilder(methodName) .addModifiers(Modifier.PUBLIC) .addParameter(field.getFieldType(), parameterName) .returns(field.getSerializeType()) .addStatement("return new $T().deserialize($N($S, $L))", field.getSerializerType(), superMethodName, field.getPrefKeyName(), parameterName) .build(); } else { return MethodSpec.methodBuilder(methodName) .addModifiers(Modifier.PUBLIC) .addParameter(field.getFieldType(), parameterName) .returns(field.getFieldType()) .addStatement("return $N($S, $N)", superMethodName, field.getPrefKeyName(), parameterName) .build(); } } private MethodSpec createGetter(Field field, String argTypeOfSuperMethod, String defaultValue) { String methodName = "get" + StringUtils.capitalize(field.getName()); String superMethodName = "get" + StringUtils.capitalize(argTypeOfSuperMethod); if (field.hasSerializer()) { return MethodSpec.methodBuilder(methodName) .addModifiers(Modifier.PUBLIC) .returns(field.getSerializeType()) .addStatement("return new $T().deserialize($N($S, $L))", field.getSerializerType(), superMethodName, field.getPrefKeyName(), defaultValue) .build(); } else { return MethodSpec.methodBuilder(methodName) .addModifiers(Modifier.PUBLIC) .returns(field.getFieldType()) .addStatement("return $N($S, $L)", superMethodName, field.getPrefKeyName(), defaultValue) .build(); } } private Collection<MethodSpec> createSetter(Field field, String argTypeOfSuperMethod) { ArrayList<MethodSpec> methodSpecs = new ArrayList<>(); { String methodName = "set" + StringUtils.capitalize(field.getName()); String superMethodName = "put" + StringUtils.capitalize(argTypeOfSuperMethod); if (field.hasSerializer()) { methodSpecs.add(MethodSpec.methodBuilder(methodName) .addModifiers(Modifier.PUBLIC) .returns(void.class) .addParameter(field.getSerializeType(), field.getName()) .addStatement("$N($S, new $T().serialize($N))", superMethodName, field.getPrefKeyName(), field.getSerializerType(), field.getName()) .build()); } else { methodSpecs.add(MethodSpec.methodBuilder(methodName) .addModifiers(Modifier.PUBLIC) .returns(void.class) .addParameter(field.getFieldType(), field.getName()) .addStatement("$N($S, $N)", superMethodName, field.getPrefKeyName(), field.getName()) .build()); } } { String methodName = "put" + StringUtils.capitalize(field.getName()); String superMethodName = "put" + StringUtils.capitalize(argTypeOfSuperMethod); if (field.hasSerializer()) { methodSpecs.add(MethodSpec.methodBuilder(methodName) .addModifiers(Modifier.PUBLIC) .returns(void.class) .addParameter(field.getSerializeType(), field.getName()) .addStatement("$N($S, new $T().serialize($N))", superMethodName, field.getPrefKeyName(), field.getSerializerType(), field.getName()) .build()); } else { methodSpecs.add(MethodSpec.methodBuilder(methodName) .addModifiers(Modifier.PUBLIC) .returns(void.class) .addParameter(field.getFieldType(), field.getName()) .addStatement("$N($S, $N)", superMethodName, field.getPrefKeyName(), field.getName()) .build()); } } return methodSpecs; } private MethodSpec createHasMethod(Field field) { String methodName = "has" + StringUtils.capitalize(field.getName()); return MethodSpec.methodBuilder(methodName) .addModifiers(Modifier.PUBLIC) .returns(boolean.class) .addStatement("return has($S)", field.getPrefKeyName()) .build(); } private MethodSpec createRemoveMethod(Field field) { String methodName = "remove" + StringUtils.capitalize(field.getName()); return MethodSpec.methodBuilder(methodName) .addModifiers(Modifier.PUBLIC) .returns(void.class) .addStatement("remove($S)", field.getPrefKeyName()) .build(); } }