Skip to content

Commit

Permalink
Json support of non-final subtypes in sealed hierarchies (#117)
Browse files Browse the repository at this point in the history
  • Loading branch information
Squiry authored Apr 13, 2023
1 parent de7188e commit 523cd1e
Show file tree
Hide file tree
Showing 82 changed files with 2,630 additions and 1,918 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@
import ru.tinkoff.kora.common.naming.NameConverter;

import javax.annotation.Nullable;
import javax.annotation.processing.FilerException;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.*;
import javax.lang.model.type.*;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.StandardLocation;
import java.io.IOException;
import java.util.*;
import java.util.function.Predicate;
Expand Down Expand Up @@ -54,36 +52,11 @@ public static boolean isNullable(Element element) {
.anyMatch(CommonUtils::isNullable);
}

public static void safeWriteTo(ProcessingEnvironment processingEnv, JavaFile file) throws IOException {
var fullClassName = file.packageName.isEmpty()
? file.typeSpec.name
: file.packageName + "." + file.typeSpec.name;
if (!isClassExists(processingEnv, fullClassName)) {
file.writeTo(processingEnv.getFiler());
}
}

public static boolean isClassExists(ProcessingEnvironment processingEnv, String fullClassName) throws IOException {
var typeElement = processingEnv.getElementUtils().getTypeElement(fullClassName);

if (typeElement != null) {
return true;
}

var fileName = fullClassName.replace('.', '/') + ".java";

public static void safeWriteTo(ProcessingEnvironment processingEnv, JavaFile file) {
try {
processingEnv.getFiler().getResource(
StandardLocation.SOURCE_OUTPUT,
"",
fileName
);
return false;
} catch (FilerException e) {
if (e.getMessage().startsWith("Attempt to reopen a file for path")) {
return true;
}
throw e;
file.writeTo(processingEnv.getFiler());
} catch (IOException e) {
throw new RuntimeException(e);
}
}

Expand Down Expand Up @@ -207,6 +180,10 @@ public static List<ExecutableElement> findConstructors(TypeElement typeElement,

public static boolean hasDefaultConstructorAndFinal(Types types, TypeMirror typeMirror) {
var typeElement = (TypeElement) types.asElement(typeMirror);
return hasDefaultConstructorAndFinal(typeElement);
}

public static boolean hasDefaultConstructorAndFinal(TypeElement typeElement) {
if (!typeElement.getModifiers().contains(Modifier.FINAL)) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package ru.tinkoff.kora.annotation.processor.common;

import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

public class SealedTypeUtils {
public static List<TypeElement> collectFinalPermittedSubtypes(Types types, Elements elements, TypeElement jsonElement) {
var result = new ArrayList<TypeElement>();
var seen = new HashSet<String>();
var o = new Object() {
void visit(TypeElement element) {
if (element.getModifiers().contains(Modifier.SEALED)) {
for (var permittedSubclass : element.getPermittedSubclasses()) {
visit((TypeElement) types.asElement(permittedSubclass));
}
} else {
var packageElement = elements.getPackageOf(element).getQualifiedName().toString();
var name = element.getQualifiedName().toString();
var fullName = packageElement + "." + name;
if (seen.add(fullName)) {
result.add(element);
}
}
}
};
o.visit(jsonElement);
return result;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
import org.junit.jupiter.api.TestInstance;
import ru.tinkoff.kora.annotation.processor.common.compile.ByteArrayJavaFileObject;
import ru.tinkoff.kora.annotation.processor.common.compile.KoraCompileTestJavaFileManager;
import ru.tinkoff.kora.application.graph.*;

import javax.annotation.Nullable;
import javax.annotation.processing.Processor;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
Expand All @@ -21,6 +23,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.function.Supplier;

@TestInstance(TestInstance.Lifecycle.PER_METHOD)
public abstract class AbstractAnnotationProcessorTest {
Expand Down Expand Up @@ -82,11 +85,14 @@ protected CompileResult compile(List<Processor> processors, @Language("java") St
if (classStart < 19) {
classStart = s.indexOf("public interface ") + 17;
if (classStart < 17) {
classStart = s.indexOf("public record ") + 14;
if (classStart < 14) {
classStart = s.indexOf("public enum ") + 12;
if (classStart < 12) {
throw new IllegalArgumentException();
classStart = s.indexOf("public @interface ") + 18;
if (classStart < 18) {
classStart = s.indexOf("public record ") + 14;
if (classStart < 14) {
classStart = s.indexOf("public enum ") + 12;
if (classStart < 12) {
throw new IllegalArgumentException();
}
}
}
}
Expand Down Expand Up @@ -132,6 +138,22 @@ public Object newObject(String className, Object... params) {
}
}

public Object enumConstant(String className, String name) {
try {
var clazz = this.compileResult.loadClass(className);
assert clazz.isEnum();
for (var enumConstant : clazz.getEnumConstants()) {
var e = (Enum<?>) enumConstant;
if (e.name().equals(name)) {
return e;
}
}
throw new RuntimeException("Invalid enum constant: " + name);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

public GeneratedResultCallback<?> newGeneratedObject(String className, Object... params) {
return () -> newObject(className, params);
}
Expand All @@ -140,4 +162,69 @@ protected interface GeneratedResultCallback<T> {
T get();
}


public GraphContainer loadGraph(String appName) {
try {
var type = compileResult.loadClass(appName + "Graph");
var constructor = type.getConstructors()[0];
var supplier = (Supplier<ApplicationGraphDraw>) constructor.newInstance();
var draw = supplier.get();
return new GraphContainer(draw);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

public static class GraphContainer implements Graph {
private final ApplicationGraphDraw draw;
private final Graph graph;

public GraphContainer(ApplicationGraphDraw draw) {
this.draw = draw;
this.graph = draw.init().block();
}

@Nullable
public <T> T findByType(Class<? extends T> type) {
for (var node : draw.getNodes()) {
var object = graph.get(node);
if (type.isInstance(object)) {
return type.cast(object);
}
}
return null;
}

@Nullable
public <T> List<T> findAllByType(Class<? extends T> type) {
var result = new ArrayList<T>();
for (var node : draw.getNodes()) {
var object = graph.get(node);
if (type.isInstance(object)) {
result.add(type.cast(object));
}
}
return result;
}

@Override
public ApplicationGraphDraw draw() {
return graph.draw();
}

@Override
public <T> T get(Node<T> node) {
return graph.get(node);
}

@Override
public <T> ValueOf<T> valueOf(Node<? extends T> node) {
return graph.valueOf(node);
}

@Override
public <T> PromiseOf<T> promiseOf(Node<T> node) {
return graph.promiseOf(node);
}
}
}
1 change: 0 additions & 1 deletion common/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ dependencies {
api project(":application-graph")

api libs.slf4j.api
api libs.jackson.core

api libs.reactor.core
compileOnly(libs.kotlin.stdlib.lib)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import java.io.IOException;

public final class ConfigKoraExtension implements KoraExtension {
private final Elements elements;
Expand Down Expand Up @@ -73,11 +72,7 @@ public ExtensionResult generateDependency(RoundEnvironment roundEnvironment, Typ

var javaFile = this.configParserGenerator.generate(roundEnvironment, targetType);

try {
CommonUtils.safeWriteTo(this.processingEnv, javaFile);
} catch (IOException e) {
throw new RuntimeException(e);
}
CommonUtils.safeWriteTo(this.processingEnv, javaFile);

return ExtensionResult.RequiresCompilingResult.INSTANCE;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.TypeElement;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
Expand Down Expand Up @@ -72,8 +71,6 @@ public boolean process0(RoundEnvironment roundEnv) {
CommonUtils.safeWriteTo(this.processingEnv, module);
} catch (NewRoundWantedException e) {
previouslyUnprocessedElements.add(e.getElement());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import java.io.IOException;
import java.util.Set;

public class ConfigSourceAnnotationProcessor extends AbstractKoraProcessor {
Expand Down Expand Up @@ -56,11 +55,7 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment

var javaFile = JavaFile.builder(packageElement.getQualifiedName().toString(), type).build();

try {
CommonUtils.safeWriteTo(this.processingEnv, javaFile);
} catch (IOException e) {
throw new RuntimeException(e);
}
CommonUtils.safeWriteTo(this.processingEnv, javaFile);
}

return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Objects;

Expand Down Expand Up @@ -58,12 +57,8 @@ public void generateMapper(TypeMirror type) {
typeSpec.addMethod(apply.build());
typeSpec.addMethod(constructor.build());

try {
var javaFile = JavaFile.builder(packageName.getQualifiedName().toString(), typeSpec.build()).build();
CommonUtils.safeWriteTo(this.processingEnv, javaFile);
} catch (IOException e) {
throw new RuntimeException(e);
}
var javaFile = JavaFile.builder(packageName.getQualifiedName().toString(), typeSpec.build()).build();
CommonUtils.safeWriteTo(this.processingEnv, javaFile);
}

public void generateListMapper(TypeMirror type) {
Expand Down Expand Up @@ -99,12 +94,8 @@ public void generateListMapper(TypeMirror type) {
typeSpec.addMethod(apply.build());
typeSpec.addMethod(constructor.build());

try {
var javaFile = JavaFile.builder(packageName.getQualifiedName().toString(), typeSpec.build()).build();
CommonUtils.safeWriteTo(this.processingEnv, javaFile);
} catch (IOException e) {
throw new RuntimeException(e);
}
var javaFile = JavaFile.builder(packageName.getQualifiedName().toString(), typeSpec.build()).build();
CommonUtils.safeWriteTo(this.processingEnv, javaFile);
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Objects;

Expand Down Expand Up @@ -59,12 +58,9 @@ public void generateMapper(TypeMirror typeMirror) {

typeSpec.addMethod(apply.build());
typeSpec.addMethod(constructor.build());
try {
var javaFile = JavaFile.builder(packageName.getQualifiedName().toString(), typeSpec.build()).build();
CommonUtils.safeWriteTo(this.processingEnv, javaFile);
} catch (IOException e) {
throw new RuntimeException(e);
}

var javaFile = JavaFile.builder(packageName.getQualifiedName().toString(), typeSpec.build()).build();
CommonUtils.safeWriteTo(this.processingEnv, javaFile);
}

public void generateListMapper(TypeMirror typeMirror) {
Expand Down Expand Up @@ -99,12 +95,9 @@ public void generateListMapper(TypeMirror typeMirror) {

typeSpec.addMethod(apply.build());
typeSpec.addMethod(constructor.build());
try {
var javaFile = JavaFile.builder(packageName.getQualifiedName().toString(), typeSpec.build()).build();
CommonUtils.safeWriteTo(this.processingEnv, javaFile);
} catch (IOException e) {
throw new RuntimeException(e);
}

var javaFile = JavaFile.builder(packageName.getQualifiedName().toString(), typeSpec.build()).build();
CommonUtils.safeWriteTo(this.processingEnv, javaFile);
}

private void readIndexes(MethodSpec.Builder apply, DbEntity entity) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ class CassandraParametersTest : AbstractCassandraRepositoryTest() {
}
""".trimIndent(), """
public class UnknownType {}
class UnknownType {}
""".trimIndent())
repository.invoke<Any>("test", 42L, new("UnknownType"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ class JdbcParametersTest : AbstractJdbcRepositoryTest() {
}
""".trimIndent(), """
public class UnknownType {}
class UnknownType {}
""".trimIndent())

Expand Down
Loading

0 comments on commit 523cd1e

Please sign in to comment.