diff --git a/src/main/java/nl/sander/jsontoy2/JavaObjectReaderFactory.java b/src/main/java/nl/sander/jsontoy2/JavaObjectReaderFactory.java index 5559c64..cfbcc92 100644 --- a/src/main/java/nl/sander/jsontoy2/JavaObjectReaderFactory.java +++ b/src/main/java/nl/sander/jsontoy2/JavaObjectReaderFactory.java @@ -7,12 +7,15 @@ import org.jetbrains.annotations.NotNull; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; -import java.util.Arrays; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; +/** + * Responsible for generating classes that parse into java beans. + *

+ * TODO remove javassist both for analysing java beans and for generating bytecode + */ public class JavaObjectReaderFactory { public static final String ROOT_PACKAGE = "serializer."; 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 CtClass[] CT_NO_PARAMS = {}; 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") static JsonValueReader createReaderInstance(Class type) { @@ -56,7 +52,7 @@ public class JavaObjectReaderFactory { private static CtMethod createReadJsonMethod(CtClass serializerClass, Class type) { try { 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); } catch (CannotCompileException e) { throw new ClassCreationException(e); @@ -110,7 +106,7 @@ public class JavaObjectReaderFactory { } } - + //should be reinstated private static String genericType(CtField field) { try { if (!Javassist.isCollection(field.getType())) { @@ -138,10 +134,6 @@ public class JavaObjectReaderFactory { return lowercase.substring(0, 1).toUpperCase() + lowercase.substring(1); } - private static Set 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 * diff --git a/src/main/java/nl/sander/jsontoy2/JsonReader.java b/src/main/java/nl/sander/jsontoy2/JsonReader.java index ccd6e11..43cd2ad 100644 --- a/src/main/java/nl/sander/jsontoy2/JsonReader.java +++ b/src/main/java/nl/sander/jsontoy2/JsonReader.java @@ -1,21 +1,16 @@ package nl.sander.jsontoy2; import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; import java.io.InputStream; -import java.lang.ref.SoftReference; -import java.nio.charset.StandardCharsets; import java.util.Map; -import java.util.Objects; /** * public api */ public class JsonReader { - - private final static ThreadLocal> PARSERS = new ThreadLocal<>(); - + private JsonReader() { + } /** * 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) { final InputStream in = ensureBufferedStream(inputStream); - try (Parser parser = getParser(in)) { + try (Parser parser = ParserFactory.getParser(in)) { return read(parser); } } @@ -44,7 +39,7 @@ public class JsonReader { * @return @see read(InputStream stream) */ 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 */ public static T read(Class type, InputStream inputStream) { - Parser parser = getParser(inputStream); + Parser parser = ParserFactory.getParser(inputStream); T value = read(type, parser); parser.close(); return value; @@ -71,31 +66,15 @@ public class JsonReader { * @return Object the specified type */ public static T read(Class 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 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) { return parser.parseAny(); } + //TODO should not be public @SuppressWarnings("unchecked") public static T read(Class type, Parser parser) { return (T) ReaderFactory.getReader(type).read(parser); diff --git a/src/main/java/nl/sander/jsontoy2/ParserFactory.java b/src/main/java/nl/sander/jsontoy2/ParserFactory.java new file mode 100644 index 0000000..0520c1b --- /dev/null +++ b/src/main/java/nl/sander/jsontoy2/ParserFactory.java @@ -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. + *

+ * The parser is not kept indefinitely, but garbage collected when the JVM considers necessary. + */ +public class ParserFactory { + + private final static ThreadLocal> 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 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; + } +} diff --git a/src/main/java/nl/sander/jsontoy2/ReaderFactory.java b/src/main/java/nl/sander/jsontoy2/ReaderFactory.java index 29d2059..6b3a199 100644 --- a/src/main/java/nl/sander/jsontoy2/ReaderFactory.java +++ b/src/main/java/nl/sander/jsontoy2/ReaderFactory.java @@ -18,7 +18,10 @@ public class ReaderFactory { registerPrimitiveTypeReaders(); } - public void registerCustomReader(Class type, JsonValueReader reader) { + private ReaderFactory() { + } + + public static void registerCustomReader(Class type, JsonValueReader reader) { readers.put(type, reader); } diff --git a/src/test/java/nl/sander/jsontoy2/WrapperObjectTests.java b/src/test/java/nl/sander/jsontoy2/WrapperObjectTests.java index 8df9b8e..817d465 100644 --- a/src/test/java/nl/sander/jsontoy2/WrapperObjectTests.java +++ b/src/test/java/nl/sander/jsontoy2/WrapperObjectTests.java @@ -6,8 +6,7 @@ import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.HashSet; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.*; public class WrapperObjectTests { @@ -22,9 +21,9 @@ public class WrapperObjectTests { @Test public void testBoolean() { -// BooleanBean trueBean = JsonReader.read(BooleanBean.class, "{\"value\": true}"); -// assertTrue(trueBean.isValue()); - // second call to read, must not recreate class definition, would not compile + BooleanBean trueBean = JsonReader.read(BooleanBean.class, "{\"value\": true}"); + assertTrue(trueBean.isValue()); + // second call to read, must not recreate class definition, (it would not compile) // so this test implicitly tests caching function too BooleanBean falseBean = JsonReader.read(BooleanBean.class, "{\"value2\": false}"); assertFalse(falseBean.isValue());