[weaver-pojo] Introduce pojo weaver post processors
This commit is contained in:
6
tweed5-weaver-pojo-serde-extension/build.gradle.kts
Normal file
6
tweed5-weaver-pojo-serde-extension/build.gradle.kts
Normal file
@@ -0,0 +1,6 @@
|
||||
dependencies {
|
||||
api(project(":tweed5-weaver-pojo"))
|
||||
api(project(":tweed5-serde-extension"))
|
||||
|
||||
testImplementation(project(":tweed5-serde-hjson"))
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package de.siphalor.tweed5.weaver.pojoext.serde.api;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* {@code <spec> = <id> [ "(" <spec> ( "," <spec> )* ")" ] }
|
||||
*/
|
||||
@Target({ElementType.FIELD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface EntryReadWriteConfig {
|
||||
String value() default "";
|
||||
String writer() default "";
|
||||
String reader() default "";
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
package de.siphalor.tweed5.weaver.pojoext.serde.api;
|
||||
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.data.extension.api.*;
|
||||
import de.siphalor.tweed5.data.extension.impl.TweedEntryReaderWriterImpls;
|
||||
import de.siphalor.tweed5.weaver.pojo.api.weaving.WeavingContext;
|
||||
import de.siphalor.tweed5.weaver.pojo.api.weaving.postprocess.TweedPojoWeavingPostProcessor;
|
||||
import de.siphalor.tweed5.weaver.pojoext.serde.impl.SerdePojoReaderWriterSpec;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.*;
|
||||
|
||||
@Slf4j
|
||||
public class ReadWritePojoPostProcessor implements TweedPojoWeavingPostProcessor {
|
||||
private final Map<String, TweedReaderWriterProvider.ReaderWriterFactory<TweedEntryReader<?, ?>>> readerFactories = new HashMap<>();
|
||||
private final Map<String, TweedReaderWriterProvider.ReaderWriterFactory<TweedEntryWriter<?, ?>>> writerFactories = new HashMap<>();
|
||||
|
||||
public ReadWritePojoPostProcessor() {
|
||||
loadProviders();
|
||||
}
|
||||
|
||||
private void loadProviders() {
|
||||
ServiceLoader<TweedReaderWriterProvider> serviceLoader = ServiceLoader.load(TweedReaderWriterProvider.class);
|
||||
|
||||
for (TweedReaderWriterProvider readerWriterProvider : serviceLoader) {
|
||||
TweedReaderWriterProvider.ProviderContext providerContext = new TweedReaderWriterProvider.ProviderContext() {
|
||||
@Override
|
||||
public void registerReaderFactory(
|
||||
String id,
|
||||
TweedReaderWriterProvider.ReaderWriterFactory<TweedEntryReader<?, ?>> readerFactory
|
||||
) {
|
||||
if (readerFactories.putIfAbsent(id, readerFactory) != null) {
|
||||
log.warn(
|
||||
"Found duplicate Tweed entry reader id \"{}\" in provider class {}",
|
||||
id,
|
||||
readerWriterProvider.getClass().getName()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerWriterFactory(
|
||||
String id,
|
||||
TweedReaderWriterProvider.ReaderWriterFactory<TweedEntryWriter<?, ?>> writerFactory
|
||||
) {
|
||||
if (writerFactories.putIfAbsent(id, writerFactory) != null) {
|
||||
log.warn(
|
||||
"Found duplicate Tweed entry writer id \"{}\" in provider class {}",
|
||||
id,
|
||||
readerWriterProvider.getClass().getName()
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
readerWriterProvider.provideReaderWriters(providerContext);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(ConfigEntry<?> configEntry, WeavingContext context) {
|
||||
EntryReadWriteConfig entryConfig = context.annotations().getAnnotation(EntryReadWriteConfig.class);
|
||||
if (entryConfig == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ReadWriteExtension readWriteExtension = context.configContainer().extension(ReadWriteExtension.class);
|
||||
if (readWriteExtension == null) {
|
||||
log.error("You must not use {} without the {}", this.getClass().getSimpleName(), ReadWriteExtension.class.getSimpleName());
|
||||
return;
|
||||
}
|
||||
|
||||
readWriteExtension.setEntryReaderWriterDefinition(configEntry, createDefinitionFromEntryConfig(entryConfig, context));
|
||||
}
|
||||
|
||||
private EntryReaderWriterDefinition createDefinitionFromEntryConfig(EntryReadWriteConfig entryConfig, WeavingContext context) {
|
||||
String readerSpecText = entryConfig.reader().isEmpty() ? entryConfig.value() : entryConfig.reader();
|
||||
String writerSpecText = entryConfig.writer().isEmpty() ? entryConfig.value() : entryConfig.writer();
|
||||
|
||||
SerdePojoReaderWriterSpec readerSpec;
|
||||
SerdePojoReaderWriterSpec writerSpec;
|
||||
if (readerSpecText.equals(writerSpecText)) {
|
||||
readerSpec = writerSpec = specFromText(readerSpecText, context);
|
||||
} else {
|
||||
readerSpec = specFromText(readerSpecText, context);
|
||||
writerSpec = specFromText(writerSpecText, context);
|
||||
}
|
||||
|
||||
//noinspection unchecked
|
||||
TweedEntryReader<?, ?> reader = readerSpec == null
|
||||
? TweedEntryReaderWriterImpls.NOOP_READER_WRITER
|
||||
: resolveReaderWriterFromSpec((Class<TweedEntryReader<?, ?>>)(Object) TweedEntryReader.class, readerFactories, readerSpec, context);
|
||||
//noinspection unchecked
|
||||
TweedEntryWriter<?, ?> writer = writerSpec == null
|
||||
? TweedEntryReaderWriterImpls.NOOP_READER_WRITER
|
||||
: resolveReaderWriterFromSpec((Class<TweedEntryWriter<?, ?>>)(Object) TweedEntryWriter.class, writerFactories, writerSpec, context);
|
||||
|
||||
return new EntryReaderWriterDefinition() {
|
||||
@Override
|
||||
public TweedEntryReader<?, ?> reader() {
|
||||
return reader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TweedEntryWriter<?, ?> writer() {
|
||||
return writer;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private SerdePojoReaderWriterSpec specFromText(String specText, WeavingContext context) {
|
||||
if (specText.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return SerdePojoReaderWriterSpec.parse(specText);
|
||||
} catch (SerdePojoReaderWriterSpec.ParseException e) {
|
||||
log.warn(
|
||||
"Failed to parse definition for reader or writer on entry {}, entry will not be included in serde",
|
||||
context.path(),
|
||||
e
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private <T> T resolveReaderWriterFromSpec(
|
||||
Class<T> baseClass,
|
||||
Map<String, TweedReaderWriterProvider.ReaderWriterFactory<T>> factories,
|
||||
SerdePojoReaderWriterSpec spec,
|
||||
WeavingContext context
|
||||
) {
|
||||
//noinspection unchecked
|
||||
T[] arguments = spec.arguments()
|
||||
.stream()
|
||||
.map(argSpec -> resolveReaderWriterFromSpec(baseClass, factories, argSpec, context))
|
||||
.toArray(length -> (T[]) Array.newInstance(baseClass, length));
|
||||
|
||||
TweedReaderWriterProvider.ReaderWriterFactory<T> factory = factories.get(spec.identifier());
|
||||
|
||||
T instance;
|
||||
if (factory != null) {
|
||||
instance = factory.create(arguments);
|
||||
} else {
|
||||
instance = loadClassIfExists(baseClass, spec.identifier(), arguments);
|
||||
}
|
||||
|
||||
if (instance == null) {
|
||||
log.warn(
|
||||
"Failed to resolve reader or writer factory \"{}\" for entry {}, entry will not be included in serde",
|
||||
spec.identifier(),
|
||||
context.path()
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
private <T> T loadClassIfExists(Class<T> baseClass, String className, T[] arguments) {
|
||||
try {
|
||||
Class<?> clazz = Class.forName(className);
|
||||
Class<?>[] argClassses = new Class<?>[arguments.length];
|
||||
Arrays.fill(argClassses, baseClass);
|
||||
|
||||
Constructor<?> constructor = clazz.getConstructor(argClassses);
|
||||
|
||||
//noinspection unchecked
|
||||
return (T) constructor.newInstance((Object[]) arguments);
|
||||
} catch (ClassNotFoundException e) {
|
||||
return null;
|
||||
} catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
|
||||
log.warn("Failed to instantiate class {}", className, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
package de.siphalor.tweed5.weaver.pojoext.serde.impl;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.PrimitiveIterator;
|
||||
|
||||
@Value
|
||||
public class SerdePojoReaderWriterSpec {
|
||||
String identifier;
|
||||
List<SerdePojoReaderWriterSpec> arguments;
|
||||
|
||||
public static SerdePojoReaderWriterSpec parse(String input) throws ParseException {
|
||||
Lexer lexer = new Lexer(input.codePoints().iterator());
|
||||
SerdePojoReaderWriterSpec spec = parseSpec(lexer);
|
||||
lexer.chompWhitespace();
|
||||
int codePoint = lexer.nextCodePoint();
|
||||
if (codePoint != -1) {
|
||||
throw lexer.createException("Found trailing text after spec", codePoint);
|
||||
}
|
||||
return spec;
|
||||
}
|
||||
|
||||
private static SerdePojoReaderWriterSpec parseSpec(Lexer lexer) throws ParseException {
|
||||
lexer.chompWhitespace();
|
||||
String identifier = lexer.nextIdentifier();
|
||||
lexer.chompWhitespace();
|
||||
int codePoint = lexer.peekCodePoint();
|
||||
if (codePoint == '(') {
|
||||
lexer.nextCodePoint();
|
||||
lexer.chompWhitespace();
|
||||
if (lexer.peekCodePoint() == ')') {
|
||||
lexer.nextCodePoint();
|
||||
return new SerdePojoReaderWriterSpec(identifier, Collections.emptyList());
|
||||
}
|
||||
SerdePojoReaderWriterSpec spec = new SerdePojoReaderWriterSpec(identifier, parseSpecList(lexer));
|
||||
codePoint = lexer.nextCodePoint();
|
||||
if (codePoint != ')') {
|
||||
throw lexer.createException("Argument list must be ended with a closing parenthesis", codePoint);
|
||||
}
|
||||
return spec;
|
||||
} else {
|
||||
return new SerdePojoReaderWriterSpec(identifier, Collections.emptyList());
|
||||
}
|
||||
}
|
||||
|
||||
private static List<SerdePojoReaderWriterSpec> parseSpecList(Lexer lexer) throws ParseException {
|
||||
List<SerdePojoReaderWriterSpec> specs = new ArrayList<>();
|
||||
while (true) {
|
||||
specs.add(parseSpec(lexer));
|
||||
lexer.chompWhitespace();
|
||||
int codePoint = lexer.peekCodePoint();
|
||||
if (codePoint != ',') {
|
||||
break;
|
||||
}
|
||||
lexer.nextCodePoint();
|
||||
}
|
||||
return Collections.unmodifiableList(specs);
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
private static class Lexer {
|
||||
private static final int EMPTY = -2;
|
||||
private final PrimitiveIterator.OfInt codePointIterator;
|
||||
private int peek = EMPTY;
|
||||
private int index;
|
||||
|
||||
public String nextIdentifier() throws ParseException {
|
||||
int codePoint = nextCodePoint();
|
||||
if (codePoint == -1) {
|
||||
throw createException("Expected identifier, got end of input", codePoint);
|
||||
} else if (!isIdentifierChar(codePoint)) {
|
||||
throw createException("Expected identifier (alphanumeric character)", codePoint);
|
||||
}
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.appendCodePoint(codePoint);
|
||||
boolean dot = false;
|
||||
while ((codePoint = peekCodePoint()) >= 0) {
|
||||
if (isIdentifierChar(codePoint)) {
|
||||
stringBuilder.appendCodePoint(nextCodePoint());
|
||||
dot = false;
|
||||
} else if (codePoint == '.') {
|
||||
if (dot) {
|
||||
throw createException("Unexpected double dot in identifier", codePoint);
|
||||
} else {
|
||||
stringBuilder.appendCodePoint(nextCodePoint());
|
||||
dot = true;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (dot) {
|
||||
throw createException("Identifier must not end with dot", codePoint);
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
private boolean isIdentifierChar(int codePoint) {
|
||||
return (codePoint >= '0' && codePoint <= '9')
|
||||
|| (codePoint >= 'a' && codePoint <= 'z')
|
||||
|| (codePoint >= 'A' && codePoint <= 'Z');
|
||||
}
|
||||
|
||||
public void chompWhitespace() {
|
||||
while (Character.isWhitespace(peekCodePoint())) {
|
||||
nextCodePoint();
|
||||
}
|
||||
}
|
||||
|
||||
private int peekCodePoint() {
|
||||
if (peek == EMPTY) {
|
||||
peek = nextCodePoint();
|
||||
}
|
||||
return peek;
|
||||
}
|
||||
|
||||
private int nextCodePoint() {
|
||||
if (peek != EMPTY) {
|
||||
int codePoint = peek;
|
||||
peek = EMPTY;
|
||||
return codePoint;
|
||||
}
|
||||
if (codePointIterator.hasNext()) {
|
||||
index++;
|
||||
return codePointIterator.nextInt();
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public ParseException createException(String message, int codePoint) {
|
||||
return new ParseException(message, index, codePoint);
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public static class ParseException extends Exception {
|
||||
private final int index;
|
||||
private final int codePoint;
|
||||
|
||||
public ParseException(String message, int index, int codePoint) {
|
||||
super(message);
|
||||
this.index = index;
|
||||
this.codePoint = codePoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
String message = super.getMessage();
|
||||
StringBuilder stringBuilder = new StringBuilder(30 + message.length())
|
||||
.append("Parse error at index ")
|
||||
.append(index)
|
||||
.append(" \"");
|
||||
if (codePoint == -1) {
|
||||
stringBuilder.append("EOF");
|
||||
} else {
|
||||
stringBuilder.appendCodePoint(codePoint);
|
||||
}
|
||||
return stringBuilder
|
||||
.append("\": ")
|
||||
.append(message)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package de.siphalor.tweed5.weaver.pojoext.serde;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.data.extension.api.*;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonLexer;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonReader;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonWriter;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataVisitor;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataWriteException;
|
||||
import de.siphalor.tweed5.weaver.pojo.api.annotation.CompoundWeaving;
|
||||
import de.siphalor.tweed5.weaver.pojo.api.annotation.PojoWeaving;
|
||||
import de.siphalor.tweed5.weaver.pojo.impl.weaving.TweedPojoWeaverBootstrapper;
|
||||
import de.siphalor.tweed5.weaver.pojoext.serde.api.EntryReadWriteConfig;
|
||||
import de.siphalor.tweed5.weaver.pojoext.serde.api.ReadWritePojoPostProcessor;
|
||||
import lombok.*;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class WeaverPojoSerdeExtensionTest {
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
void testAnnotated() {
|
||||
TweedPojoWeaverBootstrapper<AnnotatedConfig> weaverBootstrapper = TweedPojoWeaverBootstrapper.create(AnnotatedConfig.class);
|
||||
|
||||
ConfigContainer<AnnotatedConfig> configContainer = weaverBootstrapper.weave();
|
||||
configContainer.initialize();
|
||||
|
||||
ReadWriteExtension readWriteExtension = configContainer.extension(ReadWriteExtension.class);
|
||||
assertThat(readWriteExtension).isNotNull();
|
||||
|
||||
AnnotatedConfig config = new AnnotatedConfig(123, "test", new TestClass(987));
|
||||
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
HjsonWriter hjsonWriter = new HjsonWriter(stringWriter, new HjsonWriter.Options());
|
||||
readWriteExtension.write(hjsonWriter, config, configContainer.rootEntry(), readWriteExtension.createReadWriteContextExtensionsData());
|
||||
|
||||
assertThat(stringWriter).hasToString("{\n\tanInt: 123\n\ttext: test\n\ttest: my cool custom writer\n}\n");
|
||||
|
||||
HjsonReader reader = new HjsonReader(new HjsonLexer(new StringReader(
|
||||
"{\n\tanInt: 987\n\ttext: abdef\n\ttest: { inner: 29 }\n}"
|
||||
)));
|
||||
assertThat(readWriteExtension.read(
|
||||
reader,
|
||||
configContainer.rootEntry(),
|
||||
readWriteExtension.createReadWriteContextExtensionsData()
|
||||
)).isEqualTo(new AnnotatedConfig(987, "abdef", new TestClass(29)));
|
||||
}
|
||||
|
||||
@AutoService(TweedReaderWriterProvider.class)
|
||||
public static class TestWriterProvider implements TweedReaderWriterProvider {
|
||||
@Override
|
||||
public void provideReaderWriters(ProviderContext context) {
|
||||
context.registerWriterFactory("tweed5.test.dummy", delegates -> new TweedEntryWriter<Object, ConfigEntry<Object>>() {
|
||||
@Override
|
||||
public void write(
|
||||
TweedDataVisitor writer,
|
||||
Object value,
|
||||
ConfigEntry<Object> entry,
|
||||
TweedWriteContext context
|
||||
) throws TweedDataWriteException {
|
||||
writer.visitString("my cool custom writer");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@PojoWeaving(extensions = ReadWriteExtension.class, postProcessors = ReadWritePojoPostProcessor.class)
|
||||
@CompoundWeaving
|
||||
@EntryReadWriteConfig("tweed5.compound")
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode
|
||||
@ToString
|
||||
public static class AnnotatedConfig {
|
||||
@EntryReadWriteConfig("tweed5.integer")
|
||||
public int anInt;
|
||||
|
||||
@EntryReadWriteConfig("tweed5.nullable(tweed5.string)")
|
||||
public String text;
|
||||
|
||||
@EntryReadWriteConfig(writer = "tweed5.test.dummy", reader = "tweed5.compound")
|
||||
@CompoundWeaving
|
||||
public TestClass test;
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode
|
||||
@ToString
|
||||
public static class TestClass {
|
||||
@EntryReadWriteConfig("tweed5.integer")
|
||||
public int inner;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package de.siphalor.tweed5.weaver.pojoext.serde.impl;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.assertj.core.api.InstanceOfAssertFactories.type;
|
||||
|
||||
class SerdePojoReaderWriterSpecTest {
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource(ignoreLeadingAndTrailingWhitespace = false, value = {
|
||||
" abc ,abc",
|
||||
" abc() ,abc",
|
||||
" abc.123 ,abc.123",
|
||||
"abc.123 ( ) ,abc.123",
|
||||
"123.abc,123.abc",
|
||||
})
|
||||
@SneakyThrows
|
||||
void parseSimpleIdentifier(String input, String identifier) {
|
||||
SerdePojoReaderWriterSpec spec = SerdePojoReaderWriterSpec.parse(input);
|
||||
assertThat(spec.identifier()).isEqualTo(identifier);
|
||||
assertThat(spec.arguments()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
void parseNested() {
|
||||
SerdePojoReaderWriterSpec spec = SerdePojoReaderWriterSpec.parse("abc.def ( 12 ( def, ghi ( ) ), jkl ) ");
|
||||
assertThat(spec).isEqualTo(new SerdePojoReaderWriterSpec("abc.def", Arrays.asList(
|
||||
new SerdePojoReaderWriterSpec("12", Arrays.asList(
|
||||
new SerdePojoReaderWriterSpec("def", Collections.emptyList()),
|
||||
new SerdePojoReaderWriterSpec("ghi", Collections.emptyList())
|
||||
)),
|
||||
new SerdePojoReaderWriterSpec("jkl", Collections.emptyList())
|
||||
)));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource(ignoreLeadingAndTrailingWhitespace = false, nullValues = "EOF", delimiter = ';', value = {
|
||||
" abc def ;6;d",
|
||||
"abcäöüdef;4;ä",
|
||||
"abc.def(;8;EOF",
|
||||
"'';0;EOF",
|
||||
",;1;,",
|
||||
"abc(,);5;,",
|
||||
"abc..def;5;.",
|
||||
})
|
||||
@SneakyThrows
|
||||
void parseError(String input, int index, String codePoint) {
|
||||
assertThatThrownBy(() -> SerdePojoReaderWriterSpec.parse(input))
|
||||
.asInstanceOf(type(SerdePojoReaderWriterSpec.ParseException.class))
|
||||
.isInstanceOf(SerdePojoReaderWriterSpec.ParseException.class)
|
||||
.satisfies(
|
||||
exception -> assertThat(exception.index()).as("index of: " + exception.getMessage()).isEqualTo(index),
|
||||
exception -> assertThat(exception.codePoint()).as("code point of: " + exception.getMessage())
|
||||
.isEqualTo(codePoint == null ? -1 : codePoint.codePointAt(0))
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user