From b72935960f7e08a90158ef73170436320a00c8bc Mon Sep 17 00:00:00 2001 From: Sander Hautvast Date: Tue, 7 Jul 2020 21:12:04 +0200 Subject: [PATCH] added support for lists (no maps as list elements yet) --- .../java/nl/sander/jsontoy2/IoReader.java | 92 ++++++++++++++++--- .../java/nl/sander/jsontoy2/JsonReader.java | 4 +- .../sander/jsontoy2/readers/ListReader.java | 14 +++ src/test/java/nl/sander/jsontoy2/Lists.java | 68 ++++++++++++++ 4 files changed, 166 insertions(+), 12 deletions(-) create mode 100644 src/main/java/nl/sander/jsontoy2/readers/ListReader.java create mode 100644 src/test/java/nl/sander/jsontoy2/Lists.java diff --git a/src/main/java/nl/sander/jsontoy2/IoReader.java b/src/main/java/nl/sander/jsontoy2/IoReader.java index 389ccaa..69a412e 100644 --- a/src/main/java/nl/sander/jsontoy2/IoReader.java +++ b/src/main/java/nl/sander/jsontoy2/IoReader.java @@ -1,18 +1,22 @@ package nl.sander.jsontoy2; -import java.io.*; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; import java.nio.ByteBuffer; +import java.util.*; public class IoReader { private static final int HEX_RADIX = 16; private final InputStream inputStream; private final ByteBuf characterBuffer = new ByteBuf(); private final ByteBuf encodedCodePointBuffer = new ByteBuf(4); + private final ByteBuffer convertBuffer = ByteBuffer.allocate(4); private boolean escaping = false; private boolean encoded = false; - private final ByteBuffer convertBuffer = ByteBuffer.allocate(4); private byte current; private int linecount = 0; + private long charcount = 0; protected IoReader(InputStream inputStream) { this.inputStream = inputStream; @@ -55,9 +59,9 @@ public class IoReader { } public Character readCharacter() { - eat('\"'); + eatUntil('\"'); char currentChar = (char) current; - eat('\"'); + eatUntil('\"'); return currentChar; } @@ -88,9 +92,57 @@ public class IoReader { return characterBuffer.toString(); } + public List readList() { + List list = new ArrayList<>(); + + if (current != '[') { + throw new JsonReadException("no list found"); + } + advance(); + while (current != -1 && current != ']') { + Optional maybeValue = readValue(); + if (maybeValue.isEmpty()) { + break; + } else { + list.add(maybeValue.get()); + eatUntilAny(','); + } + } + + return list; + } + + private Optional readValue() { + Object value; + skipWhitespace(); + if (current == ']') { + return Optional.empty(); + } else if (current == '[') { + value = readList(); + } else if (current == '{') { + value = readMap(); + } else if (current == '\"') { + value = readString(); + } else if (current == 'T' || current == 't' || current == 'F' || current == 'f') { + value = readBoolean(); + } else { + String numeric = readNumeric(); + double doubleValue = Double.parseDouble(numeric); + if ((int) doubleValue == doubleValue) { + value = (int) doubleValue; + } else { + value = doubleValue; + } + } + return Optional.of(value); + } + + private Map readMap() { + return new HashMap<>(); + } public String readString() { - eat('\"'); + eatUntil('\"'); characterBuffer.clear(); boolean endOfString = false; @@ -134,9 +186,6 @@ public class IoReader { } advance(); } - if (current != -1) { - advance(); - } return characterBuffer.toString(); } @@ -151,7 +200,8 @@ public class IoReader { void advance() { try { - current = (byte)inputStream.read(); + current = (byte) inputStream.read(); + System.out.println((charcount++) + ":" + (char) current); } catch (IOException e) { throw new IllegalStateException(e); } @@ -160,7 +210,7 @@ public class IoReader { void skipWhitespace() { try { while (current > -1 && Character.isWhitespace(current)) { - current = (byte)inputStream.read(); + current = (byte) inputStream.read(); } if (current == -1) { throw new JsonReadException("end of source reached"); @@ -170,7 +220,7 @@ public class IoReader { } } - String eat(char until) { + String eatUntil(char until) { characterBuffer.clear(); while (current > -1 && (current != until | Character.isWhitespace(current))) { @@ -180,4 +230,24 @@ public class IoReader { advance(); return characterBuffer.toString(); } + + String eatUntilAny(char... untilOrrChars) { + characterBuffer.clear(); + + while (current > -1 && (!contains(untilOrrChars, current) | Character.isWhitespace(current))) { + characterBuffer.add(current); + advance(); + } + advance(); + return characterBuffer.toString(); + } + + private boolean contains(char[] chars, byte search) { + for (char aChar : chars) { + if (search == aChar) { + return true; + } + } + return false; + } } diff --git a/src/main/java/nl/sander/jsontoy2/JsonReader.java b/src/main/java/nl/sander/jsontoy2/JsonReader.java index 615b095..0e306eb 100644 --- a/src/main/java/nl/sander/jsontoy2/JsonReader.java +++ b/src/main/java/nl/sander/jsontoy2/JsonReader.java @@ -5,6 +5,7 @@ import nl.sander.jsontoy2.readers.*; import java.io.InputStream; import java.time.LocalDateTime; import java.util.Date; +import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -32,7 +33,7 @@ public class JsonReader { return readers.get(type); } - public static void register(Class type, JsonObjectReader objectReader) { + static void register(Class type, JsonObjectReader objectReader) { readers.put(type, objectReader); } @@ -56,5 +57,6 @@ public class JsonReader { register(char.class, new CharReader()); register(String.class, new StringReader()); register(LocalDateTime.class, new LocalDateTimeReader()); + register(List.class, new ListReader()); } } diff --git a/src/main/java/nl/sander/jsontoy2/readers/ListReader.java b/src/main/java/nl/sander/jsontoy2/readers/ListReader.java new file mode 100644 index 0000000..419a8bd --- /dev/null +++ b/src/main/java/nl/sander/jsontoy2/readers/ListReader.java @@ -0,0 +1,14 @@ +package nl.sander.jsontoy2.readers; + +import nl.sander.jsontoy2.IoReader; +import nl.sander.jsontoy2.JsonObjectReader; + +import java.util.List; + +@SuppressWarnings("rawtypes") +public class ListReader implements JsonObjectReader { + @Override + public List read(IoReader ioReader) { + return ioReader.readList(); + } +} diff --git a/src/test/java/nl/sander/jsontoy2/Lists.java b/src/test/java/nl/sander/jsontoy2/Lists.java new file mode 100644 index 0000000..cd77529 --- /dev/null +++ b/src/test/java/nl/sander/jsontoy2/Lists.java @@ -0,0 +1,68 @@ +package nl.sander.jsontoy2; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@SuppressWarnings("unchecked") +public class Lists { + + @Test + public void emptyList() { + List list = JsonReader.read(List.class, "[]"); + assertEquals(new ArrayList<>(), list); + } + + @Test + public void singleStringList() { + List list = JsonReader.read(List.class, "[\"hello jason\"]"); + assertEquals(Collections.singletonList("hello jason"), list); + } + + @Test + public void multipleStringList() { + List list = JsonReader.read(List.class, "[\"hello\" , \"jason\"]"); + List expected = Arrays.asList("hello", "jason"); + assertEquals(expected, list); + } + + @Test + public void multipleStrings_noComma_error() { + List list = JsonReader.read(List.class, "[\"hello\" \"jason\"]"); + List expected = Collections.singletonList("hello"); + assertEquals(expected, list); + } + + @Test + public void singleInt() { + List list = JsonReader.read(List.class, "[1]"); + List expected = Collections.singletonList(1); + assertEquals(expected, list); + } + + @Test + public void multipleInts() { + List list = JsonReader.read(List.class, "[1,2]"); + List expected = Arrays.asList(1,2); + assertEquals(expected, list); + } + + @Test + public void intDoubleBooleanString() { + List list = JsonReader.read(List.class, "[1,2.5,false,\"hello jason\"]"); + List expected = Arrays.asList(1,2.5,false,"hello jason"); + assertEquals(expected, list); + } + + @Test + public void nestedList() { + List list = JsonReader.read(List.class, "[[],[]]"); + List expected = Arrays.asList(List.of(), List.of()); + assertEquals(expected, list); + } +} \ No newline at end of file