From 59fbbe904a956c25189a83b19287ac32b51d5dd5 Mon Sep 17 00:00:00 2001 From: Sander Hautvast Date: Thu, 6 Aug 2020 15:50:57 +0200 Subject: [PATCH] got arrays working --- .../jsontoy2/JavaObjectReaderFactory.java | 20 ++++-- .../nl/sander/jsontoy2/JsonValueReader.java | 69 +++++++++++++++++-- src/main/java/nl/sander/jsontoy2/Parser.java | 2 +- .../nl/sander/jsontoy2/ReaderFactory.java | 2 +- .../sander/jsontoy2/WrapperObjectTests.java | 62 +++++++++++------ .../nl/sander/jsontoy2/beans/ArrayBean.java | 13 ++++ .../sander/jsontoy2/beans/LinkedListBean.java | 15 ++++ 7 files changed, 148 insertions(+), 35 deletions(-) create mode 100644 src/test/java/nl/sander/jsontoy2/beans/ArrayBean.java create mode 100644 src/test/java/nl/sander/jsontoy2/beans/LinkedListBean.java diff --git a/src/main/java/nl/sander/jsontoy2/JavaObjectReaderFactory.java b/src/main/java/nl/sander/jsontoy2/JavaObjectReaderFactory.java index cfbcc92..e83ba94 100644 --- a/src/main/java/nl/sander/jsontoy2/JavaObjectReaderFactory.java +++ b/src/main/java/nl/sander/jsontoy2/JavaObjectReaderFactory.java @@ -7,6 +7,7 @@ import org.jetbrains.annotations.NotNull; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; +import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -52,7 +53,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); @@ -99,13 +100,24 @@ public class JavaObjectReaderFactory { return "getFloat(\"" + fieldName + "\",object)"; } else if (fieldType == double.class) { return "getDouble(\"" + fieldName + "\",object)"; - } else if (Set.class.isAssignableFrom(fieldType)) { - return "getSet(\"" + fieldName + "\",object)"; } else { - return "(" + fieldType.getName() + ")(object.get(\"" + fieldName + "\"))"; + String fieldTypeName = field.getType().getName(); + if (Set.class.isAssignableFrom(fieldType)) { + return "(" + fieldTypeName + ")getSet(\"" + fieldName + "\"," + fieldTypeName + ".class,object)"; + } else if (List.class.isAssignableFrom(fieldType)) { + return "(" + fieldTypeName + ")getList(\"" + fieldName + "\"," + fieldTypeName + ".class,object)"; + } else if (fieldType.isArray()) { + return "(" + format(fieldTypeName) + "[])getArray(\"" + fieldName + "\"," + format(fieldTypeName) + ".class,object)"; + } else { + return "(" + fieldTypeName + ")(object.get(\"" + fieldName + "\"))"; + } } } + private static String format(String arrayTypeExpr) { + return arrayTypeExpr.substring(2).substring(0, arrayTypeExpr.length() - 3); + } + //should be reinstated private static String genericType(CtField field) { try { diff --git a/src/main/java/nl/sander/jsontoy2/JsonValueReader.java b/src/main/java/nl/sander/jsontoy2/JsonValueReader.java index e35eefd..db2002d 100644 --- a/src/main/java/nl/sander/jsontoy2/JsonValueReader.java +++ b/src/main/java/nl/sander/jsontoy2/JsonValueReader.java @@ -1,9 +1,9 @@ package nl.sander.jsontoy2; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.*; /** * Base class for generated readers @@ -49,9 +49,64 @@ public abstract class JsonValueReader { return value == null ? 0D : (Double) value; } - protected Set getSet(String fieldName, Map values) { - Object value = values.get(fieldName); - return value == null ? null : new HashSet<>((List) value); + /* + * Creates a Set type for any implementation of it (that the containing bean needs), + * unless it has no constructor using a java.util.Collection as single parameter. + * + * returntype is generic Set, object needs cast afterwards in generated code + */ + protected Set getSet(String fieldName, Class> type, Map values) { + List value = (List) values.get(fieldName); + if (value == null) { + return null; + } else { + try { + if (type.equals(Set.class)) { + return new HashSet<>(value); + } else { + Constructor> constructor = type.getConstructor(Collection.class); + return constructor.newInstance(value); + } + } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { + throw new JsonParseException(e); + } + } } + /* + * Creates a List type for any implementation of it (that the containing bean needs), + * unless it has no constructor using a java.util.Collection as single parameter. + * + * returntype is generic List, needs cast afterwards in generated code + */ + protected List getList(String fieldName, Class listImplType, Map values) { + List value = (List) values.get(fieldName); + if (value == null) { + return null; + } else if (listImplType == ArrayList.class || listImplType.equals(List.class)) { + return value; + } else { + try { + Constructor constructor = listImplType.getConstructor(Collection.class); + return (List) constructor.newInstance(value); + } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { + throw new JsonParseException(e); + } + } + } + + protected Object getArray(String fieldName, Class arrayType, Map values) { + List value = (List) values.get(fieldName); + if (value == null) { + return new Object[]{}; + } else { + Object[] array = (Object[]) Array.newInstance(arrayType, value.size()); + int index = 0; + for (Object element : value) { + array[index] = value.get(index++); + } + //TODO this can probably be done smarter + return array; + } + } } diff --git a/src/main/java/nl/sander/jsontoy2/Parser.java b/src/main/java/nl/sander/jsontoy2/Parser.java index f7d6517..0616cb8 100644 --- a/src/main/java/nl/sander/jsontoy2/Parser.java +++ b/src/main/java/nl/sander/jsontoy2/Parser.java @@ -148,7 +148,7 @@ public class Parser extends Lexer { skipWhitespace(); final Maybe maybeValue; try { - maybeValue = type == null ? parseValue() : parseValue(type.getDeclaredField(key).getType()); + maybeValue = type == null ? parseValue() : Maybe.of(JsonReader.read(type.getDeclaredField(key).getType(), this)); maybeValue.ifPresent(value -> map.put(key, value)); } catch (NoSuchFieldException e) { throw new JsonParseException(e); diff --git a/src/main/java/nl/sander/jsontoy2/ReaderFactory.java b/src/main/java/nl/sander/jsontoy2/ReaderFactory.java index 6b3a199..5c25ac3 100644 --- a/src/main/java/nl/sander/jsontoy2/ReaderFactory.java +++ b/src/main/java/nl/sander/jsontoy2/ReaderFactory.java @@ -32,7 +32,7 @@ public class ReaderFactory { static JsonValueReader getReader(Class type) { if (Map.class.isAssignableFrom(type)) { return MAPREADER; - } else if (List.class.isAssignableFrom(type) || Set.class.isAssignableFrom(type)) { + } else if (List.class.isAssignableFrom(type) || Set.class.isAssignableFrom(type) || type.isArray()) { return LISTREADER; } else { return readers.computeIfAbsent(type, k -> { diff --git a/src/test/java/nl/sander/jsontoy2/WrapperObjectTests.java b/src/test/java/nl/sander/jsontoy2/WrapperObjectTests.java index 817d465..4ec0085 100644 --- a/src/test/java/nl/sander/jsontoy2/WrapperObjectTests.java +++ b/src/test/java/nl/sander/jsontoy2/WrapperObjectTests.java @@ -5,6 +5,8 @@ import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; import static org.junit.jupiter.api.Assertions.*; @@ -89,23 +91,23 @@ public class WrapperObjectTests { assertEquals(new HashSet<>(Arrays.asList("a", "b")), listBean.getValue()); } -// @Test -// public void testIntegerList() { -// IntegerListBean listBean = JsonReader.read(IntegerListBean.class, "{\"value\": [1,22]}"); -// assertEquals(Arrays.asList(1, 22), listBean.getValue()); -// } -// -// @Test -// public void testCharacterList() { -// CharacterListBean listBean = JsonReader.read(CharacterListBean.class, "{\"value\": [\"a\", \"[\", \"^\"]}"); -// assertEquals(Arrays.asList('a', '[', '^'), listBean.getValue()); -// } + @Test + public void testIntegerList() { + IntegerListBean listBean = JsonReader.read(IntegerListBean.class, "{\"value\": [1,22]}"); + assertEquals(Arrays.asList(1, 22), listBean.getValue()); + } -// @Test -// public void testShortList() { -// ShortListBean listBean = JsonReader.read(ShortListBean.class, "{\"value\": [-1,0,1]}"); -// assertEquals(Arrays.asList((short) -1, (short) 0, (short) 1), listBean.getValue()); -// } + @Test + public void testCharacterList() { + CharacterListBean listBean = JsonReader.read(CharacterListBean.class, "{\"value\": [\"a\", \"[\", \"^\"]}"); + assertEquals(Arrays.asList('a', '[', '^'), listBean.getValue()); + } + + @Test + public void testShortList() { + ShortListBean listBean = JsonReader.read(ShortListBean.class, "{\"value\": [-1,0,1]}"); + assertEquals(Arrays.asList((short) -1, (short) 0, (short) 1), listBean.getValue()); + } @Test public void testBooleanList() { @@ -113,7 +115,7 @@ public class WrapperObjectTests { assertEquals(Arrays.asList(true, false), listBean.getValue()); } -// @Test + // @Test // public void testFloatList() { // FloatListBean listBean = JsonReader.read(FloatListBean.class, "{\"value\": [-100.156,78.0]}"); // assertEquals(Arrays.asList(-100.156F, 78.0F), listBean.getValue()); @@ -125,11 +127,11 @@ public class WrapperObjectTests { // assertEquals(Arrays.asList((byte) -100, (byte) 78), listBean.getValue()); // } // -// @Test -// public void testDoubleList() { -// DoubleListBean listBean = JsonReader.read(DoubleListBean.class, "{\"value\": [-100.156,78.0]}"); -// assertEquals(Arrays.asList(-100.156D, 78.0D), listBean.getValue()); -// } + @Test + public void testDoubleList() { + DoubleListBean listBean = JsonReader.read(DoubleListBean.class, "{\"value\": [-100.156,78.0]}"); + assertEquals(Arrays.asList(-100.156D, 78.0D), listBean.getValue()); + } @Test public void testNestedBean() { @@ -143,4 +145,20 @@ public class WrapperObjectTests { StringMapBean expected = new StringMapBean("a:", "b", "c:", "d"); assertEquals(expected, actual); } + + @Test + public void testLinkedList() { + LinkedListBean actual = JsonReader.read(LinkedListBean.class, "{\"list\": [\"a\"]}"); + LinkedList actualList = actual.getList(); + + assertEquals(List.of("a"), actualList); + } + + @Test + public void testArray() { + ArrayBean actual = JsonReader.read(ArrayBean.class, "{\"array\": [\"a\"]}"); + + assertEquals(1, actual.getArray().length); + assertEquals("a", actual.getArray()[0]); + } } diff --git a/src/test/java/nl/sander/jsontoy2/beans/ArrayBean.java b/src/test/java/nl/sander/jsontoy2/beans/ArrayBean.java new file mode 100644 index 0000000..c5747a9 --- /dev/null +++ b/src/test/java/nl/sander/jsontoy2/beans/ArrayBean.java @@ -0,0 +1,13 @@ +package nl.sander.jsontoy2.beans; + +public class ArrayBean { + private String[] array; + + public void setArray(String[] array) { + this.array = array; + } + + public String[] getArray() { + return array; + } +} diff --git a/src/test/java/nl/sander/jsontoy2/beans/LinkedListBean.java b/src/test/java/nl/sander/jsontoy2/beans/LinkedListBean.java new file mode 100644 index 0000000..60b150c --- /dev/null +++ b/src/test/java/nl/sander/jsontoy2/beans/LinkedListBean.java @@ -0,0 +1,15 @@ +package nl.sander.jsontoy2.beans; + +import java.util.LinkedList; + +public class LinkedListBean { + private LinkedList list; + + public LinkedList getList() { + return list; + } + + public void setList(LinkedList list) { + this.list = list; + } +}