package io.gitlab.jfronny.gson.compile.processor.adapter.impl; import com.squareup.javapoet.*; import io.gitlab.jfronny.commons.data.MutCollection; import io.gitlab.jfronny.gson.compile.processor.Cl; import io.gitlab.jfronny.gson.compile.processor.core.TypeHelper; import io.gitlab.jfronny.gson.compile.processor.adapter.Adapter; import javax.lang.model.element.Modifier; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; import java.io.IOException; import java.util.*; public class CollectionAdapter extends Adapter { @Override public Hydrated instantiate() { return new Hydrated(); } public class Hydrated extends Adapter.Hydrated { private static final Map, List>> SUPPORTED = MutCollection.mapOf( Set.class, List.of(LinkedHashSet.class, HashSet.class, TreeSet.class), List.class, List.of(ArrayList.class, LinkedList.class), Queue.class, List.of(ArrayDeque.class, LinkedList.class), Deque.class, List.of(ArrayDeque.class) ); private DeclaredType type; private TypeName implType; private TypeMirror componentType; @Override public boolean applies() { return type != null; } @Override protected void afterHydrate() { type = TypeHelper.asDeclaredType(super.type); componentType = null; if (type == null) return; List typeArguments = type.getTypeArguments(); if (typeArguments.size() == 0) { type = null; } else { componentType = typeArguments.get(0); String ts = TypeHelper.asDeclaredType(typeUtils.erasure(type)).asElement().toString(); for (Map.Entry, List>> entry : SUPPORTED.entrySet()) { if (entry.getKey().getCanonicalName().equals(ts)) { implType = TypeName.get(entry.getValue().get(0)); return; } for (Class klazz : entry.getValue()) { if (klazz.getCanonicalName().equals(ts)) { implType = TypeName.get(klazz); return; } } } type = null; componentType = null; } } @Override public void generateWrite(Runnable writeGet) { code.addStatement("writer.beginArray()"); code.add("for ($T $N : ", componentType, argName); writeGet.run(); code.beginControlFlow(")") .beginControlFlow("if ($N == null)", argName) .addStatement("if (writer.getSerializeNulls()) writer.nullValue()") .nextControlFlow("else"); generateWrite(code, componentType, argName, componentType.getAnnotationMirrors(), () -> code.add(argName)); code.endControlFlow().endControlFlow().addStatement("writer.endArray()"); } @Override public void generateRead() { CodeBlock.Builder kode = CodeBlock.builder(); kode.addStatement("$T list = new $T<>()", typeName, implType); // Coerce kode.beginControlFlow("if (reader.isLenient() && reader.peek() != $T.BEGIN_ARRAY)", Cl.GSON_TOKEN) .add("list.add("); generateRead(kode, componentType, argName, componentType.getAnnotationMirrors()); kode.add(");\n").addStatement("return list").endControlFlow(); kode.addStatement("reader.beginArray()") .beginControlFlow("while (reader.hasNext())") .beginControlFlow("if (reader.peek() == $T.NULL)", Cl.GSON_TOKEN) .addStatement("reader.nextNull()") .addStatement("list.add(null)") .nextControlFlow("else") .add("list.add("); generateRead(kode, componentType, argName, componentType.getAnnotationMirrors()); kode.add(");\n") .endControlFlow() .endControlFlow() .addStatement("reader.endArray()") .addStatement("return list"); String methodName = "read$" + name; klazz.addMethod( MethodSpec.methodBuilder(methodName) .addModifiers(Modifier.PRIVATE, Modifier.STATIC) .returns(typeName) .addParameter(Cl.GSON_READER, "reader") .addException(IOException.class) .addCode(kode.build()) .build() ); code.add("$N(reader)", methodName); } } }