some cleanup and added javadocs, upgrade to junit5

This commit is contained in:
Sander Hautvast 2020-08-05 11:53:14 +02:00
parent a8a364b557
commit c650834d3b
5 changed files with 60 additions and 49 deletions

View file

@ -7,12 +7,15 @@ import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Set; import java.util.Set;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Responsible for generating classes that parse into java beans.
* <p>
* TODO remove javassist both for analysing java beans and for generating bytecode
*/
public class JavaObjectReaderFactory { public class JavaObjectReaderFactory {
public static final String ROOT_PACKAGE = "serializer."; public static final String ROOT_PACKAGE = "serializer.";
private final static CtClass DESERIALIZER_BASE = Javassist.getTypeDefinition(JsonValueReader.class); private final static CtClass DESERIALIZER_BASE = Javassist.getTypeDefinition(JsonValueReader.class);
@ -21,13 +24,6 @@ public class JavaObjectReaderFactory {
private static final Class<?>[] NO_PARAMS = {}; private static final Class<?>[] NO_PARAMS = {};
private static final CtClass[] CT_NO_PARAMS = {}; private static final CtClass[] CT_NO_PARAMS = {};
private final static CtClass OBJECT_CLASS = Javassist.getTypeDefinition(Object.class); private final static CtClass OBJECT_CLASS = Javassist.getTypeDefinition(Object.class);
private final static CtClass BOOLEAN = Javassist.getTypeDefinition(boolean.class);
private final static CtClass INT = Javassist.getTypeDefinition(int.class);
private final static CtClass LONG = Javassist.getTypeDefinition(long.class);
private final static CtClass BYTE = Javassist.getTypeDefinition(byte.class);
private final static CtClass SHORT = Javassist.getTypeDefinition(short.class);
private final static CtClass FLOAT = Javassist.getTypeDefinition(float.class);
private final static CtClass DOUBLE = Javassist.getTypeDefinition(double.class);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
static <T> JsonValueReader<T> createReaderInstance(Class<T> type) { static <T> JsonValueReader<T> createReaderInstance(Class<T> type) {
@ -56,7 +52,7 @@ public class JavaObjectReaderFactory {
private static CtMethod createReadJsonMethod(CtClass serializerClass, Class<?> type) { private static CtMethod createReadJsonMethod(CtClass serializerClass, Class<?> type) {
try { try {
String readMethodBodySource = createReadMethodBodySource(type); String readMethodBodySource = createReadMethodBodySource(type);
System.out.println(readMethodBodySource); // System.out.println(readMethodBodySource);
return CtNewMethod.make(Modifier.PUBLIC, OBJECT_CLASS, "read", PARSER_PARAM, NO_EXCEPTIONS, readMethodBodySource, serializerClass); return CtNewMethod.make(Modifier.PUBLIC, OBJECT_CLASS, "read", PARSER_PARAM, NO_EXCEPTIONS, readMethodBodySource, serializerClass);
} catch (CannotCompileException e) { } catch (CannotCompileException e) {
throw new ClassCreationException(e); throw new ClassCreationException(e);
@ -110,7 +106,7 @@ public class JavaObjectReaderFactory {
} }
} }
//should be reinstated
private static String genericType(CtField field) { private static String genericType(CtField field) {
try { try {
if (!Javassist.isCollection(field.getType())) { if (!Javassist.isCollection(field.getType())) {
@ -138,10 +134,6 @@ public class JavaObjectReaderFactory {
return lowercase.substring(0, 1).toUpperCase() + lowercase.substring(1); return lowercase.substring(0, 1).toUpperCase() + lowercase.substring(1);
} }
private static Set<String> createTypeList(Class<?>... classes) {
return Arrays.stream(classes).map(Class::getName).collect(Collectors.toSet());
}
/* /*
* custom root package is prepended to avoid the java.lang class in which it's illegal to create new classes * custom root package is prepended to avoid the java.lang class in which it's illegal to create new classes
* *

View file

@ -1,21 +1,16 @@
package nl.sander.jsontoy2; package nl.sander.jsontoy2;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.InputStream; import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.nio.charset.StandardCharsets;
import java.util.Map; import java.util.Map;
import java.util.Objects;
/** /**
* public api * public api
*/ */
public class JsonReader { public class JsonReader {
private JsonReader() {
private final static ThreadLocal<SoftReference<Parser>> PARSERS = new ThreadLocal<>(); }
/** /**
* reads a value from a stream for a type that is not known beforehand * reads a value from a stream for a type that is not known beforehand
@ -32,7 +27,7 @@ public class JsonReader {
*/ */
public static Object read(InputStream inputStream) { public static Object read(InputStream inputStream) {
final InputStream in = ensureBufferedStream(inputStream); final InputStream in = ensureBufferedStream(inputStream);
try (Parser parser = getParser(in)) { try (Parser parser = ParserFactory.getParser(in)) {
return read(parser); return read(parser);
} }
} }
@ -44,7 +39,7 @@ public class JsonReader {
* @return @see read(InputStream stream) * @return @see read(InputStream stream)
*/ */
public static Object read(String jsonString) { public static Object read(String jsonString) {
return read(getParser(jsonString)); return read(ParserFactory.getParser(jsonString));
} }
/** /**
@ -56,7 +51,7 @@ public class JsonReader {
* @return Object the specified type * @return Object the specified type
*/ */
public static <T> T read(Class<T> type, InputStream inputStream) { public static <T> T read(Class<T> type, InputStream inputStream) {
Parser parser = getParser(inputStream); Parser parser = ParserFactory.getParser(inputStream);
T value = read(type, parser); T value = read(type, parser);
parser.close(); parser.close();
return value; return value;
@ -71,31 +66,15 @@ public class JsonReader {
* @return Object the specified type * @return Object the specified type
*/ */
public static <T> T read(Class<T> type, String jsonString) { public static <T> T read(Class<T> type, String jsonString) {
return read(type, getParser(jsonString)); return read(type, ParserFactory.getParser(jsonString));
} }
private static Parser getParser(String jsonString) {
return getParser(new ByteArrayInputStream(jsonString.getBytes(StandardCharsets.UTF_8)));
}
private static Parser getParser(InputStream inputStream) {
Objects.requireNonNull(inputStream, "File not found");
Parser parser;
SoftReference<Parser> parserReference = PARSERS.get();
if (parserReference == null || (parser = parserReference.get()) == null) {
parser = new Parser(inputStream);
parserReference = new SoftReference<>(parser);
PARSERS.set(parserReference);
} else {
parser.init(inputStream);
}
return parser;
}
static Object read(Parser parser) { static Object read(Parser parser) {
return parser.parseAny(); return parser.parseAny();
} }
//TODO should not be public
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T> T read(Class<T> type, Parser parser) { public static <T> T read(Class<T> type, Parser parser) {
return (T) ReaderFactory.getReader(type).read(parser); return (T) ReaderFactory.getReader(type).read(parser);

View file

@ -0,0 +1,38 @@
package nl.sander.jsontoy2;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
/**
* Keeps a Threadlocal parser that can be (re)used by clients.
* <p>
* The parser is not kept indefinitely, but garbage collected when the JVM considers necessary.
*/
public class ParserFactory {
private final static ThreadLocal<SoftReference<Parser>> PARSERS = new ThreadLocal<>();
private ParserFactory() {
}
static Parser getParser(String jsonString) {
return getParser(new ByteArrayInputStream(jsonString.getBytes(StandardCharsets.UTF_8)));
}
static Parser getParser(InputStream inputStream) {
Objects.requireNonNull(inputStream, "File not found");
Parser parser;
SoftReference<Parser> parserReference = PARSERS.get();
if (parserReference == null || (parser = parserReference.get()) == null) {
parser = new Parser(inputStream);
parserReference = new SoftReference<>(parser);
PARSERS.set(parserReference);
} else {
parser.init(inputStream);
}
return parser;
}
}

View file

@ -18,7 +18,10 @@ public class ReaderFactory {
registerPrimitiveTypeReaders(); registerPrimitiveTypeReaders();
} }
public <T> void registerCustomReader(Class<T> type, JsonValueReader<T> reader) { private ReaderFactory() {
}
public static <T> void registerCustomReader(Class<T> type, JsonValueReader<T> reader) {
readers.put(type, reader); readers.put(type, reader);
} }

View file

@ -6,8 +6,7 @@ import org.junit.jupiter.api.Test;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertFalse;
public class WrapperObjectTests { public class WrapperObjectTests {
@ -22,9 +21,9 @@ public class WrapperObjectTests {
@Test @Test
public void testBoolean() { public void testBoolean() {
// BooleanBean trueBean = JsonReader.read(BooleanBean.class, "{\"value\": true}"); BooleanBean trueBean = JsonReader.read(BooleanBean.class, "{\"value\": true}");
// assertTrue(trueBean.isValue()); assertTrue(trueBean.isValue());
// second call to read, must not recreate class definition, would not compile // second call to read, must not recreate class definition, (it would not compile)
// so this test implicitly tests caching function too // so this test implicitly tests caching function too
BooleanBean falseBean = JsonReader.read(BooleanBean.class, "{\"value2\": false}"); BooleanBean falseBean = JsonReader.read(BooleanBean.class, "{\"value2\": false}");
assertFalse(falseBean.isValue()); assertFalse(falseBean.isValue());