Skip to content

Commit

Permalink
generate impl like gson
Browse files Browse the repository at this point in the history
  • Loading branch information
anirudhramanan committed Nov 30, 2017
1 parent 25fbda4 commit 5d81b61
Showing 1 changed file with 139 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,14 @@
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeVariableName;
import com.vimeo.stag.processor.generators.model.ClassInfo;
Expand All @@ -39,11 +43,13 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import javax.lang.model.element.Modifier;
import javax.lang.model.type.TypeMirror;
Expand Down Expand Up @@ -80,7 +86,7 @@ public void setGeneratedStagFactoryWrappers(@NotNull List<String> generatedStagF
}

@Nullable
public ClassInfo getKnownClass(@NotNull TypeMirror typeMirror) {
ClassInfo getKnownClass(@NotNull TypeMirror typeMirror) {
return mKnownClasses.get(typeMirror.toString());
}

Expand All @@ -102,11 +108,74 @@ public TypeSpec createStagSpec() {
@NotNull
private TypeSpec getAdapterFactorySpec() {
TypeVariableName genericTypeName = TypeVariableName.get("T");
TypeVariableName wildcardTypeName = TypeVariableName.get("?");

TypeSpec.Builder adapterFactoryBuilder = TypeSpec.classBuilder(CLASS_TYPE_ADAPTER_FACTORY)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addSuperinterface(TypeAdapterFactory.class);

TypeName callsTypeName = ParameterizedTypeName.get(ClassName.get(ThreadLocal.class),
ParameterizedTypeName.get(ClassName.get(Map.class), ParameterizedTypeName.get(ClassName.get(TypeToken.class), wildcardTypeName), TypeVariableName.get("FutureTypeAdapter<?>")));

TypeName typeTokenCacheTypeName = ParameterizedTypeName.get(ClassName.get(ConcurrentHashMap.class), ParameterizedTypeName.get(ClassName.get(TypeToken.class), wildcardTypeName),
ParameterizedTypeName.get(ClassName.get(TypeAdapter.class), wildcardTypeName));

FieldSpec.Builder callsFieldBuilder = FieldSpec.builder(callsTypeName, "calls").
addModifiers(Modifier.PRIVATE, Modifier.FINAL).initializer("new " + callsTypeName.toString() + "()");

FieldSpec.Builder typeTokenCacheFieldBuilder = FieldSpec.builder(typeTokenCacheTypeName, "typeTokenCache").
addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC).initializer("new " + typeTokenCacheTypeName.toString() + "(100)");

MethodSpec.Builder getAdapterMethodBuilder = MethodSpec.methodBuilder("getAdapter")
.addModifiers(Modifier.PUBLIC)
.addTypeVariable(genericTypeName)
.addAnnotation(AnnotationSpec.builder(SuppressWarnings.class)
.addMember("value", "\"unchecked\"")
.addMember("value", "\"rawtypes\"")
.build())
.returns(ParameterizedTypeName.get(ClassName.get(TypeAdapter.class), genericTypeName))
.addParameter(Gson.class, "gson")
.addParameter(ParameterizedTypeName.get(ClassName.get(TypeToken.class), genericTypeName), "typeToken");

getAdapterMethodBuilder.addStatement("TypeAdapter<?> cached = typeTokenCache.get(typeToken)");
getAdapterMethodBuilder.beginControlFlow("if (cached != null)");
getAdapterMethodBuilder.addStatement("return (TypeAdapter) cached");
getAdapterMethodBuilder.endControlFlow();
getAdapterMethodBuilder.addStatement("Map<TypeToken<?>, FutureTypeAdapter<?>> threadCalls = calls.get()");
getAdapterMethodBuilder.addStatement("boolean requiresThreadLocalCleanup = false");
getAdapterMethodBuilder.beginControlFlow("if (threadCalls == null)");
getAdapterMethodBuilder.addStatement("threadCalls = new " + ParameterizedTypeName.get(ClassName.get(HashMap.class), TypeVariableName.get("TypeToken<?>"), TypeVariableName.get("FutureTypeAdapter<?>")) + "()");
getAdapterMethodBuilder.addStatement("calls.set(threadCalls)");
getAdapterMethodBuilder.addStatement("requiresThreadLocalCleanup = true");
getAdapterMethodBuilder.endControlFlow();
getAdapterMethodBuilder.addStatement("FutureTypeAdapter ongoingCall = (FutureTypeAdapter) threadCalls.get(typeToken)");
getAdapterMethodBuilder.beginControlFlow("if (ongoingCall != null)");
getAdapterMethodBuilder.addStatement("return ongoingCall");
getAdapterMethodBuilder.endControlFlow();
getAdapterMethodBuilder.beginControlFlow("try");
getAdapterMethodBuilder.addCode("FutureTypeAdapter call = new FutureTypeAdapter<T>();\n" +
"threadCalls.put(typeToken, call);\n" +
"\n" +
"TypeAdapter typeAdapter = this.create(gson, typeToken);\n" +
"if (typeAdapter == null) {\n" +
" typeAdapter = gson.getAdapter(typeToken);\n" +
"}\n" +
"\n" +
"if (typeAdapter != null) {\n" +
" typeTokenCache.put(typeToken, typeAdapter);\n" +
" return typeAdapter;\n" +
"}\n" +
"\n" +
"throw new IllegalArgumentException(\"GSON cannot handle \" + typeToken);\n");
getAdapterMethodBuilder.endControlFlow();
getAdapterMethodBuilder.beginControlFlow("finally");
getAdapterMethodBuilder.addCode("threadCalls.remove(typeToken);\n" +
"\n" +
"if (requiresThreadLocalCleanup) {\n" +
" calls.remove();\n" +
"}\n");
getAdapterMethodBuilder.endControlFlow();

MethodSpec.Builder createMethodBuilder = MethodSpec.methodBuilder("create")
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
Expand All @@ -120,19 +189,85 @@ private TypeSpec getAdapterFactorySpec() {
.addParameter(ParameterizedTypeName.get(ClassName.get(TypeToken.class), genericTypeName),
"type");

createMethodBuilder.addCode("TypeAdapter typeAdapter = typeTokenCache.get(type);\n" +
"if (typeAdapter != null) {\n" +
" return typeAdapter;\n" +
"}\n\n");
createMethodBuilder.addStatement("TypeAdapter<T> result = null");

int count = 1;
for (String stagFileName : generatedStagFactoryWrappers) {
String fieldName = "adapter" + count;
createMethodBuilder.addStatement("TypeAdapter<T> " + fieldName + " = " + stagFileName + ".getAdapter(gson, type)");
createMethodBuilder.addStatement("TypeAdapter<T> " + fieldName + " = " + stagFileName + ".getAdapter(gson, type, this)");
createMethodBuilder.beginControlFlow("if (" + fieldName + " != null)");
createMethodBuilder.addStatement("return " + fieldName);
createMethodBuilder.addStatement("result = " + fieldName);
createMethodBuilder.endControlFlow();
count++;
}
createMethodBuilder.addStatement("return null");

createMethodBuilder.addCode("\nif (result != null) {\n" +
" typeTokenCache.put(type, result);\n" +
"}\n\n");
createMethodBuilder.addStatement("return result");

adapterFactoryBuilder.addMethod(createMethodBuilder.build());
adapterFactoryBuilder.addMethod(getAdapterMethodBuilder.build());
adapterFactoryBuilder.addField(callsFieldBuilder.build());
adapterFactoryBuilder.addField(typeTokenCacheFieldBuilder.build());
adapterFactoryBuilder.addType(createFutureTypeAdapter());

return adapterFactoryBuilder.build();
}

@NotNull
private TypeSpec createFutureTypeAdapter() {
TypeVariableName genericTypeName = TypeVariableName.get("T");

TypeSpec.Builder futureTypeAdapterBuilder = TypeSpec.classBuilder("FutureTypeAdapter")
.addTypeVariable(genericTypeName)
.addModifiers(Modifier.STATIC)
.superclass(ParameterizedTypeName.get(ClassName.get(TypeAdapter.class), genericTypeName));


TypeName parameterizedTypeName = ParameterizedTypeName.get(ClassName.get(TypeAdapter.class), genericTypeName);
FieldSpec.Builder delegateFieldBuilder = FieldSpec.builder(parameterizedTypeName, "delegate")
.addModifiers(Modifier.PRIVATE);

MethodSpec.Builder setDelegateMethodBuilder = MethodSpec.methodBuilder("setDelegate")
.addModifiers(Modifier.PUBLIC)
.addParameter(parameterizedTypeName, "typeAdapter")
.beginControlFlow("if (delegate != null)")
.addStatement("throw new AssertionError()")
.endControlFlow()
.addStatement("delegate = typeAdapter");

MethodSpec.Builder readMethodBuilder = MethodSpec.methodBuilder("read")
.addModifiers(Modifier.PUBLIC)
.returns(genericTypeName)
.addAnnotation(Override.class)
.addParameter(JsonReader.class, "in")
.addException(IOException.class)
.beginControlFlow("if (delegate == null)")
.addStatement("throw new IllegalStateException()")
.endControlFlow()
.addStatement("return delegate.read(in)");

MethodSpec.Builder writeMethodBuilder = MethodSpec.methodBuilder("write")
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.addParameter(JsonWriter.class, "out")
.addParameter(genericTypeName, "value")
.addException(IOException.class)
.beginControlFlow("if (delegate == null)")
.addStatement("throw new IllegalStateException()")
.endControlFlow()
.addStatement("delegate.write(out, value)");

futureTypeAdapterBuilder.addField(delegateFieldBuilder.build());
futureTypeAdapterBuilder.addMethod(setDelegateMethodBuilder.build());
futureTypeAdapterBuilder.addMethod(readMethodBuilder.build());
futureTypeAdapterBuilder.addMethod(writeMethodBuilder.build());

return futureTypeAdapterBuilder.build();
}
}

0 comments on commit 5d81b61

Please sign in to comment.