Skip to content

Instantly share code, notes, and snippets.

@xizzhu
Last active July 21, 2016 20:03
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xizzhu/f4be1de7c1dec6b02c4eed203dfea320 to your computer and use it in GitHub Desktop.
Save xizzhu/f4be1de7c1dec6b02c4eed203dfea320 to your computer and use it in GitHub Desktop.
Auto Value Shared Preferences Extension
package com.github.xizzhu.auto.value.sharedpreferences;
import com.google.auto.service.AutoService;
import com.google.auto.value.extension.AutoValueExtension;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.type.TypeMirror;
@AutoService(AutoValueExtension.class)
public final class AutoValueSharedPreferencesExtension extends AutoValueExtension {
private static final ClassName SHARED_PREFERENCES_CLASS = ClassName.get("android.content", "SharedPreferences");
private static final String FACTORY_METHOD_NAME = "createFromSharedPreferences";
@Override
public boolean applicable(Context context) {
TypeMirror annotationType = context.processingEnvironment().getElementUtils()
.getTypeElement(KeyName.class.getName()).asType();
for (ExecutableElement element : context.properties().values()) {
for (AnnotationMirror annotation : element.getAnnotationMirrors()) {
if (annotation.getAnnotationType().equals(annotationType)) {
return true;
}
}
}
return false;
}
@Override
public String generateClass(Context context, String className, String classToExtend, boolean isFinal) {
final String packageName = context.packageName();
final Map<String, ExecutableElement> properties = context.properties();
TypeSpec subclass = TypeSpec.classBuilder(className)
.addModifiers(isFinal ? Modifier.FINAL : Modifier.ABSTRACT)
.superclass(ClassName.get(packageName, classToExtend))
.addMethod(generateConstructor(properties))
.addMethod(generateFactoryMethod(context, properties))
.build();
return JavaFile.builder(packageName, subclass).build().toString();
}
private static MethodSpec generateConstructor(Map<String, ExecutableElement> properties) {
List<ParameterSpec> params = new ArrayList<>();
for (Map.Entry<String, ExecutableElement> entry : properties.entrySet()) {
TypeName typeName = TypeName.get(entry.getValue().getReturnType());
params.add(ParameterSpec.builder(typeName, entry.getKey()).build());
}
StringBuilder body = new StringBuilder("super(");
for (int i = properties.size(); i > 0; --i) {
body.append("$N");
if (i > 1) body.append(", ");
}
body.append(")");
return MethodSpec.constructorBuilder()
.addParameters(params)
.addStatement(body.toString(), properties.keySet().toArray())
.build();
}
private static MethodSpec generateFactoryMethod(Context context, Map<String, ExecutableElement> properties) {
MethodSpec.Builder factoryMethod = MethodSpec.methodBuilder(FACTORY_METHOD_NAME)
.addModifiers(Modifier.STATIC)
.addParameter(SHARED_PREFERENCES_CLASS, "sharedPreferences");
String classSimpleName = "AutoValue_" + context.autoValueClass().getSimpleName().toString();
factoryMethod.returns(ClassName.get(context.packageName(), classSimpleName));
for (Map.Entry<String, ExecutableElement> entry : properties.entrySet()) {
String name = entry.getKey();
ExecutableElement element = entry.getValue();
TypeName typeName = TypeName.get(element.getReturnType());
KeyName keyName = element.getAnnotation(KeyName.class);
String key = keyName != null ? keyName.key() : "";
String defaultValue = keyName != null ? keyName.defaultValue() : "";
if (typeName.equals(TypeName.BOOLEAN) || typeName.equals(TypeName.BOOLEAN.box())) {
factoryMethod.addStatement("boolean $N = sharedPreferences.getBoolean($S, $L)",
name, key, Boolean.parseBoolean(defaultValue));
} else if (typeName.equals(TypeName.FLOAT) || typeName.equals(TypeName.FLOAT.box())) {
factoryMethod.addStatement("float $N = sharedPreferences.getFloat($S, $LF)",
name, key, Float.parseFloat(defaultValue));
} else if (typeName.equals(TypeName.INT) || typeName.equals(TypeName.INT.box())) {
factoryMethod.addStatement("int $N = sharedPreferences.getInt($S, $L)",
name, key, Integer.parseInt(defaultValue));
} else if (typeName.equals(TypeName.LONG) || typeName.equals(TypeName.LONG.box())) {
factoryMethod.addStatement("long $N = sharedPreferences.getLong($S, $LL)",
name, key, Long.parseLong(defaultValue));
} else if (typeName.equals(TypeName.get(String.class))) {
factoryMethod.addStatement("String $N = sharedPreferences.getString($S, $S)",
name, key, defaultValue);
} else if (typeName.equals(ParameterizedTypeName.get(ClassName.get("java.util", "Set"), TypeName.get(String.class)))) {
factoryMethod.addStatement("Set<String> $N = sharedPreferences.getStringSet($S, null)",
name, key);
} else {
// TODO support other types
}
}
Object[] propertyNames = properties.keySet().toArray();
StringBuilder construct = new StringBuilder("new ").append(classSimpleName).append("(");
for (int i = 0; i < propertyNames.length; ++i) {
construct.append("$N").append(", ");
}
construct.setLength(construct.length() - 2);
construct.append(")");
factoryMethod.addCode("return ")
.addCode(CodeBlock.builder().addStatement(construct.toString(), propertyNames).build());
return factoryMethod.build();
}
}
package com.github.xizzhu.auto.value.sharedpreferences;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.METHOD})
public @interface KeyName {
String key();
String defaultValue();
}
package com.github.xizzhu.auto.value.sharedpreferences.sample;
import android.content.SharedPreferences;
import com.google.auto.value.AutoValue;
import com.github.xizzhu.auto.value.sharedpreferences.KeyName;
import java.util.Set;
@AutoValue
public abstract class MyPreference {
@KeyName(key = "a_boolean", defaultValue = "true")
abstract boolean aBoolean();
@KeyName(key = "a_float", defaultValue = "12.34F")
abstract float aFloat();
@KeyName(key = "an_int", defaultValue = "56789")
abstract int anInt();
@KeyName(key = "a_long", defaultValue = "1234567890")
abstract long aLong();
@KeyName(key = "another_Long", defaultValue = "9876543210")
abstract Long anotherLong();
@KeyName(key = "a_string", defaultValue = "yep, it is a string")
abstract String aString();
@KeyName(key = "a_string_set", defaultValue = "no body cares about the default value")
abstract Set<String> aStringSet();
public static MyPreference create(SharedPreferences sharedPreferences) {
return AutoValue_MyPreference.createFromSharedPreferences(sharedPreferences);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment