Experiment with manifold extension methods
ci/woodpecker/push/woodpecker Pipeline was successful
Details
ci/woodpecker/push/woodpecker Pipeline was successful
Details
This commit is contained in:
parent
3ba71463bc
commit
b46ccd5574
|
@ -0,0 +1,23 @@
|
|||
plugins {
|
||||
java
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven("https://maven.frohnmeyer-wds.de/artifacts")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
annotationProcessor(project(":gson-compile-processor"))
|
||||
compileOnly(project(":gson-compile-annotations"))
|
||||
implementation(project(":gson-compile-core"))
|
||||
|
||||
val manifoldVersion = "2022.1.26"
|
||||
|
||||
implementation("systems.manifold:manifold-ext-rt:$manifoldVersion")
|
||||
annotationProcessor("systems.manifold:manifold-ext:$manifoldVersion")
|
||||
}
|
||||
|
||||
tasks.withType<JavaCompile> {
|
||||
options.compilerArgs.addAll(arrayOf("-AgsonCompileNoReflect", "-Xplugin:Manifold no-bootstrap"))
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package io.gitlab.jfronny.gson;
|
||||
|
||||
import io.gitlab.jfronny.gson.compile.annotations.GSerializable;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Hello world!");
|
||||
ExampleRecord.read("");
|
||||
}
|
||||
|
||||
@GSerializable
|
||||
public record ExampleRecord(String yes) {
|
||||
}
|
||||
}
|
|
@ -19,4 +19,6 @@ public class Cl {
|
|||
public static final ClassName GCOMMENT = ClassName.get("io.gitlab.jfronny.gson.compile.annotations", "GComment");
|
||||
public static final ClassName GISO8601UTILS = ClassName.get("io.gitlab.jfronny.gson.internal.bind.util", "ISO8601Utils");
|
||||
public static final ClassName CCORE = ClassName.get("io.gitlab.jfronny.gson.compile.core", "CCore");
|
||||
|
||||
public static final ClassName MANIFOLD_EXTENSION = ClassName.get("manifold.ext.rt.api", "Extension");
|
||||
}
|
||||
|
|
|
@ -19,7 +19,8 @@ import javax.tools.Diagnostic;
|
|||
import java.io.*;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.UnaryOperator;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@SupportedSourceVersion(SourceVersion.RELEASE_17)
|
||||
@SupportedAnnotationTypes2({GSerializable.class})
|
||||
|
@ -27,17 +28,25 @@ import java.util.function.Function;
|
|||
public class GsonCompileProcessor extends AbstractProcessor2 {
|
||||
private Messager message;
|
||||
private Filer filer;
|
||||
private Set<ClassName> seen;
|
||||
private Map<ClassName, TypeSpec.Builder> seen;
|
||||
private ValueCreator valueCreator;
|
||||
private Elements elements;
|
||||
private boolean hasManifold = false;
|
||||
|
||||
@Override
|
||||
public synchronized void init(ProcessingEnvironment processingEnv) {
|
||||
super.init(processingEnv);
|
||||
try {
|
||||
Class.forName("manifold.ext.Model");
|
||||
System.out.println("Detected manifold!");
|
||||
hasManifold = true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
hasManifold = false;
|
||||
}
|
||||
message = processingEnv.getMessager();
|
||||
filer = processingEnv.getFiler();
|
||||
elements = processingEnv.getElementUtils();
|
||||
seen = new LinkedHashSet<>();
|
||||
seen = new LinkedHashMap<>();
|
||||
valueCreator = new ValueCreator(processingEnv);
|
||||
for (Adapter adapter : Adapters.ADAPTERS) {
|
||||
adapter.init(processingEnv);
|
||||
|
@ -86,7 +95,7 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
if (bld.configure == null) throw new IllegalArgumentException("Missing annotation parameter: configure");
|
||||
if (bld.generateAdapter == null) throw new IllegalArgumentException("Missing annotation parameter: generateAdapter");
|
||||
|
||||
toGenerate.add(SerializableClass.of((TypeElement) element, bld.with, bld.builder, bld.configure, bld.generateAdapter));
|
||||
toGenerate.add(SerializableClass.of((TypeElement) element, bld.with, bld.builder, bld.configure, bld.generateAdapter, hasManifold));
|
||||
}
|
||||
} catch (ElementException e) {
|
||||
e.printMessage(message);
|
||||
|
@ -100,18 +109,65 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
for (SerializableClass toProcess : toGenerate) {
|
||||
try {
|
||||
process(toProcess, toGenerate);
|
||||
} catch (IOException e) {
|
||||
message.printMessage(Diagnostic.Kind.ERROR, "GsonCompile threw an exception: " + StringFormatter.toString(e), toProcess.classElement());
|
||||
} catch (ElementException e) {
|
||||
e.printMessage(message);
|
||||
}
|
||||
}
|
||||
for (var entry : seen.keySet().stream().collect(Collectors.groupingBy(ClassName::packageName)).entrySet()) {
|
||||
Map<List<String>, TypeSpec.Builder> known = entry.getValue().stream().collect(Collectors.toMap(ClassName::simpleNames, seen::get));
|
||||
// Generate additional parent classes
|
||||
for (List<String> klazz : known.keySet().stream().toList()) {
|
||||
List<String> current = new LinkedList<>();
|
||||
for (String s : klazz) {
|
||||
current.add(s);
|
||||
TypeSpec.Builder builder = find(known, current);
|
||||
if (builder == null) {
|
||||
builder = TypeSpec.classBuilder(s).addModifiers(Modifier.PUBLIC);
|
||||
known.put(List.copyOf(current), builder);
|
||||
}
|
||||
if (current.size() > 1) builder.addModifiers(Modifier.STATIC);
|
||||
else if (hasManifold) builder.addAnnotation(Cl.MANIFOLD_EXTENSION);
|
||||
}
|
||||
}
|
||||
// Add to parent class
|
||||
for (var entry1 : known.entrySet()) {
|
||||
if (entry1.getKey().size() == 1) continue;
|
||||
find(known, entry1.getKey().subList(0, entry1.getKey().size() - 1)).addType(entry1.getValue().build());
|
||||
}
|
||||
// Print
|
||||
// System.out.println("Got " + known.size() + " classes");
|
||||
// for (var entry1 : known.entrySet()) {
|
||||
// System.out.println("Class " + entry.getKey() + '.' + String.join(".", entry1.getKey()));
|
||||
// for (TypeSpec typeSpec : entry1.getValue().typeSpecs) {
|
||||
// System.out.println("- " + typeSpec.name);
|
||||
// }
|
||||
// }
|
||||
// Write top-level classes
|
||||
for (var entry1 : known.entrySet()) {
|
||||
if (entry1.getKey().size() == 1) {
|
||||
JavaFile javaFile = JavaFile.builder(entry.getKey(), entry1.getValue().build())
|
||||
.skipJavaLangImports(true)
|
||||
.indent(" ")
|
||||
.build();
|
||||
try {
|
||||
javaFile.writeTo(filer);
|
||||
} catch (IOException e) {
|
||||
message.printMessage(Diagnostic.Kind.ERROR, "Could not write source: " + StringFormatter.toString(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
seen.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
private void process(SerializableClass toProcess, Set<SerializableClass> other) throws IOException, ElementException {
|
||||
ClassName className = toProcess.getClassName();
|
||||
if (!seen.add(className)) return; // Don't process the same class more than once
|
||||
private <K, V> V find(Map<List<K>, V> map, List<K> key) {
|
||||
for (var entry : map.entrySet()) if (entry.getKey().equals(key)) return entry.getValue();
|
||||
return null;
|
||||
}
|
||||
|
||||
private void process(SerializableClass toProcess, Set<SerializableClass> other) throws ElementException {
|
||||
if (seen.containsKey(toProcess.generatedClassName())) return; // Don't process the same class more than once
|
||||
|
||||
TypeName classType = toProcess.getTypeName();
|
||||
List<TypeVariableName> typeVariables = new ArrayList<>();
|
||||
|
@ -123,8 +179,10 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
|
||||
TypeSpec.Builder spec = TypeSpec.classBuilder(toProcess.generatedClassName().simpleName())
|
||||
.addOriginatingElement(toProcess.classElement())
|
||||
.addTypeVariables(typeVariables)
|
||||
.addModifiers(Modifier.PUBLIC);
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.addTypeVariables(typeVariables);
|
||||
|
||||
seen.put(toProcess.generatedClassName(), spec);
|
||||
|
||||
if (toProcess.adapter() != null) {
|
||||
generateDelegateToAdapter(spec, classType, toProcess.adapter());
|
||||
|
@ -136,13 +194,6 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
}
|
||||
|
||||
generateAuxiliary(spec, classType, toProcess.configure());
|
||||
|
||||
JavaFile javaFile = JavaFile.builder(className.packageName(), spec.build())
|
||||
.skipJavaLangImports(true)
|
||||
.indent(" ")
|
||||
.build();
|
||||
javaFile.writeTo(filer);
|
||||
message.printMessage(Diagnostic.Kind.NOTE, "Processed " + className);
|
||||
}
|
||||
|
||||
private static void generateDelegatingAdapter(TypeSpec.Builder spec, TypeName classType, ClassName generatedClassName) {
|
||||
|
@ -170,9 +221,9 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
);
|
||||
}
|
||||
|
||||
private static void generateDelegateToAdapter(TypeSpec.Builder spec, TypeName classType, TypeMirror adapter) {
|
||||
private void generateDelegateToAdapter(TypeSpec.Builder spec, TypeName classType, TypeMirror adapter) {
|
||||
spec.addMethod(
|
||||
MethodSpec.methodBuilder("read")
|
||||
extension(MethodSpec.methodBuilder("read"))
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||
.addParameter(Cl.GSON_READER, "reader")
|
||||
.addException(IOException.class)
|
||||
|
@ -181,7 +232,7 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
.build()
|
||||
);
|
||||
spec.addMethod(
|
||||
MethodSpec.methodBuilder("write")
|
||||
extension(MethodSpec.methodBuilder("write"))
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||
.addParameter(Cl.GSON_WRITER, "writer")
|
||||
.addParameter(classType, "value")
|
||||
|
@ -191,18 +242,18 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
);
|
||||
}
|
||||
|
||||
private static void generateAuxiliary(TypeSpec.Builder spec, TypeName classType, TypeMirror configure) {
|
||||
Function<CodeBlock.Builder, CodeBlock.Builder> configureReader = cb -> {
|
||||
private void generateAuxiliary(TypeSpec.Builder spec, TypeName classType, TypeMirror configure) {
|
||||
UnaryOperator<CodeBlock.Builder> configureReader = cb -> {
|
||||
if (configure != null) cb.addStatement("$T.configure(reader)", configure);
|
||||
return cb;
|
||||
};
|
||||
Function<CodeBlock.Builder, CodeBlock.Builder> configureWriter = cb -> {
|
||||
UnaryOperator<CodeBlock.Builder> configureWriter = cb -> {
|
||||
if (configure != null) cb.addStatement("$T.configure(writer)", configure);
|
||||
return cb;
|
||||
};
|
||||
|
||||
spec.addMethod(
|
||||
MethodSpec.methodBuilder("read")
|
||||
extension(MethodSpec.methodBuilder("read"))
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||
.addParameter(TypeName.get(Reader.class), "in")
|
||||
.addException(IOException.class)
|
||||
|
@ -215,7 +266,7 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
);
|
||||
|
||||
spec.addMethod(
|
||||
MethodSpec.methodBuilder("read")
|
||||
extension(MethodSpec.methodBuilder("read"))
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||
.addParameter(TypeName.get(String.class), "json")
|
||||
.addException(IOException.class)
|
||||
|
@ -228,7 +279,7 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
);
|
||||
|
||||
spec.addMethod(
|
||||
MethodSpec.methodBuilder("read")
|
||||
extension(MethodSpec.methodBuilder("read"))
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||
.addParameter(Cl.GSON_ELEMENT, "tree")
|
||||
.addException(IOException.class)
|
||||
|
@ -241,7 +292,7 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
);
|
||||
|
||||
spec.addMethod(
|
||||
MethodSpec.methodBuilder("read")
|
||||
extension(MethodSpec.methodBuilder("read"))
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||
.addParameter(Path.class, "path")
|
||||
.addException(IOException.class)
|
||||
|
@ -254,7 +305,7 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
);
|
||||
|
||||
spec.addMethod(
|
||||
MethodSpec.methodBuilder("write")
|
||||
extension(MethodSpec.methodBuilder("write"))
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||
.addParameter(Writer.class, "out")
|
||||
.addParameter(classType, "value")
|
||||
|
@ -267,7 +318,7 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
);
|
||||
|
||||
spec.addMethod(
|
||||
MethodSpec.methodBuilder("write")
|
||||
extension(MethodSpec.methodBuilder("write"))
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||
.addParameter(Path.class, "path")
|
||||
.addParameter(classType, "value")
|
||||
|
@ -280,7 +331,7 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
);
|
||||
|
||||
spec.addMethod(
|
||||
MethodSpec.methodBuilder("toJson")
|
||||
extension(MethodSpec.methodBuilder("toJson"))
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||
.addParameter(classType, "value")
|
||||
.addException(IOException.class)
|
||||
|
@ -294,7 +345,7 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
);
|
||||
|
||||
spec.addMethod(
|
||||
MethodSpec.methodBuilder("toJsonTree")
|
||||
extension(MethodSpec.methodBuilder("toJsonTree"))
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||
.addParameter(classType, "value")
|
||||
.addException(IOException.class)
|
||||
|
@ -359,7 +410,7 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
}
|
||||
code.addStatement("writer.endObject()");
|
||||
|
||||
spec.addMethod(MethodSpec.methodBuilder("write")
|
||||
spec.addMethod(extension(MethodSpec.methodBuilder("write"))
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||
.addParameter(Cl.GSON_WRITER, "writer")
|
||||
.addParameter(self.getTypeName(), "value")
|
||||
|
@ -450,7 +501,7 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
}
|
||||
code.addStatement("return result");
|
||||
|
||||
spec.addMethod(MethodSpec.methodBuilder("read")
|
||||
spec.addMethod(extension(MethodSpec.methodBuilder("read"))
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||
.returns(self.getTypeName())
|
||||
.addParameter(Cl.GSON_READER, "reader")
|
||||
|
@ -477,4 +528,9 @@ public class GsonCompileProcessor extends AbstractProcessor2 {
|
|||
}
|
||||
return property.getName();
|
||||
}
|
||||
|
||||
private MethodSpec.Builder extension(MethodSpec.Builder method) {
|
||||
if (hasManifold) method.addAnnotation(Cl.MANIFOLD_EXTENSION);
|
||||
return method;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,9 +9,10 @@ import javax.lang.model.element.TypeElement;
|
|||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
public record SerializableClass(TypeElement classElement, ClassName generatedClassName, @Nullable TypeMirror adapter, @Nullable TypeMirror builder, @Nullable TypeMirror configure, boolean generateAdapter) {
|
||||
public static SerializableClass of(TypeElement element, @Nullable TypeMirror with, @Nullable TypeMirror builder, @Nullable TypeMirror configure, boolean generateAdapter) throws ElementException {
|
||||
public static SerializableClass of(TypeElement element, @Nullable TypeMirror with, @Nullable TypeMirror builder, @Nullable TypeMirror configure, boolean generateAdapter, boolean manifold) throws ElementException {
|
||||
ClassName className = ClassName.get(element);
|
||||
ClassName generatedClassName = ClassName.get(className.packageName(), "GC_" + String.join("_", className.simpleNames()));
|
||||
String pkg = manifold ? "gsoncompile.extensions." + className.packageName() + '.' + className.simpleNames().get(0) : className.packageName();
|
||||
ClassName generatedClassName = ClassName.get(pkg, "GC_" + className.simpleNames().get(0), className.simpleNames().subList(1, className.simpleNames().size()).toArray(String[]::new));
|
||||
return new SerializableClass(element, generatedClassName, voidToNull(with), voidToNull(builder), voidToNull(configure), generateAdapter).validate();
|
||||
}
|
||||
|
||||
|
|
|
@ -13,3 +13,4 @@ include("gson-compile-core")
|
|||
include("gson-compile-processor")
|
||||
include("gson-compile-annotations")
|
||||
include("gson-compile-example")
|
||||
include("gson-compile-example-manifold")
|
||||
|
|
Loading…
Reference in New Issue