Implement GPrefer
This commit is contained in:
parent
f0f9a171fd
commit
cf795cdedc
|
@ -16,14 +16,15 @@ The goal of this AP is to
|
|||
- Nested serializable types
|
||||
- Arrays
|
||||
- Collections (Sets, Lists, Queues, Deques)
|
||||
- GPrefer to resolve multiple factories/constructors
|
||||
|
||||
## TODO
|
||||
- Setters/field access in read()
|
||||
- Maps with string, primitive or enum keys
|
||||
- Date via ISO8601Utils
|
||||
- Enums
|
||||
- Support for nested types from libraries
|
||||
- Static classes (for configs)
|
||||
- GPrefer to bypass builder/constructor recovery
|
||||
|
||||
## Credit
|
||||
The Gson-Compile processor is based on [gsonvalue](https://github.com/evant/gsonvalue) and [value-processor](https://github.com/evant/value-processor) by Eva Tatarka.
|
||||
|
|
|
@ -5,6 +5,13 @@ import java.lang.annotation.*;
|
|||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface GSerializable {
|
||||
/**
|
||||
* @return The class implementing this types serialization/deserialization. Must have static read/write methods
|
||||
*/
|
||||
Class<?> with() default void.class;
|
||||
|
||||
/**
|
||||
* @return Whether to generate an adapter class to use with normal gson adapter resolution
|
||||
*/
|
||||
boolean generateAdapter() default false;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
package io.gitlab.jfronny.gson;
|
||||
|
||||
import io.gitlab.jfronny.gson.annotations.SerializedName;
|
||||
import io.gitlab.jfronny.gson.compile.annotations.GComment;
|
||||
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
|
||||
import io.gitlab.jfronny.gson.compile.annotations.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
@ -49,5 +48,9 @@ public class Main {
|
|||
|
||||
@GSerializable
|
||||
public record ExampleRecord(String hello, @GComment("Sheesh") ExamplePojo2 pojo) {
|
||||
@GPrefer
|
||||
public ExampleRecord(String yes) {
|
||||
this(yes, null);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -72,7 +72,6 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
if (bld.with == null) throw new IllegalArgumentException("Missing annotation parameter: with");
|
||||
if (bld.generateAdapter == null) throw new IllegalArgumentException("Missing annotation parameter: generateAdapter");
|
||||
if (bld.with.toString().equals("void")) bld.with = null;
|
||||
if (bld.with != null && bld.generateAdapter) throw new IllegalArgumentException("Adapter for " + element + " already exists, not generating another!");
|
||||
|
||||
toGenerate.add(SerializableClass.of((TypeElement) element, bld.with, bld.generateAdapter));
|
||||
}
|
||||
|
@ -154,19 +153,13 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
}
|
||||
|
||||
private static void generateDelegateToAdapter(TypeSpec.Builder spec, TypeName classType, TypeMirror adapter) {
|
||||
TypeName adapterType = TypeName.get(adapter);
|
||||
spec.addField(
|
||||
FieldSpec.builder(adapterType, "ADAPTER", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
|
||||
.initializer("new $T()", adapterType)
|
||||
.build()
|
||||
);
|
||||
spec.addMethod(
|
||||
MethodSpec.methodBuilder("read")
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||
.addParameter(Cl.GSON_READER, "reader")
|
||||
.addException(IOException.class)
|
||||
.returns(classType)
|
||||
.addCode("return ADAPTER.read(reader);")
|
||||
.addCode("return $T.read(reader);", adapter)
|
||||
.build()
|
||||
);
|
||||
spec.addMethod(
|
||||
|
@ -176,7 +169,7 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
.addParameter(classType, "value")
|
||||
.addException(IOException.class)
|
||||
.returns(classType)
|
||||
.addCode("ADAPTER.write(reader, value);")
|
||||
.addCode("$T.write(reader, value);", adapter)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
@ -264,7 +257,7 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
}
|
||||
|
||||
private void generateSerialisation(TypeSpec.Builder spec, TypeName classType, TypeElement classElement, List<TypeVariableName> typeVariables, Set<SerializableClass> otherAdapters) throws ElementException {
|
||||
Value value = valueCreator.from(classElement);
|
||||
Value value = valueCreator.from(classElement, false);
|
||||
ConstructionSource constructionSource = value.getConstructionSource();
|
||||
Properties properties = value.getProperties();
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package io.gitlab.jfronny.gson.compile.processor.util.valueprocessor;
|
||||
|
||||
import io.gitlab.jfronny.gson.compile.annotations.GPrefer;
|
||||
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.*;
|
||||
import javax.lang.model.util.ElementFilter;
|
||||
|
@ -12,10 +14,6 @@ public class ValueCreator {
|
|||
this.env = env;
|
||||
}
|
||||
|
||||
public Value from(Element element) throws ElementException {
|
||||
return from(element, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [Value] from the given element. This element can be the [TypeElement] of the target class, or a
|
||||
* specific constructor or factory method. If [isBuilder] is true, then the element represents the builder class,
|
||||
|
@ -133,17 +131,23 @@ public class ValueCreator {
|
|||
constructors.add(method);
|
||||
}
|
||||
}
|
||||
if (constructors.isEmpty() && noArgConstructor != null) {
|
||||
constructors.add(noArgConstructor);
|
||||
if (constructors.isEmpty()) {
|
||||
if (noArgConstructor != null) return noArgConstructor;
|
||||
else throw new ElementException("Lacking constructor or factory method", klazz);
|
||||
}
|
||||
if (constructors.size() == 1) {
|
||||
return constructors.get(0);
|
||||
} else {
|
||||
List<ElementException.Message> messages = new ArrayList<>();
|
||||
messages.add(new ElementException.Message("More than one constructor or factory method found.", klazz));
|
||||
constructors.stream().map(s -> new ElementException.Message(" " + s, s)).forEach(messages::add);
|
||||
throw new ElementException(messages);
|
||||
if (constructors.size() == 1) return constructors.get(0);
|
||||
if (noArgConstructor != null) constructors.add(noArgConstructor);
|
||||
List<ExecutableElement> preferred = new ArrayList<>();
|
||||
for (ExecutableElement constructor : constructors) {
|
||||
if (constructor.getAnnotationsByType(GPrefer.class).length != 0) {
|
||||
preferred.add(constructor);
|
||||
}
|
||||
}
|
||||
if (preferred.size() == 1) return preferred.get(0);
|
||||
List<ElementException.Message> messages = new ArrayList<>();
|
||||
messages.add(new ElementException.Message("More than one constructor or factory method found.", klazz));
|
||||
constructors.stream().map(s -> new ElementException.Message(" " + s, s)).forEach(messages::add);
|
||||
throw new ElementException(messages);
|
||||
}
|
||||
|
||||
private static void checkKind(Element element, ElementKind kind) {
|
||||
|
|
Loading…
Reference in New Issue