diff --git a/benchmark/build.gradle b/benchmark/build.gradle index 16af97d..79b0899 100644 --- a/benchmark/build.gradle +++ b/benchmark/build.gradle @@ -8,7 +8,7 @@ jmhReport { } dependencies { - implementation project(':datastructures-json') + implementation project(':datastructures-json-tiny') implementation project(':datastructures-json-dsl') implementation project(':datastructures-json-flat') implementation project(':datastructures-json-iterator') diff --git a/benchmark/src/jmh/java/org/xbib/datastructures/benchmark/JsonLargeBenchmark.java b/benchmark/src/jmh/java/org/xbib/datastructures/benchmark/JsonLargeBenchmark.java index 0d36e15..945f871 100644 --- a/benchmark/src/jmh/java/org/xbib/datastructures/benchmark/JsonLargeBenchmark.java +++ b/benchmark/src/jmh/java/org/xbib/datastructures/benchmark/JsonLargeBenchmark.java @@ -15,10 +15,10 @@ import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Threads; import org.openjdk.jmh.annotations.Timeout; import org.openjdk.jmh.annotations.Warmup; -import org.xbib.datastructures.json.EmptyJsonListener; -import org.xbib.datastructures.json.StandardJsonListener; -import org.xbib.datastructures.json.StringParser; -import org.xbib.datastructures.json.TinyJsonListener; +import org.xbib.datastructures.json.tiny.EmptyJsonListener; +import org.xbib.datastructures.json.tiny.StandardJsonListener; +import org.xbib.datastructures.json.tiny.StringParser; +import org.xbib.datastructures.json.tiny.TinyJsonListener; import org.xbib.datastructures.json.flat.Json; import org.xbib.datastructures.json.noggit.ObjectBuilder; import org.xbib.datastructures.json.simple.JSONParser; diff --git a/benchmark/src/jmh/java/org/xbib/datastructures/benchmark/JsonMediumBenchmark.java b/benchmark/src/jmh/java/org/xbib/datastructures/benchmark/JsonMediumBenchmark.java index 46ba2a3..76af194 100644 --- a/benchmark/src/jmh/java/org/xbib/datastructures/benchmark/JsonMediumBenchmark.java +++ b/benchmark/src/jmh/java/org/xbib/datastructures/benchmark/JsonMediumBenchmark.java @@ -15,10 +15,10 @@ import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Threads; import org.openjdk.jmh.annotations.Warmup; import org.openjdk.jmh.annotations.Timeout; -import org.xbib.datastructures.json.EmptyJsonListener; -import org.xbib.datastructures.json.StandardJsonListener; -import org.xbib.datastructures.json.StringParser; -import org.xbib.datastructures.json.TinyJsonListener; +import org.xbib.datastructures.json.tiny.EmptyJsonListener; +import org.xbib.datastructures.json.tiny.StandardJsonListener; +import org.xbib.datastructures.json.tiny.StringParser; +import org.xbib.datastructures.json.tiny.TinyJsonListener; import org.xbib.datastructures.json.flat.Json; import org.xbib.datastructures.json.noggit.ObjectBuilder; import org.xbib.datastructures.json.simple.JSONParser; diff --git a/benchmark/src/jmh/java/org/xbib/datastructures/benchmark/JsonSmallBenchmark.java b/benchmark/src/jmh/java/org/xbib/datastructures/benchmark/JsonSmallBenchmark.java index 2640650..6261a53 100644 --- a/benchmark/src/jmh/java/org/xbib/datastructures/benchmark/JsonSmallBenchmark.java +++ b/benchmark/src/jmh/java/org/xbib/datastructures/benchmark/JsonSmallBenchmark.java @@ -15,10 +15,10 @@ import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Threads; import org.openjdk.jmh.annotations.Timeout; import org.openjdk.jmh.annotations.Warmup; -import org.xbib.datastructures.json.EmptyJsonListener; -import org.xbib.datastructures.json.StandardJsonListener; -import org.xbib.datastructures.json.StringParser; -import org.xbib.datastructures.json.TinyJsonListener; +import org.xbib.datastructures.json.tiny.EmptyJsonListener; +import org.xbib.datastructures.json.tiny.StandardJsonListener; +import org.xbib.datastructures.json.tiny.StringParser; +import org.xbib.datastructures.json.tiny.TinyJsonListener; import org.xbib.datastructures.json.flat.Json; import org.xbib.datastructures.json.noggit.ObjectBuilder; import org.xbib.datastructures.json.simple.JSONParser; diff --git a/datastructures-api/src/main/java/module-info.java b/datastructures-api/src/main/java/module-info.java new file mode 100644 index 0000000..873246f --- /dev/null +++ b/datastructures-api/src/main/java/module-info.java @@ -0,0 +1,3 @@ +module org.xbib.datastructures.api { + exports org.xbib.datastructures.api; +} diff --git a/datastructures-api/src/main/java/org/xbib/datastructures/api/Builder.java b/datastructures-api/src/main/java/org/xbib/datastructures/api/Builder.java new file mode 100644 index 0000000..5b788ba --- /dev/null +++ b/datastructures-api/src/main/java/org/xbib/datastructures/api/Builder.java @@ -0,0 +1,28 @@ +package org.xbib.datastructures.api; + +import java.io.IOException; +import java.util.Collection; +import java.util.Map; + +public interface Builder { + + Builder beginCollection() throws IOException; + + Builder endCollection() throws IOException; + + Builder beginMap() throws IOException; + + Builder endMap() throws IOException; + + Builder buildMap(Map map) throws IOException; + + Builder buildCollection(Collection collection) throws IOException; + + Builder buildKey(CharSequence charSequence) throws IOException; + + Builder buildValue(Object object) throws IOException; + + Builder buildNull() throws IOException; + + String build(); +} diff --git a/datastructures-api/src/main/java/org/xbib/datastructures/api/ByteSizeUnit.java b/datastructures-api/src/main/java/org/xbib/datastructures/api/ByteSizeUnit.java new file mode 100644 index 0000000..3ea61b7 --- /dev/null +++ b/datastructures-api/src/main/java/org/xbib/datastructures/api/ByteSizeUnit.java @@ -0,0 +1,232 @@ +package org.xbib.datastructures.api; + +/** + * A {@code SizeUnit} represents size at a given unit of + * granularity and provides utility methods to convert across units. + * A {@code SizeUnit} does not maintain size information, but only + * helps organize and use size representations that may be maintained + * separately across various contexts. + */ +public enum ByteSizeUnit { + BYTES { + @Override + public long toBytes(long size) { + return size; + } + + @Override + public long toKB(long size) { + return size / (C1 / C0); + } + + @Override + public long toMB(long size) { + return size / (C2 / C0); + } + + @Override + public long toGB(long size) { + return size / (C3 / C0); + } + + @Override + public long toTB(long size) { + return size / (C4 / C0); + } + + @Override + public long toPB(long size) { + return size / (C5 / C0); + } + }, + KB { + @Override + public long toBytes(long size) { + return x(size, C1 / C0, MAX / (C1 / C0)); + } + + @Override + public long toKB(long size) { + return size; + } + + @Override + public long toMB(long size) { + return size / (C2 / C1); + } + + @Override + public long toGB(long size) { + return size / (C3 / C1); + } + + @Override + public long toTB(long size) { + return size / (C4 / C1); + } + + @Override + public long toPB(long size) { + return size / (C5 / C1); + } + }, + MB { + @Override + public long toBytes(long size) { + return x(size, C2 / C0, MAX / (C2 / C0)); + } + + @Override + public long toKB(long size) { + return x(size, C2 / C1, MAX / (C2 / C1)); + } + + @Override + public long toMB(long size) { + return size; + } + + @Override + public long toGB(long size) { + return size / (C3 / C2); + } + + @Override + public long toTB(long size) { + return size / (C4 / C2); + } + + @Override + public long toPB(long size) { + return size / (C5 / C2); + } + }, + GB { + @Override + public long toBytes(long size) { + return x(size, C3 / C0, MAX / (C3 / C0)); + } + + @Override + public long toKB(long size) { + return x(size, C3 / C1, MAX / (C3 / C1)); + } + + @Override + public long toMB(long size) { + return x(size, C3 / C2, MAX / (C3 / C2)); + } + + @Override + public long toGB(long size) { + return size; + } + + @Override + public long toTB(long size) { + return size / (C4 / C3); + } + + @Override + public long toPB(long size) { + return size / (C5 / C3); + } + }, + TB { + @Override + public long toBytes(long size) { + return x(size, C4 / C0, MAX / (C4 / C0)); + } + + @Override + public long toKB(long size) { + return x(size, C4 / C1, MAX / (C4 / C1)); + } + + @Override + public long toMB(long size) { + return x(size, C4 / C2, MAX / (C4 / C2)); + } + + @Override + public long toGB(long size) { + return x(size, C4 / C3, MAX / (C4 / C3)); + } + + @Override + public long toTB(long size) { + return size; + } + + @Override + public long toPB(long size) { + return size / (C5 / C4); + } + }, + PB { + @Override + public long toBytes(long size) { + return x(size, C5 / C0, MAX / (C5 / C0)); + } + + @Override + public long toKB(long size) { + return x(size, C5 / C1, MAX / (C5 / C1)); + } + + @Override + public long toMB(long size) { + return x(size, C5 / C2, MAX / (C5 / C2)); + } + + @Override + public long toGB(long size) { + return x(size, C5 / C3, MAX / (C5 / C3)); + } + + @Override + public long toTB(long size) { + return x(size, C5 / C4, MAX / (C5 / C4)); + } + + @Override + public long toPB(long size) { + return size; + } + }; + + static final long C0 = 1L; + static final long C1 = C0 * 1024L; + static final long C2 = C1 * 1024L; + static final long C3 = C2 * 1024L; + static final long C4 = C3 * 1024L; + static final long C5 = C4 * 1024L; + + static final long MAX = Long.MAX_VALUE; + + /** + * Scale d by m, checking for overflow. + * This has a short name to make above code more readable. + */ + static long x(long d, long m, long over) { + if (d > over) { + return Long.MAX_VALUE; + } + if (d < -over) { + return Long.MIN_VALUE; + } + return d * m; + } + + public abstract long toBytes(long size); + + public abstract long toKB(long size); + + public abstract long toMB(long size); + + public abstract long toGB(long size); + + public abstract long toTB(long size); + + public abstract long toPB(long size); +} diff --git a/datastructures-api/src/main/java/org/xbib/datastructures/api/ByteSizeValue.java b/datastructures-api/src/main/java/org/xbib/datastructures/api/ByteSizeValue.java new file mode 100644 index 0000000..bbe2217 --- /dev/null +++ b/datastructures-api/src/main/java/org/xbib/datastructures/api/ByteSizeValue.java @@ -0,0 +1,228 @@ +package org.xbib.datastructures.api; + +import java.util.Locale; + +/** + * + */ +public class ByteSizeValue { + + private long size; + + private ByteSizeUnit sizeUnit; + + private ByteSizeValue() { + } + + public ByteSizeValue(long size, ByteSizeUnit sizeUnit) { + this.size = size; + this.sizeUnit = sizeUnit; + } + + /** + * Format the double value with a single decimal points, trimming trailing '.0'. + * @param value value + * @param suffix suffix + * @return formatted decimal + */ + public static String format1Decimals(double value, String suffix) { + String p = String.valueOf(value); + int ix = p.indexOf('.') + 1; + int ex = p.indexOf('E'); + char fraction = p.charAt(ix); + if (fraction == '0') { + if (ex != -1) { + return p.substring(0, ix - 1) + p.substring(ex) + suffix; + } else { + return p.substring(0, ix - 1) + suffix; + } + } else { + if (ex != -1) { + return p.substring(0, ix) + fraction + p.substring(ex) + suffix; + } else { + return p.substring(0, ix) + fraction + suffix; + } + } + } + + public static ByteSizeValue parseBytesSizeValue(String sValue) { + return parseBytesSizeValue(sValue, null); + } + + public static ByteSizeValue parseBytesSizeValue(String sValue, ByteSizeValue defaultValue) { + if (sValue == null) { + return defaultValue; + } + long bytes; + try { + String lastTwoChars = sValue.substring(sValue.length() - Math.min(2, sValue.length())).toLowerCase(Locale.ROOT); + if (lastTwoChars.endsWith("k")) { + bytes = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 1)) * ByteSizeUnit.C1); + } else if (lastTwoChars.endsWith("kb")) { + bytes = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 2)) * ByteSizeUnit.C1); + } else if (lastTwoChars.endsWith("m")) { + bytes = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 1)) * ByteSizeUnit.C2); + } else if (lastTwoChars.endsWith("mb")) { + bytes = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 2)) * ByteSizeUnit.C2); + } else if (lastTwoChars.endsWith("g")) { + bytes = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 1)) * ByteSizeUnit.C3); + } else if (lastTwoChars.endsWith("gb")) { + bytes = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 2)) * ByteSizeUnit.C3); + } else if (lastTwoChars.endsWith("t")) { + bytes = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 1)) * ByteSizeUnit.C4); + } else if (lastTwoChars.endsWith("tb")) { + bytes = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 2)) * ByteSizeUnit.C4); + } else if (lastTwoChars.endsWith("p")) { + bytes = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 1)) * ByteSizeUnit.C5); + } else if (lastTwoChars.endsWith("pb")) { + bytes = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 2)) * ByteSizeUnit.C5); + } else if (lastTwoChars.endsWith("b")) { + bytes = Long.parseLong(sValue.substring(0, sValue.length() - 1)); + } else { + bytes = Long.parseLong(sValue); + } + } catch (NumberFormatException e) { + return defaultValue; + } + return new ByteSizeValue(bytes, ByteSizeUnit.BYTES); + } + + public int bytesAsInt() throws IllegalArgumentException { + long bytes = bytes(); + if (bytes > Integer.MAX_VALUE) { + throw new IllegalArgumentException("size [" + toString() + "] is bigger than max int"); + } + return (int) bytes; + } + + public long bytes() { + return sizeUnit.toBytes(size); + } + + public long getBytes() { + return bytes(); + } + + public long kb() { + return sizeUnit.toKB(size); + } + + public long getKb() { + return kb(); + } + + public long mb() { + return sizeUnit.toMB(size); + } + + public long getMb() { + return mb(); + } + + public long gb() { + return sizeUnit.toGB(size); + } + + public long getGb() { + return gb(); + } + + public long tb() { + return sizeUnit.toTB(size); + } + + public long getTb() { + return tb(); + } + + public long pb() { + return sizeUnit.toPB(size); + } + + public long getPb() { + return pb(); + } + + public double kbFrac() { + return ((double) bytes()) / ByteSizeUnit.C1; + } + + public double getKbFrac() { + return kbFrac(); + } + + public double mbFrac() { + return ((double) bytes()) / ByteSizeUnit.C2; + } + + public double getMbFrac() { + return mbFrac(); + } + + public double gbFrac() { + return ((double) bytes()) / ByteSizeUnit.C3; + } + + public double getGbFrac() { + return gbFrac(); + } + + public double tbFrac() { + return ((double) bytes()) / ByteSizeUnit.C4; + } + + public double getTbFrac() { + return tbFrac(); + } + + public double pbFrac() { + return ((double) bytes()) / ByteSizeUnit.C5; + } + + public double getPbFrac() { + return pbFrac(); + } + + @Override + public String toString() { + long bytes = bytes(); + double value = bytes; + String suffix = "b"; + if (bytes >= ByteSizeUnit.C5) { + value = pbFrac(); + suffix = "pb"; + } else if (bytes >= ByteSizeUnit.C4) { + value = tbFrac(); + suffix = "tb"; + } else if (bytes >= ByteSizeUnit.C3) { + value = gbFrac(); + suffix = "gb"; + } else if (bytes >= ByteSizeUnit.C2) { + value = mbFrac(); + suffix = "mb"; + } else if (bytes >= ByteSizeUnit.C1) { + value = kbFrac(); + suffix = "kb"; + } + return format1Decimals(value, suffix); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ByteSizeValue sizeValue = (ByteSizeValue) o; + return size == sizeValue.size && sizeUnit == sizeValue.sizeUnit; + } + + @Override + public int hashCode() { + int result = (int) (size ^ (size >>> 32)); + result = 31 * result + (sizeUnit != null ? sizeUnit.hashCode() : 0); + return result; + } +} diff --git a/datastructures-api/src/main/java/org/xbib/datastructures/api/DataStructure.java b/datastructures-api/src/main/java/org/xbib/datastructures/api/DataStructure.java new file mode 100644 index 0000000..15007b3 --- /dev/null +++ b/datastructures-api/src/main/java/org/xbib/datastructures/api/DataStructure.java @@ -0,0 +1,47 @@ +package org.xbib.datastructures.api; + +import java.time.Instant; +import java.util.function.Consumer; + +public interface DataStructure { + + Parser createParser(); + + Generator createGenerator(Node root); + + Builder createBuilder(); + + Builder createBuilder(Consumer consumer); + + void setRoot(Node root); + + Node getRoot(); + + Node getNode(String path); + + boolean set(String path, Object value); + + Boolean getBoolean(String path); + + Byte getByte(String path); + + Short getShort(String path); + + Integer getInteger(String path); + + Long getLong(String path); + + Float getFloat(String path); + + Double getDouble(String path); + + Character getCharacter(String path); + + String getString(String path); + + Instant getInstant(String path); + + TimeValue getAsTime(String setting, TimeValue defaultValue); + + ByteSizeValue getAsBytesSize(String setting, ByteSizeValue defaultValue); +} diff --git a/datastructures-api/src/main/java/org/xbib/datastructures/api/Generator.java b/datastructures-api/src/main/java/org/xbib/datastructures/api/Generator.java new file mode 100644 index 0000000..9eef035 --- /dev/null +++ b/datastructures-api/src/main/java/org/xbib/datastructures/api/Generator.java @@ -0,0 +1,9 @@ +package org.xbib.datastructures.api; + +import java.io.IOException; +import java.io.Writer; + +public interface Generator { + + void generate(Writer writer) throws IOException; +} diff --git a/datastructures-api/src/main/java/org/xbib/datastructures/api/ListNode.java b/datastructures-api/src/main/java/org/xbib/datastructures/api/ListNode.java new file mode 100644 index 0000000..ba965e5 --- /dev/null +++ b/datastructures-api/src/main/java/org/xbib/datastructures/api/ListNode.java @@ -0,0 +1,6 @@ +package org.xbib.datastructures.api; + +import java.util.List; + +public interface ListNode extends Node>> { +} diff --git a/datastructures-api/src/main/java/org/xbib/datastructures/api/MapNode.java b/datastructures-api/src/main/java/org/xbib/datastructures/api/MapNode.java new file mode 100644 index 0000000..0c1af90 --- /dev/null +++ b/datastructures-api/src/main/java/org/xbib/datastructures/api/MapNode.java @@ -0,0 +1,6 @@ +package org.xbib.datastructures.api; + +import java.util.Map; + +public interface MapNode extends Node>> { +} diff --git a/datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/Node.java b/datastructures-api/src/main/java/org/xbib/datastructures/api/Node.java similarity index 63% rename from datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/Node.java rename to datastructures-api/src/main/java/org/xbib/datastructures/api/Node.java index e47bbe9..484effd 100644 --- a/datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/Node.java +++ b/datastructures-api/src/main/java/org/xbib/datastructures/api/Node.java @@ -1,4 +1,4 @@ -package org.xbib.datastructures.yaml; +package org.xbib.datastructures.api; public interface Node { diff --git a/datastructures-api/src/main/java/org/xbib/datastructures/api/Parser.java b/datastructures-api/src/main/java/org/xbib/datastructures/api/Parser.java new file mode 100644 index 0000000..57ee98c --- /dev/null +++ b/datastructures-api/src/main/java/org/xbib/datastructures/api/Parser.java @@ -0,0 +1,9 @@ +package org.xbib.datastructures.api; + +import java.io.IOException; +import java.io.Reader; + +public interface Parser { + + Node parse(Reader reader) throws IOException; +} diff --git a/datastructures-api/src/main/java/org/xbib/datastructures/api/SizeUnit.java b/datastructures-api/src/main/java/org/xbib/datastructures/api/SizeUnit.java new file mode 100644 index 0000000..d028de9 --- /dev/null +++ b/datastructures-api/src/main/java/org/xbib/datastructures/api/SizeUnit.java @@ -0,0 +1,229 @@ +package org.xbib.datastructures.api; + +/** + * + */ +public enum SizeUnit { + SCALAR { + @Override + public long toScalar(long size) { + return size; + } + + @Override + public long toKilo(long size) { + return size / (C1 / C0); + } + + @Override + public long toMega(long size) { + return size / (C2 / C0); + } + + @Override + public long toGiga(long size) { + return size / (C3 / C0); + } + + @Override + public long toTera(long size) { + return size / (C4 / C0); + } + + @Override + public long toPeta(long size) { + return size / (C5 / C0); + } + }, + KILO { + @Override + public long toScalar(long size) { + return x(size, C1 / C0, MAX / (C1 / C0)); + } + + @Override + public long toKilo(long size) { + return size; + } + + @Override + public long toMega(long size) { + return size / (C2 / C1); + } + + @Override + public long toGiga(long size) { + return size / (C3 / C1); + } + + @Override + public long toTera(long size) { + return size / (C4 / C1); + } + + @Override + public long toPeta(long size) { + return size / (C5 / C1); + } + }, + MEGA { + @Override + public long toScalar(long size) { + return x(size, C2 / C0, MAX / (C2 / C0)); + } + + @Override + public long toKilo(long size) { + return x(size, C2 / C1, MAX / (C2 / C1)); + } + + @Override + public long toMega(long size) { + return size; + } + + @Override + public long toGiga(long size) { + return size / (C3 / C2); + } + + @Override + public long toTera(long size) { + return size / (C4 / C2); + } + + @Override + public long toPeta(long size) { + return size / (C5 / C2); + } + }, + GIGA { + @Override + public long toScalar(long size) { + return x(size, C3 / C0, MAX / (C3 / C0)); + } + + @Override + public long toKilo(long size) { + return x(size, C3 / C1, MAX / (C3 / C1)); + } + + @Override + public long toMega(long size) { + return x(size, C3 / C2, MAX / (C3 / C2)); + } + + @Override + public long toGiga(long size) { + return size; + } + + @Override + public long toTera(long size) { + return size / (C4 / C3); + } + + @Override + public long toPeta(long size) { + return size / (C5 / C3); + } + }, + TERA { + @Override + public long toScalar(long size) { + return x(size, C4 / C0, MAX / (C4 / C0)); + } + + @Override + public long toKilo(long size) { + return x(size, C4 / C1, MAX / (C4 / C1)); + } + + @Override + public long toMega(long size) { + return x(size, C4 / C2, MAX / (C4 / C2)); + } + + @Override + public long toGiga(long size) { + return x(size, C4 / C3, MAX / (C4 / C3)); + } + + @Override + public long toTera(long size) { + return size; + } + + @Override + public long toPeta(long size) { + return size / (C5 / C0); + } + }, + PETA { + @Override + public long toScalar(long size) { + return x(size, C5 / C0, MAX / (C5 / C0)); + } + + @Override + public long toKilo(long size) { + return x(size, C5 / C1, MAX / (C5 / C1)); + } + + @Override + public long toMega(long size) { + return x(size, C5 / C2, MAX / (C5 / C2)); + } + + @Override + public long toGiga(long size) { + return x(size, C5 / C3, MAX / (C5 / C3)); + } + + @Override + public long toTera(long size) { + return x(size, C5 / C4, MAX / (C5 / C4)); + } + + @Override + public long toPeta(long size) { + return size; + } + }; + + static final long C0 = 1L; + static final long C1 = C0 * 1000L; + static final long C2 = C1 * 1000L; + static final long C3 = C2 * 1000L; + static final long C4 = C3 * 1000L; + static final long C5 = C4 * 1000L; + + static final long MAX = Long.MAX_VALUE; + + /** + * Scale d by m, checking for overflow. + * This has a short name to make above code more readable. + */ + static long x(long d, long m, long over) { + if (d > over) { + return Long.MAX_VALUE; + } + if (d < -over) { + return Long.MIN_VALUE; + } + return d * m; + } + + + public abstract long toScalar(long size); + + public abstract long toKilo(long size); + + public abstract long toMega(long size); + + public abstract long toGiga(long size); + + public abstract long toTera(long size); + + public abstract long toPeta(long size); +} diff --git a/datastructures-api/src/main/java/org/xbib/datastructures/api/TimeValue.java b/datastructures-api/src/main/java/org/xbib/datastructures/api/TimeValue.java new file mode 100644 index 0000000..0596193 --- /dev/null +++ b/datastructures-api/src/main/java/org/xbib/datastructures/api/TimeValue.java @@ -0,0 +1,259 @@ +package org.xbib.datastructures.api; + +import java.util.concurrent.TimeUnit; + +/** + * + */ +public class TimeValue { + + private static final long C0 = 1L; + private static final long C1 = C0 * 1000L; + private static final long C2 = C1 * 1000L; + private static final long C3 = C2 * 1000L; + private static final long C4 = C3 * 60L; + private static final long C5 = C4 * 60L; + private static final long C6 = C5 * 24L; + private long duration; + private TimeUnit timeUnit; + + private TimeValue() { + } + + public TimeValue(long millis) { + this(millis, TimeUnit.MILLISECONDS); + } + + public TimeValue(long duration, TimeUnit timeUnit) { + this.duration = duration; + this.timeUnit = timeUnit; + } + + public static TimeValue timeValueNanos(long nanos) { + return new TimeValue(nanos, TimeUnit.NANOSECONDS); + } + + public static TimeValue timeValueMillis(long millis) { + return new TimeValue(millis, TimeUnit.MILLISECONDS); + } + + public static TimeValue timeValueSeconds(long seconds) { + return new TimeValue(seconds, TimeUnit.SECONDS); + } + + public static TimeValue timeValueMinutes(long minutes) { + return new TimeValue(minutes, TimeUnit.MINUTES); + } + + public static TimeValue timeValueHours(long hours) { + return new TimeValue(hours, TimeUnit.HOURS); + } + + /** + * Format the double value with a single decimal points, trimming trailing '.0'. + * + * @param value value + * @param suffix suffix + * @return string + */ + public static String format1Decimals(double value, String suffix) { + String p = String.valueOf(value); + int ix = p.indexOf('.') + 1; + int ex = p.indexOf('E'); + char fraction = p.charAt(ix); + if (fraction == '0') { + if (ex != -1) { + return p.substring(0, ix - 1) + p.substring(ex) + suffix; + } else { + return p.substring(0, ix - 1) + suffix; + } + } else { + if (ex != -1) { + return p.substring(0, ix) + fraction + p.substring(ex) + suffix; + } else { + return p.substring(0, ix) + fraction + suffix; + } + } + } + + public static TimeValue parseTimeValue(String sValue, TimeValue defaultValue) { + if (sValue == null) { + return defaultValue; + } + long millis; + if (sValue.endsWith("S")) { + millis = Long.parseLong(sValue.substring(0, sValue.length() - 1)); + } else if (sValue.endsWith("ms")) { + millis = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 2))); + } else if (sValue.endsWith("s")) { + millis = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 1)) * 1000); + } else if (sValue.endsWith("m")) { + millis = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 1)) * 60 * 1000); + } else if (sValue.endsWith("H") || sValue.endsWith("h")) { + millis = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 1)) * 60 * 60 * 1000); + } else if (sValue.endsWith("d")) { + millis = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 1)) * 24 * 60 * 60 * 1000); + } else if (sValue.endsWith("w")) { + millis = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 1)) * 7 * 24 * 60 * 60 * 1000); + } else { + millis = Long.parseLong(sValue); + } + return new TimeValue(millis, TimeUnit.MILLISECONDS); + } + + public long nanos() { + return timeUnit.toNanos(duration); + } + + public long getNanos() { + return nanos(); + } + + public long micros() { + return timeUnit.toMicros(duration); + } + + public long getMicros() { + return micros(); + } + + public long millis() { + return timeUnit.toMillis(duration); + } + + public long getMillis() { + return millis(); + } + + public long seconds() { + return timeUnit.toSeconds(duration); + } + + public long getSeconds() { + return seconds(); + } + + public long minutes() { + return timeUnit.toMinutes(duration); + } + + public long getMinutes() { + return minutes(); + } + + public long hours() { + return timeUnit.toHours(duration); + } + + public long getHours() { + return hours(); + } + + public long days() { + return timeUnit.toDays(duration); + } + + public long getDays() { + return days(); + } + + public double microsFrac() { + return ((double) nanos()) / C1; + } + + public double getMicrosFrac() { + return microsFrac(); + } + + public double millisFrac() { + return ((double) nanos()) / C2; + } + + public double getMillisFrac() { + return millisFrac(); + } + + public double secondsFrac() { + return ((double) nanos()) / C3; + } + + public double getSecondsFrac() { + return secondsFrac(); + } + + public double minutesFrac() { + return ((double) nanos()) / C4; + } + + public double getMinutesFrac() { + return minutesFrac(); + } + + public double hoursFrac() { + return ((double) nanos()) / C5; + } + + public double getHoursFrac() { + return hoursFrac(); + } + + public double daysFrac() { + return ((double) nanos()) / C6; + } + + public double getDaysFrac() { + return daysFrac(); + } + + @Override + public String toString() { + if (duration < 0) { + return Long.toString(duration); + } + long nanos = nanos(); + if (nanos == 0) { + return "0s"; + } + double value = nanos; + String suffix = "nanos"; + if (nanos >= C6) { + value = daysFrac(); + suffix = "d"; + } else if (nanos >= C5) { + value = hoursFrac(); + suffix = "h"; + } else if (nanos >= C4) { + value = minutesFrac(); + suffix = "m"; + } else if (nanos >= C3) { + value = secondsFrac(); + suffix = "s"; + } else if (nanos >= C2) { + value = millisFrac(); + suffix = "ms"; + } else if (nanos >= C1) { + value = microsFrac(); + suffix = "micros"; + } + return format1Decimals(value, suffix); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TimeValue timeValue = (TimeValue) o; + return duration == timeValue.duration && timeUnit == timeValue.timeUnit; + } + + @Override + public int hashCode() { + int result = (int) (duration ^ (duration >>> 32)); + result = 31 * result + (timeUnit != null ? timeUnit.hashCode() : 0); + return result; + } +} diff --git a/datastructures-api/src/main/java/org/xbib/datastructures/api/ValueNode.java b/datastructures-api/src/main/java/org/xbib/datastructures/api/ValueNode.java new file mode 100644 index 0000000..f50af4f --- /dev/null +++ b/datastructures-api/src/main/java/org/xbib/datastructures/api/ValueNode.java @@ -0,0 +1,9 @@ +package org.xbib.datastructures.api; + +public interface ValueNode extends Node { + + void set(Object value); + + @Override + Object get(); +} diff --git a/datastructures-json-micro/src/main/java/org/xbib/datastructures/json/micro/Reader.java b/datastructures-json-micro/src/main/java/org/xbib/datastructures/json/micro/Reader.java index eba6c4f..646af78 100644 --- a/datastructures-json-micro/src/main/java/org/xbib/datastructures/json/micro/Reader.java +++ b/datastructures-json-micro/src/main/java/org/xbib/datastructures/json/micro/Reader.java @@ -25,14 +25,14 @@ public class Reader { private static final Map escapes = new HashMap(); static { - escapes.put(new Character('"'), new Character('"')); - escapes.put(new Character('\\'), new Character('\\')); - escapes.put(new Character('/'), new Character('/')); - escapes.put(new Character('b'), new Character('\b')); - escapes.put(new Character('f'), new Character('\f')); - escapes.put(new Character('n'), new Character('\n')); - escapes.put(new Character('r'), new Character('\r')); - escapes.put(new Character('t'), new Character('\t')); + escapes.put('"', '"'); + escapes.put('\\', '\\'); + escapes.put('/', '/'); + escapes.put('b', '\b'); + escapes.put('f', '\f'); + escapes.put('n', '\n'); + escapes.put('r', '\r'); + escapes.put('t', '\t'); } private CharacterIterator it; @@ -272,9 +272,9 @@ public class Reader { if (c == 'u') { add(unicode()); } else { - Object value = escapes.get(new Character(c)); + Character value = escapes.get(c); if (value != null) { - add(((Character) value).charValue()); + add(value); } } } else { diff --git a/datastructures-json-noggit/src/main/java/org/xbib/datastructures/json/noggit/JSONParser.java b/datastructures-json-noggit/src/main/java/org/xbib/datastructures/json/noggit/JSONParser.java index 4acdcc6..9ccc3ff 100755 --- a/datastructures-json-noggit/src/main/java/org/xbib/datastructures/json/noggit/JSONParser.java +++ b/datastructures-json-noggit/src/main/java/org/xbib/datastructures/json/noggit/JSONParser.java @@ -15,7 +15,7 @@ public class JSONParser { public static final int LONG = 2; /** * Event indicating a JSON number value which has a fractional part or an exponent - * and with string length <= 23 chars not including sign. This covers + * and with string length <= 23 chars not including sign. This covers * all representations of normal values for Double.toString(). */ public static final int NUMBER = 3; diff --git a/datastructures-json/NOTICE.txt b/datastructures-json-tiny/NOTICE.txt similarity index 100% rename from datastructures-json/NOTICE.txt rename to datastructures-json-tiny/NOTICE.txt diff --git a/datastructures-json/build.gradle b/datastructures-json-tiny/build.gradle similarity index 59% rename from datastructures-json/build.gradle rename to datastructures-json-tiny/build.gradle index 1a8be78..a97af3b 100644 --- a/datastructures-json/build.gradle +++ b/datastructures-json-tiny/build.gradle @@ -1,3 +1,4 @@ dependencies { + api project(':datastructures-api') api project(':datastructures-tiny') } diff --git a/datastructures-json-tiny/src/main/java/module-info.java b/datastructures-json-tiny/src/main/java/module-info.java new file mode 100644 index 0000000..e8ad193 --- /dev/null +++ b/datastructures-json-tiny/src/main/java/module-info.java @@ -0,0 +1,9 @@ +import org.xbib.datastructures.api.DataStructure; +import org.xbib.datastructures.json.tiny.Json; + +module org.xbib.datastructures.json.tiny { + exports org.xbib.datastructures.json.tiny; + requires org.xbib.datastructures.api; + requires org.xbib.datastructures.tiny; + provides DataStructure with Json; +} diff --git a/datastructures-json/src/main/java/org/xbib/datastructures/json/EmptyJsonListener.java b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/EmptyJsonListener.java similarity index 79% rename from datastructures-json/src/main/java/org/xbib/datastructures/json/EmptyJsonListener.java rename to datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/EmptyJsonListener.java index ef9cd4b..b008697 100644 --- a/datastructures-json/src/main/java/org/xbib/datastructures/json/EmptyJsonListener.java +++ b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/EmptyJsonListener.java @@ -1,8 +1,10 @@ -package org.xbib.datastructures.json; +package org.xbib.datastructures.json.tiny; + +import org.xbib.datastructures.api.Node; import java.util.Deque; -public class EmptyJsonListener implements JsonDeserializer { +public class EmptyJsonListener implements JsonResult { @Override public void begin() { @@ -48,18 +50,15 @@ public class EmptyJsonListener implements JsonDeserializer { } @Override - public void beginList() { - + public void beginCollection() { } @Override - public void endList() { - + public void endCollection() { } @Override public void beginMap() { - } @Override @@ -67,7 +66,6 @@ public class EmptyJsonListener implements JsonDeserializer { } - @Override public Deque> getStack() { return null; } diff --git a/datastructures-json/src/main/java/org/xbib/datastructures/json/FastDoubleMath.java b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/FastDoubleMath.java similarity index 99% rename from datastructures-json/src/main/java/org/xbib/datastructures/json/FastDoubleMath.java rename to datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/FastDoubleMath.java index 0fd5595..a5ca693 100644 --- a/datastructures-json/src/main/java/org/xbib/datastructures/json/FastDoubleMath.java +++ b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/FastDoubleMath.java @@ -1,4 +1,4 @@ -package org.xbib.datastructures.json; +package org.xbib.datastructures.json.tiny; import java.util.Objects; diff --git a/datastructures-json/src/main/java/org/xbib/datastructures/json/FastDoubleParser.java b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/FastDoubleParser.java similarity index 99% rename from datastructures-json/src/main/java/org/xbib/datastructures/json/FastDoubleParser.java rename to datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/FastDoubleParser.java index 325ad40..cf828fe 100644 --- a/datastructures-json/src/main/java/org/xbib/datastructures/json/FastDoubleParser.java +++ b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/FastDoubleParser.java @@ -1,4 +1,4 @@ -package org.xbib.datastructures.json; +package org.xbib.datastructures.json.tiny; public class FastDoubleParser { diff --git a/datastructures-json/src/main/java/org/xbib/datastructures/json/Json.java b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/Json.java similarity index 79% rename from datastructures-json/src/main/java/org/xbib/datastructures/json/Json.java rename to datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/Json.java index b584db3..b28c122 100644 --- a/datastructures-json/src/main/java/org/xbib/datastructures/json/Json.java +++ b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/Json.java @@ -1,11 +1,12 @@ -package org.xbib.datastructures.json; +package org.xbib.datastructures.json.tiny; -import java.io.IOException; -import java.io.Reader; -import java.io.Writer; +import org.xbib.datastructures.api.*; + +import java.io.StringWriter; import java.time.Instant; +import java.util.function.Consumer; -public class Json { +public class Json implements DataStructure { private final char separator; @@ -24,28 +25,49 @@ public class Json { this.separator = separator; } - public void read(Reader reader) throws IOException { - StreamParser streamParser = new StreamParser(); - streamParser.parse(reader); - setRoot(streamParser.getNode()); + @Override + public Parser createParser() { + return new StreamParser(); } - public void save(Writer writer) throws IOException { - new Generator(getRoot(), writer).generate(); + @Override + public Generator createGenerator(Node root) { + return new JsonGenerator(root); } + @Override + public Builder createBuilder() { + return new JsonBuilder(new StringWriter()); + } + + @Override + public Builder createBuilder(Consumer consumer) { + return new JsonBuilder(new StringWriter()) { + @Override + public String toString() { + String string = super.toString(); + consumer.accept(string); + return string; + } + }; + } + + @Override public void setRoot(Node root) { this.root = root; } + @Override public Node getRoot() { return root; } - public boolean exist(String path) { - return getNode(path) != null; + @Override + public Node getNode(String path) { + return path == null ? null : internalGetNode(root, path); } + @Override public Boolean getBoolean(String path) { String value = internalGet(path); if (value == null) { @@ -58,10 +80,7 @@ public class Json { } } - public Boolean getBoolean(String path, Boolean defaultval) { - return getBoolean(path) != null ? getBoolean(path) : defaultval; - } - + @Override public Byte getByte(String path) { String value = internalGet(path); if (value == null) { @@ -74,10 +93,7 @@ public class Json { } } - public Byte getByte(String path, Byte defaultval) { - return getByte(path) != null ? getByte(path) : defaultval; - } - + @Override public Short getShort(String path) { String value = internalGet(path); if (value == null) { @@ -90,10 +106,7 @@ public class Json { } } - public Short getShort(String path, Short defaultval) { - return getShort(path) != null ? getShort(path) : defaultval; - } - + @Override public Integer getInteger(String path) { String value = internalGet(path); if (value == null) { @@ -106,10 +119,7 @@ public class Json { } } - public Integer getInteger(String path, Integer defaultval) { - return getInteger(path) != null ? getInteger(path) : defaultval; - } - + @Override public Long getLong(String path) { String value = internalGet(path); if (value == null) { @@ -122,10 +132,7 @@ public class Json { } } - public Long getLong(String path, Long defaultval) { - return getLong(path) != null ? getLong(path) : defaultval; - } - + @Override public Float getFloat(String path) { String value = internalGet(path); if (value == null) { @@ -138,10 +145,7 @@ public class Json { } } - public Float getFloat(String path, Float defaultval) { - return getFloat(path) != null ? getFloat(path) : defaultval; - } - + @Override public Double getDouble(String path) { String value = internalGet(path); if (value == null) { @@ -154,10 +158,7 @@ public class Json { } } - public Double getDouble(String path, Double defaultval) { - return getDouble(path) != null ? getDouble(path) : defaultval; - } - + @Override public Character getCharacter(String path) { String value = internalGet(path); if (value == null) { @@ -170,18 +171,12 @@ public class Json { } } - public Character getCharacter(String path, Character defaultval) { - return getCharacter(path) != null ? getCharacter(path) : defaultval; - } - + @Override public String getString(String path) { return internalGet(path); } - public String getString(String path, String defaultval) { - return getString(path) != null ? getString(path) : defaultval; - } - + @Override public Instant getInstant(String path) { String value = internalGet(path); if (value == null) { @@ -190,11 +185,17 @@ public class Json { return Instant.parse(value); } - public Instant getInstant(String path, Instant defaultval) { - Instant instant = getInstant(path); - return instant != null ? instant : defaultval; + @Override + public TimeValue getAsTime(String path, TimeValue defaultValue) { + return TimeValue.parseTimeValue(getString(path), defaultValue); } + @Override + public ByteSizeValue getAsBytesSize(String path, ByteSizeValue defaultValue) { + return ByteSizeValue.parseBytesSizeValue(getString(path), defaultValue); + } + + @Override public boolean set(String path, Object value) { return set(path, value, true); } @@ -210,7 +211,7 @@ public class Json { if (!(node instanceof ValueNode)) { return false; } - ValueNode txnode = (ValueNode) node; + ValueNode valueNode = (ValueNode) node; if (value instanceof String || value instanceof Integer || value instanceof Double @@ -220,12 +221,12 @@ public class Json { || value instanceof Short || value instanceof Character || value instanceof Byte) { - txnode.setValue(String.valueOf(value)); + valueNode.set(String.valueOf(value)); } else if (value instanceof Instant) { Instant instant = (Instant) value; - txnode.setValue(instant.toString()); + valueNode.set(instant.toString()); } else { - txnode.setValue(value == null ? null : value.toString()); + valueNode.set(value == null ? null : value.toString()); } return true; } @@ -234,10 +235,6 @@ public class Json { return set(path, instant.toString()); } - public Node getNode(String path) { - return path == null ? null : internalGetNode(root, path); - } - private Node internalGetNode(Node node, String path) { if (node == null || path.isEmpty()) { return node; @@ -257,7 +254,7 @@ public class Json { return null; } - public void makeNode(String path) { + private void makeNode(String path) { if (path != null) { internalMakeNode(root, path); } @@ -341,7 +338,7 @@ public class Json { } private String internalGet(String path) { - Node node = getNode(dequote(path)); + Node node = getNode(unquote(path)); return node instanceof ValueNode ? node.toString() : null; } @@ -352,7 +349,7 @@ public class Json { return new String[]{seg, rest}; } - private static String dequote(String string) { + private static String unquote(String string) { if (string.startsWith("'") && string.endsWith("'") || string.startsWith("\"") && string.endsWith("\"")) { return string.substring(1, string.length() - 1); diff --git a/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/JsonBuilder.java b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/JsonBuilder.java new file mode 100644 index 0000000..ed7e707 --- /dev/null +++ b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/JsonBuilder.java @@ -0,0 +1,274 @@ +package org.xbib.datastructures.json.tiny; + +import org.xbib.datastructures.api.Builder; +import org.xbib.datastructures.api.ByteSizeValue; +import org.xbib.datastructures.api.TimeValue; + +import java.io.IOException; +import java.io.StringWriter; +import java.io.UncheckedIOException; +import java.io.Writer; +import java.time.Instant; +import java.util.Collection; +import java.util.Map; +import java.util.Objects; + +public class JsonBuilder implements Builder { + + private final Writer writer; + + private State state; + + public JsonBuilder() { + this(new StringWriter()); + } + + public JsonBuilder(Writer writer) { + this.writer = writer; + this.state = new State(null, 0, Structure.MAP, true); + } + + @Override + public Builder beginCollection() throws IOException { + this.state = new State(state, state.level + 1, Structure.COLLECTION, true); + writer.write('['); + return this; + } + + @Override + public Builder endCollection() throws IOException { + if (state.structure != Structure.COLLECTION) { + throw new JsonException("no array to close"); + } + writer.write(']'); + this.state = state != null ? state.parent : null; + return this; + } + + @Override + public Builder beginMap() throws IOException { + this.state = new State(state, state.level + 1, Structure.MAP, true); + writer.write('{'); + return this; + } + + @Override + public Builder endMap() throws IOException { + if (state.structure != Structure.MAP) { + throw new JsonException("no object to close"); + } + writer.write('}'); + this.state = state != null ? state.parent : null; + return this; + } + + + @Override + public Builder buildMap(Map map) throws IOException { + Objects.requireNonNull(map); + beginMap(); + map.forEach((k, v) -> { + try { + buildKey(k); + buildValue(v); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + endMap(); + return this; + } + + @Override + public Builder buildCollection(Collection collection) throws IOException { + Objects.requireNonNull(collection); + beginCollection(); + collection.forEach(v -> { + try { + buildValue(v); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + endCollection(); + return this; + } + + @SuppressWarnings("unchecked") + @Override + public Builder buildValue(Object object) throws IOException { + if (state.structure == Structure.MAP) { + beginValue(object); + } else if (state.structure == Structure.COLLECTION) { + beginArrayValue(object); + } + if (object == null) { + buildNull(); + return this; + } else if (object instanceof Map) { + buildMap((Map) object); + } else if (object instanceof Collection) { + buildCollection((Collection) object); + } else if (object instanceof CharSequence) { + buildString((CharSequence) object, true); + } else if (object instanceof Boolean) { + buildBoolean((Boolean) object); + } else if (object instanceof Byte) { + buildNumber((byte) object); + } else if (object instanceof Integer) { + buildNumber((int) object); + } else if (object instanceof Long) { + buildNumber((long) object); + } else if (object instanceof Float) { + buildNumber((float) object); + } else if (object instanceof Double) { + buildNumber((double) object); + } else if (object instanceof Number) { + buildNumber((Number) object); + } else if (object instanceof Instant) { + buildInstant((Instant) object); + } else if (object instanceof ByteSizeValue) { + buildString(object.toString(), false); + } else if (object instanceof TimeValue) { + buildString(object.toString(), false); + } else { + throw new IllegalArgumentException("unable to write object class " + object.getClass()); + } + if (state.structure == Structure.MAP) { + endValue(object); + } else if (state.structure == Structure.COLLECTION) { + endArrayValue(object); + } + return this; + } + + @Override + public Builder buildKey(CharSequence string) throws IOException { + if (state.structure == Structure.MAP) { + beginKey(string != null ? string.toString() : null); + } + buildString(string, true); + if (state.structure == Structure.MAP) { + endKey(string != null ? string.toString() : null); + } + return this; + } + + @Override + public Builder buildNull() throws IOException { + if (state.structure == Structure.MAP) { + beginValue(null); + } else if (state.structure == Structure.COLLECTION) { + beginArrayValue(null); + } + buildString("null", false); + return this; + } + + @Override + public String build() { + return writer.toString(); + } + + private void beginKey(String k) throws IOException { + if (state.first) { + state.first = false; + } else { + writer.write(","); + } + } + + private void endKey(String k) throws IOException { + writer.write(":"); + } + + private void beginValue(Object v) { + } + + private void endValue(Object v) { + } + + private void beginArrayValue(Object v) throws IOException { + if (state.first) { + state.first = false; + } else { + writer.write(","); + } + } + + private void endArrayValue(Object v) { + } + + private void buildBoolean(boolean bool) throws IOException { + buildString(bool ? "true" : "false", false); + } + + private void buildNumber(Number number) throws IOException { + buildString(number != null ? number.toString() : null, false); + } + + private void buildInstant(Instant instant) throws IOException{ + buildString(instant.toString(), true); + } + + private void buildString(CharSequence string, boolean escape) throws IOException { + writer.write(escape ? escapeString(string) : string.toString()); + } + + private String escapeString(CharSequence string) { + StringBuilder sb = new StringBuilder(); + sb.append('"'); + int start = 0; + int l = string.length(); + for (int i = 0; i < l; i++) { + char c = string.charAt(i); + if (c == '"' || c < 32 || c >= 127 || c == '\\') { + if (start < i) { + sb.append(string.toString(), start, i - start); + } + start = i; + sb.append(escapeCharacter(c)); + } + } + if (start < l) { + sb.append(string.toString(), start, l - start); + } + sb.append('"'); + return sb.toString(); + } + + private static String escapeCharacter(char c) { + switch (c) { + case '\n': + return "\\n"; + case '\r': + return "\\r"; + case '\t': + return "\\t"; + case '\\': + return "\\\\"; + case '\'': + return "\\'"; + case '\"': + return "\\\""; + } + String hex = Integer.toHexString(c); + return "\\u0000".substring(0, 6 - hex.length()) + hex; + } + + private enum Structure { MAP, COLLECTION }; + + private static class State { + State parent; + int level; + Structure structure; + boolean first; + + State(State parent, int level, Structure structure, boolean first) { + this.parent = parent; + this.level = level; + this.structure = structure; + this.first = first; + } + } +} diff --git a/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/JsonException.java b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/JsonException.java new file mode 100644 index 0000000..1597358 --- /dev/null +++ b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/JsonException.java @@ -0,0 +1,13 @@ +package org.xbib.datastructures.json.tiny; + +@SuppressWarnings("serial") +public class JsonException extends RuntimeException { + + public JsonException(String message) { + super(message); + } + + public JsonException(Exception exception) { + super(exception); + } +} diff --git a/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/JsonGenerator.java b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/JsonGenerator.java new file mode 100644 index 0000000..607123a --- /dev/null +++ b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/JsonGenerator.java @@ -0,0 +1,52 @@ +package org.xbib.datastructures.json.tiny; + +import org.xbib.datastructures.api.Generator; +import org.xbib.datastructures.api.Node; + +import java.io.IOException; +import java.io.Writer; +import java.util.Map; + +public class JsonGenerator implements Generator { + + private final Node root; + + private JsonBuilder builder; + + public JsonGenerator(Node root) { + this.root = root; + } + + @Override + public void generate(Writer writer) throws IOException { + this.builder = new JsonBuilder(writer); + try (writer) { + if (root != null) { + internalWrite(root); + } + } + } + + private void internalWrite(Node curnode) throws IOException { + if (curnode instanceof ValueNode) { + ValueNode valueNode = (ValueNode) curnode; + Object object = valueNode.get(); + builder.buildValue(object); + } else if (curnode instanceof MapNode) { + MapNode mapNode = (MapNode) curnode; + builder.beginMap(); + for (Map.Entry> e : mapNode.get().entrySet()) { + builder.buildKey(e.getKey()); + internalWrite(e.getValue()); + } + builder.endMap(); + } else if (curnode instanceof ListNode) { + ListNode listNode = (ListNode) curnode; + builder.beginCollection(); + for (Node node : listNode.get()) { + internalWrite(node); + } + builder.endCollection(); + } + } +} diff --git a/datastructures-json/src/main/java/org/xbib/datastructures/json/JsonListener.java b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/JsonListener.java similarity index 76% rename from datastructures-json/src/main/java/org/xbib/datastructures/json/JsonListener.java rename to datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/JsonListener.java index a5c1ad9..f11bd7c 100644 --- a/datastructures-json/src/main/java/org/xbib/datastructures/json/JsonListener.java +++ b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/JsonListener.java @@ -1,4 +1,4 @@ -package org.xbib.datastructures.json; +package org.xbib.datastructures.json.tiny; public interface JsonListener { @@ -20,9 +20,9 @@ public interface JsonListener { void onDouble(Double value); - void beginList(); + void beginCollection(); - void endList(); + void endCollection(); void beginMap(); diff --git a/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/JsonResult.java b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/JsonResult.java new file mode 100644 index 0000000..9900e5e --- /dev/null +++ b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/JsonResult.java @@ -0,0 +1,8 @@ +package org.xbib.datastructures.json.tiny; + +import org.xbib.datastructures.api.Node; + +public interface JsonResult extends JsonListener { + + Node getResult(); +} diff --git a/datastructures-json/src/main/java/org/xbib/datastructures/json/KeyNode.java b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/KeyNode.java similarity index 63% rename from datastructures-json/src/main/java/org/xbib/datastructures/json/KeyNode.java rename to datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/KeyNode.java index 897b992..3eb6579 100644 --- a/datastructures-json/src/main/java/org/xbib/datastructures/json/KeyNode.java +++ b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/KeyNode.java @@ -1,4 +1,6 @@ -package org.xbib.datastructures.json; +package org.xbib.datastructures.json.tiny; + +import org.xbib.datastructures.api.Node; public class KeyNode implements Node { @@ -12,6 +14,11 @@ public class KeyNode implements Node { this.value = value; } + @Override + public int getDepth() { + return 0; + } + @Override public CharSequence get() { return value; @@ -19,6 +26,6 @@ public class KeyNode implements Node { @Override public String toString() { - return value.toString(); + return value != null ? value.toString() : null; } } diff --git a/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/ListNode.java b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/ListNode.java new file mode 100644 index 0000000..b617fd2 --- /dev/null +++ b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/ListNode.java @@ -0,0 +1,26 @@ +package org.xbib.datastructures.json.tiny; + +import org.xbib.datastructures.api.Node; +import org.xbib.datastructures.tiny.TinyList; +import java.util.List; + +public class ListNode extends TinyList.Builder> implements org.xbib.datastructures.api.ListNode { + + public boolean has(int i) { + return i >= 0 && i < size() && get(i) == null; + } + + public boolean has(Node node) { + return contains(node); + } + + @Override + public int getDepth() { + return 0; + } + + @Override + public List> get() { + return this; + } +} diff --git a/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/MapNode.java b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/MapNode.java new file mode 100644 index 0000000..edb59b3 --- /dev/null +++ b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/MapNode.java @@ -0,0 +1,22 @@ +package org.xbib.datastructures.json.tiny; + +import org.xbib.datastructures.api.Node; +import org.xbib.datastructures.tiny.TinyMap; +import java.util.Map; + +public class MapNode extends TinyMap.Builder> implements org.xbib.datastructures.api.MapNode { + + public boolean has(String name) { + return containsKey(name); + } + + @Override + public int getDepth() { + return 0; + } + + @Override + public Map> get() { + return this; + } +} diff --git a/datastructures-json/src/main/java/org/xbib/datastructures/json/StandardJsonListener.java b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/StandardJsonListener.java similarity index 91% rename from datastructures-json/src/main/java/org/xbib/datastructures/json/StandardJsonListener.java rename to datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/StandardJsonListener.java index e6a7ca9..5eeb537 100644 --- a/datastructures-json/src/main/java/org/xbib/datastructures/json/StandardJsonListener.java +++ b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/StandardJsonListener.java @@ -1,9 +1,11 @@ -package org.xbib.datastructures.json; +package org.xbib.datastructures.json.tiny; + +import org.xbib.datastructures.api.Node; import java.util.Deque; import java.util.LinkedList; -public class StandardJsonListener implements JsonDeserializer { +public class StandardJsonListener implements JsonResult { private Node node; @@ -15,7 +17,6 @@ public class StandardJsonListener implements JsonDeserializer { private final ValueNode FALSE_NODE = new ValueNode(Boolean.FALSE); - @Override public Deque> getStack() { return stack; } @@ -69,12 +70,12 @@ public class StandardJsonListener implements JsonDeserializer { } @Override - public void beginList() { + public void beginCollection() { stack.push(new StandardListNode()); } @Override - public void endList() { + public void endCollection() { node = stack.pop(); tryAppend(node); } diff --git a/datastructures-json/src/main/java/org/xbib/datastructures/json/StandardListNode.java b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/StandardListNode.java similarity index 73% rename from datastructures-json/src/main/java/org/xbib/datastructures/json/StandardListNode.java rename to datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/StandardListNode.java index b379419..e581075 100644 --- a/datastructures-json/src/main/java/org/xbib/datastructures/json/StandardListNode.java +++ b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/StandardListNode.java @@ -1,4 +1,6 @@ -package org.xbib.datastructures.json; +package org.xbib.datastructures.json.tiny; + +import org.xbib.datastructures.api.Node; import java.util.ArrayList; import java.util.List; @@ -14,6 +16,11 @@ public class StandardListNode extends ArrayList implements Node get() { return this; diff --git a/datastructures-json/src/main/java/org/xbib/datastructures/json/StandardMapNode.java b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/StandardMapNode.java similarity index 70% rename from datastructures-json/src/main/java/org/xbib/datastructures/json/StandardMapNode.java rename to datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/StandardMapNode.java index fdfc19b..4e37b6c 100644 --- a/datastructures-json/src/main/java/org/xbib/datastructures/json/StandardMapNode.java +++ b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/StandardMapNode.java @@ -1,4 +1,6 @@ -package org.xbib.datastructures.json; +package org.xbib.datastructures.json.tiny; + +import org.xbib.datastructures.api.Node; import java.util.LinkedHashMap; import java.util.Map; @@ -10,6 +12,11 @@ public class StandardMapNode extends LinkedHashMap impleme return containsKey(name); } + @Override + public int getDepth() { + return 0; + } + @Override public Map get() { return this; diff --git a/datastructures-json/src/main/java/org/xbib/datastructures/json/StreamParser.java b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/StreamParser.java similarity index 94% rename from datastructures-json/src/main/java/org/xbib/datastructures/json/StreamParser.java rename to datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/StreamParser.java index 8039831..b1eff71 100644 --- a/datastructures-json/src/main/java/org/xbib/datastructures/json/StreamParser.java +++ b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/StreamParser.java @@ -1,14 +1,15 @@ -package org.xbib.datastructures.json; +package org.xbib.datastructures.json.tiny; + +import org.xbib.datastructures.api.Node; +import org.xbib.datastructures.api.Parser; import java.io.IOException; import java.io.Reader; import java.util.Objects; -import java.util.logging.Level; -import java.util.logging.Logger; -public class StreamParser { +public class StreamParser implements Parser { - private final JsonDeserializer listener; + private final JsonResult listener; private Reader reader; @@ -18,15 +19,12 @@ public class StreamParser { this(new TinyJsonListener()); } - public StreamParser(JsonDeserializer listener) { + public StreamParser(JsonResult listener) { this.listener = listener; } - public Node getNode() { - return listener.getResult(); - } - - public void parse(Reader reader) throws JsonException, IOException { + @Override + public Node parse(Reader reader) throws IOException { Objects.requireNonNull(reader); Objects.requireNonNull(listener); this.reader = reader; @@ -39,6 +37,7 @@ public class StreamParser { throw new JsonException("malformed json: " + ch); } listener.end(); + return listener.getResult(); } private void parseValue() throws IOException, JsonException { @@ -189,12 +188,12 @@ public class StreamParser { private void parseList() throws IOException { int count = 0; - listener.beginList(); + listener.beginCollection(); ch = reader.read(); while (true) { skipWhitespace(); if (ch == ']') { - listener.endList(); + listener.endCollection(); ch = reader.read(); return; } @@ -270,9 +269,9 @@ public class StreamParser { ch = reader.read(); } - private void expectChar(int expected) throws JsonException { + private void expectChar(char expected) throws JsonException { if (ch != expected) { - throw new JsonException("expected char " + (char)expected + " but got " + (char)ch + " " + listener.getStack()); + throw new JsonException("expected char " + (char)expected + " but got " + (char)ch); } } diff --git a/datastructures-json/src/main/java/org/xbib/datastructures/json/StringParser.java b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/StringParser.java similarity index 89% rename from datastructures-json/src/main/java/org/xbib/datastructures/json/StringParser.java rename to datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/StringParser.java index f15099b..6f9284c 100644 --- a/datastructures-json/src/main/java/org/xbib/datastructures/json/StringParser.java +++ b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/StringParser.java @@ -1,4 +1,6 @@ -package org.xbib.datastructures.json; +package org.xbib.datastructures.json.tiny; + +import org.xbib.datastructures.api.Node; import java.util.Objects; @@ -22,7 +24,7 @@ public class StringParser { private static final char COLON = ':'; - private final JsonDeserializer deserializer; + private final JsonResult listener; private String input; @@ -30,16 +32,16 @@ public class StringParser { private char ch; - public StringParser(JsonDeserializer deserializer) { - this.deserializer = deserializer; + public StringParser(JsonResult listener) { + this.listener = listener; } public void parse(String input) throws JsonException { Objects.requireNonNull(input); - Objects.requireNonNull(deserializer); + Objects.requireNonNull(listener); this.input = input; this.i = 0; - deserializer.begin(); + listener.begin(); ch = next(); skipWhitespace(); parseValue(); @@ -47,11 +49,11 @@ public class StringParser { if (ch != EOS) { throw new JsonException("malformed json: " + ch); } - deserializer.end(); + listener.end(); } public Node getNode() { - return deserializer.getResult(); + return listener.getResult(); } private void parseValue() throws JsonException { @@ -139,9 +141,9 @@ public class StringParser { throw new JsonException("isolated minus"); } if (dot || exponent) { - deserializer.onDouble(FastDoubleParser.parseDouble(input.substring(start, i - 1))); + listener.onDouble(FastDoubleParser.parseDouble(input.substring(start, i - 1))); } else { - deserializer.onLong(Long.parseLong(input.substring(start, i - 1))); + listener.onLong(Long.parseLong(input.substring(start, i - 1))); } } @@ -153,15 +155,15 @@ public class StringParser { if (escaped) { CharSequence s = unescape(input.substring(start, i - 1)); if (isKey) { - deserializer.onKey(s); + listener.onKey(s); } else { - deserializer.onValue(s); + listener.onValue(s); } } else { if (isKey) { - deserializer.onKey(input.substring(start, i - 1)); + listener.onKey(input.substring(start, i - 1)); } else { - deserializer.onValue(input.substring(start, i - 1)); + listener.onValue(input.substring(start, i - 1)); } } ch = next(); @@ -189,12 +191,12 @@ public class StringParser { private void parseList() { int count = 0; - deserializer.beginList(); + listener.beginCollection(); ch = next(); while (true) { skipWhitespace(); if (ch == CLOSE_LIST) { - deserializer.endList(); + listener.endCollection(); ch = next(); return; } @@ -210,12 +212,12 @@ public class StringParser { private void parseMap() { int count = 0; - deserializer.beginMap(); + listener.beginMap(); ch = next(); while (true) { skipWhitespace(); if (ch == CLOSE_MAP) { - deserializer.endMap(); + listener.endMap(); ch = next(); return; } @@ -243,7 +245,7 @@ public class StringParser { expectChar('l'); ch = next(); expectChar('l'); - deserializer.onNull(); + listener.onNull(); ch = next(); } @@ -254,7 +256,7 @@ public class StringParser { expectChar('u'); ch = next(); expectChar('e'); - deserializer.onTrue(); + listener.onTrue(); ch = next(); } @@ -267,13 +269,13 @@ public class StringParser { expectChar('s'); ch = next(); expectChar('e'); - deserializer.onFalse(); + listener.onFalse(); ch = next(); } private void expectChar(char expected) throws JsonException { if (ch != expected) { - throw new JsonException("expected char " + expected + " but got " + ch + " " + deserializer.getStack()); + throw new JsonException("expected char " + expected + " but got " + ch); } } diff --git a/datastructures-json/src/main/java/org/xbib/datastructures/json/TinyJsonListener.java b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/TinyJsonListener.java similarity index 91% rename from datastructures-json/src/main/java/org/xbib/datastructures/json/TinyJsonListener.java rename to datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/TinyJsonListener.java index 2190a9f..39b6d6a 100644 --- a/datastructures-json/src/main/java/org/xbib/datastructures/json/TinyJsonListener.java +++ b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/TinyJsonListener.java @@ -1,9 +1,11 @@ -package org.xbib.datastructures.json; +package org.xbib.datastructures.json.tiny; + +import org.xbib.datastructures.api.Node; import java.util.Deque; import java.util.LinkedList; -public class TinyJsonListener implements JsonDeserializer { +public class TinyJsonListener implements JsonResult { private Node node; @@ -15,7 +17,6 @@ public class TinyJsonListener implements JsonDeserializer { private final ValueNode FALSE_NODE = new ValueNode(Boolean.FALSE); - @Override public Deque> getStack() { return stack; } @@ -70,12 +71,12 @@ public class TinyJsonListener implements JsonDeserializer { } @Override - public void beginList() { + public void beginCollection() { stack.push(new ListNode()); } @Override - public void endList() { + public void endCollection() { node = stack.pop(); tryAppend(node); } diff --git a/datastructures-json/src/main/java/org/xbib/datastructures/json/ValueNode.java b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/ValueNode.java similarity index 57% rename from datastructures-json/src/main/java/org/xbib/datastructures/json/ValueNode.java rename to datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/ValueNode.java index 85b28e2..158cfba 100644 --- a/datastructures-json/src/main/java/org/xbib/datastructures/json/ValueNode.java +++ b/datastructures-json-tiny/src/main/java/org/xbib/datastructures/json/tiny/ValueNode.java @@ -1,6 +1,6 @@ -package org.xbib.datastructures.json; +package org.xbib.datastructures.json.tiny; -public class ValueNode implements Node { +public class ValueNode implements org.xbib.datastructures.api.ValueNode { private Object value; @@ -8,7 +8,13 @@ public class ValueNode implements Node { this.value = value; } - public void setValue(Object value) { + @Override + public int getDepth() { + return 0; + } + + @Override + public void set(Object value) { this.value = value; } diff --git a/datastructures-json/src/test/java/org/xbib/datastructures/json/test/DebugJsonListener.java b/datastructures-json-tiny/src/test/java/org/xbib/datastructures/json/tiny/test/DebugJsonListener.java similarity index 77% rename from datastructures-json/src/test/java/org/xbib/datastructures/json/test/DebugJsonListener.java rename to datastructures-json-tiny/src/test/java/org/xbib/datastructures/json/tiny/test/DebugJsonListener.java index 7270eae..11be3b6 100644 --- a/datastructures-json/src/test/java/org/xbib/datastructures/json/test/DebugJsonListener.java +++ b/datastructures-json-tiny/src/test/java/org/xbib/datastructures/json/tiny/test/DebugJsonListener.java @@ -1,11 +1,11 @@ -package org.xbib.datastructures.json.test; +package org.xbib.datastructures.json.tiny.test; -import org.xbib.datastructures.json.TinyJsonListener; -import org.xbib.datastructures.json.KeyNode; -import org.xbib.datastructures.json.ListNode; -import org.xbib.datastructures.json.MapNode; -import org.xbib.datastructures.json.Node; -import org.xbib.datastructures.json.ValueNode; +import org.xbib.datastructures.api.Node; +import org.xbib.datastructures.json.tiny.TinyJsonListener; +import org.xbib.datastructures.json.tiny.KeyNode; +import org.xbib.datastructures.json.tiny.ListNode; +import org.xbib.datastructures.json.tiny.MapNode; +import org.xbib.datastructures.json.tiny.ValueNode; import java.util.Stack; import java.util.logging.Logger; @@ -19,9 +19,9 @@ public class DebugJsonListener extends TinyJsonListener { private final ValueNode NULL_NODE = new ValueNode(null); - private final ValueNode TRUE_NODE = new ValueNode(Boolean.TRUE); + private final ValueNode TRUE_NODE = new ValueNode(Boolean.TRUE.toString()); - private final ValueNode FALSE_NODE = new ValueNode(Boolean.FALSE); + private final ValueNode FALSE_NODE = new ValueNode(Boolean.FALSE.toString()); public Node getResult() { return node; @@ -49,26 +49,26 @@ public class DebugJsonListener extends TinyJsonListener { @Override public void onValue(CharSequence value) { - valueNode(new ValueNode(value)); + valueNode(new ValueNode(value.toString())); } @Override public void onLong(Long value) { - valueNode(new ValueNode(value)); + valueNode(new ValueNode(value.toString())); } @Override public void onDouble(Double value) { - valueNode(new ValueNode(value)); + valueNode(new ValueNode(value.toString())); } @Override - public void beginList() { + public void beginCollection() { stack.push(new ListNode()); } @Override - public void endList() { + public void endCollection() { node = stack.pop(); tryAppend(node); } diff --git a/datastructures-json-tiny/src/test/java/org/xbib/datastructures/json/tiny/test/JsonBuilderTest.java b/datastructures-json-tiny/src/test/java/org/xbib/datastructures/json/tiny/test/JsonBuilderTest.java new file mode 100644 index 0000000..1cdc9ed --- /dev/null +++ b/datastructures-json-tiny/src/test/java/org/xbib/datastructures/json/tiny/test/JsonBuilderTest.java @@ -0,0 +1,111 @@ +package org.xbib.datastructures.json.tiny.test; + +import org.junit.jupiter.api.Test; +import org.xbib.datastructures.json.tiny.JsonBuilder; +import org.xbib.datastructures.json.tiny.StreamParser; + +import java.io.IOException; +import java.io.StringReader; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class JsonBuilderTest { + + @Test + public void testObjectStrFromMap() throws IOException { + JsonBuilder jsonBuilder = new JsonBuilder(); + jsonBuilder.buildMap(Map.of("a", "b")); + assertEquals("{\"a\":\"b\"}", jsonBuilder.build()); + } + + @Test + public void testObjectNumFromMap() throws IOException { + JsonBuilder jsonBuilder = new JsonBuilder(); + jsonBuilder.buildMap(Map.of("a", 2)); + assertEquals("{\"a\":2}", jsonBuilder.build()); + } + + @Test + public void testArrayFromCollection() throws IOException { + JsonBuilder jsonBuilder = new JsonBuilder(); + jsonBuilder.buildCollection(List.of("a", "b", "c")); + assertEquals("[\"a\",\"b\",\"c\"]", jsonBuilder.build()); + } + + @Test + public void testArrayStr() throws IOException { + JsonBuilder jsonBuilder = new JsonBuilder(); + jsonBuilder.beginCollection() + .buildValue("a") + .buildValue("b") + .buildValue("c") + .endCollection(); + String s = jsonBuilder.build(); + assertEquals("[\"a\",\"b\",\"c\"]", s); + StreamParser streamParser = new StreamParser(); + assertEquals("[a, b, c]", streamParser.parse(new StringReader(s)).get().toString()); + } + + @Test + public void testArrayNum() throws IOException { + JsonBuilder jsonBuilder = new JsonBuilder(); + jsonBuilder.beginCollection() + .buildValue(1) + .buildValue(2) + .buildValue(3) + .endCollection(); + String s = jsonBuilder.build(); + assertEquals("[1,2,3]", s); + StreamParser streamParser = new StreamParser(); + assertEquals("[1, 2, 3]", streamParser.parse(new StringReader(s)).get().toString()); + } + + @Test + public void testArrayFloat() throws IOException { + JsonBuilder jsonBuilder = new JsonBuilder(); + jsonBuilder.beginCollection() + .buildValue(1.0) + .buildValue(2.0) + .buildValue(3.0) + .endCollection(); + String s = jsonBuilder.build(); + assertEquals("[1.0,2.0,3.0]", s); + StreamParser streamParser = new StreamParser(); + assertEquals("[1.0, 2.0, 3.0]", streamParser.parse(new StringReader(s)).get().toString()); + } + + @Test + public void testObjectStr() throws IOException { + JsonBuilder jsonBuilder = new JsonBuilder(); + jsonBuilder.beginMap() + .buildKey("a") + .buildValue("b") + .endMap(); + String s = jsonBuilder.build(); + assertEquals("{\"a\":\"b\"}", s); + StreamParser streamParser = new StreamParser(); + assertEquals("{a=b}", streamParser.parse(new StringReader(s)).get().toString()); + } + + @Test + public void testObjectNum() throws IOException { + JsonBuilder jsonBuilder = new JsonBuilder(); + jsonBuilder.beginMap() + .buildKey("a") + .buildValue(1) + .endMap(); + String s = jsonBuilder.build(); + assertEquals("{\"a\":1}", s); + StreamParser streamParser = new StreamParser(); + assertEquals("{a=1}", streamParser.parse(new StringReader(s)).get().toString()); + } + + @Test + public void testBuild() throws Exception { + JsonBuilder jsonBuilder = new JsonBuilder(); + jsonBuilder.buildMap(Map.of("a", "b")); + assertEquals("{\"a\":\"b\"}", jsonBuilder.build()); + } +} diff --git a/datastructures-json-tiny/src/test/java/org/xbib/datastructures/json/tiny/test/LargeFileTest.java b/datastructures-json-tiny/src/test/java/org/xbib/datastructures/json/tiny/test/LargeFileTest.java new file mode 100644 index 0000000..fade4c6 --- /dev/null +++ b/datastructures-json-tiny/src/test/java/org/xbib/datastructures/json/tiny/test/LargeFileTest.java @@ -0,0 +1,61 @@ +package org.xbib.datastructures.json.tiny.test; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.xbib.datastructures.json.tiny.TinyJsonListener; +import org.xbib.datastructures.json.tiny.StreamParser; +import org.xbib.datastructures.json.tiny.StringParser; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import java.util.zip.GZIPInputStream; + +public class LargeFileTest { + + @Test + public void testStringParser() throws IOException { + InputStream inputStream = ParserTest.class.getResourceAsStream("/org/xbib/datastructures/json/tiny/test/large-file.json"); + if (inputStream != null) { + try (inputStream) { + byte[] b = inputStream.readAllBytes(); + String string = new String(b, StandardCharsets.UTF_8); + StringParser stringParser = new StringParser(new TinyJsonListener()); + stringParser.parse(string); + stringParser.getNode().get(); + stringParser.parse(string); + stringParser.getNode().get(); + } + } + } + + @Test + public void testStreamParser() throws IOException { + InputStream inputStream = ParserTest.class.getResourceAsStream("/org/xbib/datastructures/json/tiny/test/test.json"); + if (inputStream != null) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { + StreamParser streamParser = new StreamParser(new TinyJsonListener()); + Logger.getLogger("").log(Level.INFO, streamParser.parse(reader).get().toString()); + } + } + } + + @Disabled + @Test + public void largeFileTest() throws IOException { + InputStream inputStream = new GZIPInputStream(Files.newInputStream(Paths.get("/Users/joerg/jam.jsonlines.gz"))); + try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { + for (String line : bufferedReader.lines().collect(Collectors.toList())) { + StreamParser streamParser = new StreamParser(); + streamParser.parse(new StringReader(line)); + } + } + } +} diff --git a/datastructures-json/src/test/java/org/xbib/datastructures/json/test/ParserTest.java b/datastructures-json-tiny/src/test/java/org/xbib/datastructures/json/tiny/test/ParserTest.java similarity index 73% rename from datastructures-json/src/test/java/org/xbib/datastructures/json/test/ParserTest.java rename to datastructures-json-tiny/src/test/java/org/xbib/datastructures/json/tiny/test/ParserTest.java index 1aff9e3..af84775 100644 --- a/datastructures-json/src/test/java/org/xbib/datastructures/json/test/ParserTest.java +++ b/datastructures-json-tiny/src/test/java/org/xbib/datastructures/json/tiny/test/ParserTest.java @@ -1,9 +1,9 @@ -package org.xbib.datastructures.json.test; +package org.xbib.datastructures.json.tiny.test; import org.junit.jupiter.api.Test; -import org.xbib.datastructures.json.TinyJsonListener; -import org.xbib.datastructures.json.StreamParser; -import org.xbib.datastructures.json.StringParser; +import org.xbib.datastructures.json.tiny.TinyJsonListener; +import org.xbib.datastructures.json.tiny.StreamParser; +import org.xbib.datastructures.json.tiny.StringParser; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -16,7 +16,7 @@ public class ParserTest { @Test public void testStringParser() throws IOException { - try (InputStream inputStream = ParserTest.class.getResourceAsStream("/org/xbib/datastructures/json/test/test.json")) { + try (InputStream inputStream = ParserTest.class.getResourceAsStream("/org/xbib/datastructures/json/tiny/test/test.json")) { byte [] b = inputStream.readAllBytes(); String string = new String(b, StandardCharsets.UTF_8); StringParser stringParser = new StringParser(new TinyJsonListener()); @@ -29,10 +29,9 @@ public class ParserTest { @Test public void testStreamParser() throws IOException { - try (BufferedReader reader = new BufferedReader(new InputStreamReader(ParserTest.class.getResourceAsStream("/org/xbib/datastructures/json/test/test.json")))) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(ParserTest.class.getResourceAsStream("/org/xbib/datastructures/json/tiny/test/test.json")))) { StreamParser streamParser = new StreamParser(new TinyJsonListener()); - streamParser.parse(reader); - Logger.getLogger("").log(Level.INFO, streamParser.getNode().get().toString()); + Logger.getLogger("").log(Level.INFO, streamParser.parse(reader).get().toString()); } } } diff --git a/datastructures-json/src/test/resources/org/xbib/datastructures/json/test/large-file.json b/datastructures-json-tiny/src/test/resources/org/xbib/datastructures/json/tiny/test/large-file.json similarity index 100% rename from datastructures-json/src/test/resources/org/xbib/datastructures/json/test/large-file.json rename to datastructures-json-tiny/src/test/resources/org/xbib/datastructures/json/tiny/test/large-file.json diff --git a/datastructures-json/src/test/resources/org/xbib/datastructures/json/test/test.json b/datastructures-json-tiny/src/test/resources/org/xbib/datastructures/json/tiny/test/test.json similarity index 100% rename from datastructures-json/src/test/resources/org/xbib/datastructures/json/test/test.json rename to datastructures-json-tiny/src/test/resources/org/xbib/datastructures/json/tiny/test/test.json diff --git a/datastructures-json/src/main/java/module-info.java b/datastructures-json/src/main/java/module-info.java deleted file mode 100644 index 58d662d..0000000 --- a/datastructures-json/src/main/java/module-info.java +++ /dev/null @@ -1,5 +0,0 @@ -module org.xbib.datastructures.json { - exports org.xbib.datastructures.json; - requires org.xbib.datastructures.tiny; - requires java.logging; -} diff --git a/datastructures-json/src/main/java/org/xbib/datastructures/json/Generator.java b/datastructures-json/src/main/java/org/xbib/datastructures/json/Generator.java deleted file mode 100644 index cec2bb6..0000000 --- a/datastructures-json/src/main/java/org/xbib/datastructures/json/Generator.java +++ /dev/null @@ -1,337 +0,0 @@ -package org.xbib.datastructures.json; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.io.Writer; -import java.util.Collection; -import java.util.Map; -import java.util.Objects; - -public class Generator { - - private final Node node; - - private final Writer writer; - - private JsonContext context; - - public Generator(Node node, Writer writer) { - this.node = node; - this.writer = writer; - } - - public void generate() throws IOException { - } - - public Generator beginArray() throws IOException { - if (context != null) { - beginStructure(); - } - context = new JsonContext(context, JsonContext.StartArray); - writer.write('['); - return this; - } - - public Generator endArray() throws IOException { - writer.write(']'); - endStructure(); - return this; - } - - public Generator beginObject() throws IOException { - if (context != null) { - beginStructure(); - } - context = new JsonContext(context, JsonContext.StartObject); - writer.write('{'); - return this; - } - - public Generator endObject() throws IOException { - writer.write('}'); - endStructure(); - return this; - } - - public Generator writeString(String string) throws IOException { - writeString(string, true); - return this; - } - - public Generator writeNull() throws IOException { - writeString("null", false); - return this; - } - - public Generator writeObject(Object object) throws IOException { - if (object == null) { - writeNull(); - return this; - } - if (object instanceof String) { - writeString((String) object); - return this; - } - if (object instanceof Boolean) { - writeBoolean((Boolean) object); - return this; - } - if (object instanceof Byte) { - writeNumber((byte) object); - return this; - } - if (object instanceof Integer) { - writeNumber((int) object); - return this; - } - if (object instanceof Long) { - writeNumber((long) object); - return this; - } - if (object instanceof Float) { - writeNumber((float) object); - return this; - } - if (object instanceof Double) { - writeNumber((double) object); - return this; - } - if (object instanceof Number) { - writeNumber((Number) object); - return this; - } - throw new IllegalArgumentException("unable to write object class " + object.getClass()); - } - - public Generator writeBoolean(boolean bool) throws IOException { - writeString(bool ? "true" : "false", false); - return this; - } - - public Generator writeNumber(byte number) throws IOException { - writeString(Byte.toString(number), false); - return this; - } - - public Generator writeNumber(int number) throws IOException { - writeString(Integer.toString(number), false); - return this; - } - - public Generator writeNumber(long number) throws IOException { - writeString(Long.toString(number), false); - return this; - } - - public Generator writeNumber(float number) throws IOException { - writeString(Float.toString(number), false); - return this; - } - - public Generator writeNumber(double number) throws IOException { - writeString(Double.toString(number), false); - return this; - } - - public Generator writeNumber(Number number) throws IOException { - writeString(number != null ? number.toString() : null); - return this; - } - - public Generator write(Map map) throws IOException { - Objects.requireNonNull(map); - beginObject(); - map.forEach((k, v) -> { - try { - writeString(k); - writeObject(v); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - }); - endObject(); - return this; - } - - public Generator write(Collection collection) throws IOException { - Objects.requireNonNull(collection); - beginArray(); - collection.forEach(v -> { - try { - writeObject(v); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - }); - endArray(); - return this; - } - - private void writeString(String string, boolean escape) throws IOException { - beforeWrite(); - if (escape) { - if (string == null) { - writeNull(); - } else { - string(string); - } - } else { - writer.write(string); - } - afterWrite(); - } - - private void beginStructure() throws IOException { - final int state = context.state; - switch (context.state) { - case JsonContext.PropertyKey: - writer.write(':'); - break; - case JsonContext.ArrayValue: - writer.write(','); - break; - case JsonContext.StartObject: - break; - case JsonContext.StartArray: - break; - default: - throw new JsonException("illegal state : " + state); - } - } - - private void endStructure() { - context = context.parent; - if (context == null) { - return; - } - int newState = -1; - switch (context.state) { - case JsonContext.PropertyKey: - newState = JsonContext.PropertyValue; - break; - case JsonContext.StartArray: - newState = JsonContext.ArrayValue; - break; - case JsonContext.ArrayValue: - break; - case JsonContext.StartObject: - newState = JsonContext.PropertyKey; - break; - default: - break; - } - if (newState != -1) { - context.state = newState; - } - } - - private void beforeWrite() throws IOException { - if (context == null) { - return; - } - switch (context.state) { - case JsonContext.StartObject: - case JsonContext.StartArray: - break; - case JsonContext.PropertyKey: - writer.write(':'); - break; - case JsonContext.PropertyValue: - writer.write(','); - break; - case JsonContext.ArrayValue: - writer.write(','); - break; - default: - break; - } - } - - private void afterWrite() { - if (context == null) { - return; - } - int newState = -1; - switch (context.state) { - case JsonContext.PropertyKey: - newState = JsonContext.PropertyValue; - break; - case JsonContext.StartObject: - case JsonContext.PropertyValue: - newState = JsonContext.PropertyKey; - break; - case JsonContext.StartArray: - newState = JsonContext.ArrayValue; - break; - case JsonContext.ArrayValue: - break; - default: - break; - } - if (newState != -1) { - context.state = newState; - } - } - - private void string(String string) throws IOException { - writer.write('"'); - int start = 0; - int l = string.length(); - for (int i = 0; i < l; i++) { - char c = string.charAt(i); - if (c == '"' || c < 32 || c >= 127 || c == '\\') { - if (start < i) { - writer.write(string, start, i - start); - } - start = i; - writer.write(escapeCharacter(c)); - } - } - if (start < l) { - writer.write(string, start, l - start); - } - writer.write('"'); - } - - private static String escapeCharacter(char c) { - switch (c) { - case '\n': - return "\\n"; - case '\r': - return "\\r"; - case '\t': - return "\\t"; - case '\\': - return "\\\\"; - case '\'': - return "\\'"; - case '\"': - return "\\\""; - } - String hex = Integer.toHexString(c); - return "\\u0000".substring(0, 6 - hex.length()) + hex; - } - - private static class JsonContext { - - final static int StartObject = 1001; - final static int PropertyKey = 1002; - final static int PropertyValue = 1003; - final static int StartArray = 1004; - final static int ArrayValue = 1005; - - private final JsonContext parent; - - private int state; - - public JsonContext(JsonContext parent, int state) { - this.parent = parent; - this.state = state; - } - } - - @Override - public String toString() { - return writer.toString(); - } -} diff --git a/datastructures-json/src/main/java/org/xbib/datastructures/json/JsonDeserializer.java b/datastructures-json/src/main/java/org/xbib/datastructures/json/JsonDeserializer.java deleted file mode 100644 index 7748b79..0000000 --- a/datastructures-json/src/main/java/org/xbib/datastructures/json/JsonDeserializer.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.xbib.datastructures.json; - -import java.util.Deque; - -public interface JsonDeserializer extends JsonListener { - - Deque> getStack(); - - Node getResult(); - -} diff --git a/datastructures-json/src/main/java/org/xbib/datastructures/json/JsonException.java b/datastructures-json/src/main/java/org/xbib/datastructures/json/JsonException.java deleted file mode 100644 index 7512037..0000000 --- a/datastructures-json/src/main/java/org/xbib/datastructures/json/JsonException.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.xbib.datastructures.json; - -import java.io.IOException; -import java.io.UncheckedIOException; - -@SuppressWarnings("serial") -public class JsonException extends UncheckedIOException { - - public JsonException(String message) { - super(new IOException(message)); - } - - public JsonException(IOException exception) { - super(exception); - } -} diff --git a/datastructures-json/src/main/java/org/xbib/datastructures/json/ListNode.java b/datastructures-json/src/main/java/org/xbib/datastructures/json/ListNode.java deleted file mode 100644 index 79c82d6..0000000 --- a/datastructures-json/src/main/java/org/xbib/datastructures/json/ListNode.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.xbib.datastructures.json; - -import org.xbib.datastructures.tiny.TinyList; -import java.util.List; - -public class ListNode extends TinyList.Builder implements Node> { - - public boolean has(int i) { - return i >= 0 && i < size() && get(i) == null; - } - - public boolean has(Node node) { - return contains(node); - } - - @Override - public List get() { - return this; - } -} diff --git a/datastructures-json/src/main/java/org/xbib/datastructures/json/MapNode.java b/datastructures-json/src/main/java/org/xbib/datastructures/json/MapNode.java deleted file mode 100644 index a68b09c..0000000 --- a/datastructures-json/src/main/java/org/xbib/datastructures/json/MapNode.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.xbib.datastructures.json; - -import org.xbib.datastructures.tiny.TinyMap; -import java.util.Map; - -public class MapNode extends TinyMap.Builder implements Node> { - - public boolean has(String name) { - return containsKey(name); - } - - @Override - public Map get() { - return this; - } -} diff --git a/datastructures-json/src/main/java/org/xbib/datastructures/json/Node.java b/datastructures-json/src/main/java/org/xbib/datastructures/json/Node.java deleted file mode 100644 index efb3cae..0000000 --- a/datastructures-json/src/main/java/org/xbib/datastructures/json/Node.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.xbib.datastructures.json; - -public interface Node { - - T get(); -} diff --git a/datastructures-json/src/test/java/org/xbib/datastructures/json/test/GeneratorTest.java b/datastructures-json/src/test/java/org/xbib/datastructures/json/test/GeneratorTest.java deleted file mode 100644 index ab4629c..0000000 --- a/datastructures-json/src/test/java/org/xbib/datastructures/json/test/GeneratorTest.java +++ /dev/null @@ -1,113 +0,0 @@ -package org.xbib.datastructures.json.test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.xbib.datastructures.json.Generator; -import org.xbib.datastructures.json.StreamParser; -import java.io.IOException; -import java.io.StringReader; -import java.io.StringWriter; -import java.util.List; -import java.util.Map; - -public class GeneratorTest { - - Generator generator; - - @BeforeEach - public void setup() { - StringWriter sw = new StringWriter(); - generator = new Generator(null, sw); - } - - @Test - public void testObjectStrFromMap() throws IOException { - generator.write(Map.of("a", "b")); - assertEquals("{\"a\":\"b\"}", generator.toString()); - } - - @Test - public void testObjectNumFromMap() throws IOException { - generator.write(Map.of("a", 2)); - assertEquals("{\"a\":2}", generator.toString()); - } - - @Test - public void testArrayFromCollection() throws IOException { - generator.write(List.of("a", "b", "c")); - assertEquals("[\"a\",\"b\",\"c\"]", generator.toString()); - } - - @Test - public void testArrayStr() throws IOException { - generator.beginArray() - .writeString("a") - .writeString("b") - .writeString("c") - .endArray(); - String s = generator.toString(); - assertEquals("[\"a\",\"b\",\"c\"]", s); - StreamParser streamParser = new StreamParser(); - streamParser.parse(new StringReader(s)); - assertEquals("[a, b, c]", streamParser.getNode().get().toString()); - } - - @Test - public void testArrayNum() throws IOException { - generator.beginArray() - .writeNumber(1) - .writeNumber(2) - .writeNumber(3) - .endArray(); - String s = generator.toString(); - assertEquals("[1,2,3]", s); - StreamParser streamParser = new StreamParser(); - streamParser.parse(new StringReader(s)); - assertEquals("[1, 2, 3]", streamParser.getNode().get().toString()); - } - - @Test - public void testArrayFloat() throws IOException { - generator.beginArray() - .writeNumber(1.0) - .writeNumber(2.0) - .writeNumber(3.0) - .endArray(); - String s = generator.toString(); - assertEquals("[1.0,2.0,3.0]", s); - StreamParser streamParser = new StreamParser(); - streamParser.parse(new StringReader(s)); - assertNotNull(streamParser.getNode()); - assertNotNull(streamParser.getNode().get()); - assertEquals("[1.0, 2.0, 3.0]", streamParser.getNode().get().toString()); - } - - @Test - public void testObjectStr() throws IOException { - generator.beginObject() - .writeString("a") - .writeString("b") - .endObject(); - String s = generator.toString(); - assertEquals("{\"a\":\"b\"}", s); - StreamParser streamParser = new StreamParser(); - streamParser.parse(new StringReader(s)); - assertEquals("{a=b}", streamParser.getNode().get().toString()); - } - - @Test - public void testObjectNum() throws IOException { - generator.beginObject() - .writeString("a") - .writeNumber(1) - .endObject(); - String s = generator.toString(); - assertEquals("{\"a\":1}", s); - StreamParser streamParser = new StreamParser(); - streamParser.parse(new StringReader(s)); - assertEquals("{a=1}", streamParser.getNode().get().toString()); - } - -} diff --git a/datastructures-json/src/test/java/org/xbib/datastructures/json/test/LargeFileTest.java b/datastructures-json/src/test/java/org/xbib/datastructures/json/test/LargeFileTest.java deleted file mode 100644 index 55ef044..0000000 --- a/datastructures-json/src/test/java/org/xbib/datastructures/json/test/LargeFileTest.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.xbib.datastructures.json.test; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.xbib.datastructures.json.TinyJsonListener; -import org.xbib.datastructures.json.StreamParser; -import org.xbib.datastructures.json.StringParser; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.StringReader; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.stream.Collectors; -import java.util.zip.GZIPInputStream; - -public class LargeFileTest { - - @Test - public void testStringParser() throws IOException { - try (InputStream inputStream = ParserTest.class.getResourceAsStream("/org/xbib/datastructures/json/test/large-file.json")) { - byte [] b = inputStream.readAllBytes(); - String string = new String(b, StandardCharsets.UTF_8); - StringParser stringParser = new StringParser(new TinyJsonListener()); - stringParser.parse(string); - stringParser.getNode().get().toString(); - stringParser.parse(string); - stringParser.getNode().get().toString(); - } - } - - @Test - public void testStreamParser() throws IOException { - try (BufferedReader reader = new BufferedReader(new InputStreamReader(ParserTest.class.getResourceAsStream("/org/xbib/datastructures/json/test/test.json")))) { - StreamParser streamParser = new StreamParser(new TinyJsonListener()); - streamParser.parse(reader); - Logger.getLogger("").log(Level.INFO, streamParser.getNode().get().toString()); - } - } - - @Disabled - @Test - public void largeFileTest() throws IOException { - try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new GZIPInputStream(Files.newInputStream(Paths.get("/Users/joerg/jam.jsonlines.gz")))))) { - for (String line : bufferedReader.lines().collect(Collectors.toList())) { - StreamParser streamParser = new StreamParser(); - streamParser.parse(new StringReader(line)); - } - } - } -} diff --git a/datastructures-tiny/build.gradle b/datastructures-tiny/build.gradle index 50d4350..90b4a20 100644 --- a/datastructures-tiny/build.gradle +++ b/datastructures-tiny/build.gradle @@ -1,4 +1,3 @@ - dependencies { api project(':datastructures-common') } diff --git a/datastructures-tiny/src/main/java/module-info.java b/datastructures-tiny/src/main/java/module-info.java index 20d189a..da89b65 100644 --- a/datastructures-tiny/src/main/java/module-info.java +++ b/datastructures-tiny/src/main/java/module-info.java @@ -1,5 +1,4 @@ module org.xbib.datastructures.tiny { exports org.xbib.datastructures.tiny; requires org.xbib.datastructures.common; - requires java.logging; } diff --git a/datastructures-yaml-tiny/NOTICE.txt b/datastructures-yaml-tiny/NOTICE.txt new file mode 100644 index 0000000..06218d9 --- /dev/null +++ b/datastructures-yaml-tiny/NOTICE.txt @@ -0,0 +1,7 @@ +This is a modfied work of + +https://github.com/Kahsolt/IfYaml + +as of May, 2021 + +WTFPL License \ No newline at end of file diff --git a/datastructures-yaml/build.gradle b/datastructures-yaml-tiny/build.gradle similarity index 59% rename from datastructures-yaml/build.gradle rename to datastructures-yaml-tiny/build.gradle index 1a8be78..a97af3b 100644 --- a/datastructures-yaml/build.gradle +++ b/datastructures-yaml-tiny/build.gradle @@ -1,3 +1,4 @@ dependencies { + api project(':datastructures-api') api project(':datastructures-tiny') } diff --git a/datastructures-yaml-tiny/src/main/java/module-info.java b/datastructures-yaml-tiny/src/main/java/module-info.java new file mode 100644 index 0000000..2b080a2 --- /dev/null +++ b/datastructures-yaml-tiny/src/main/java/module-info.java @@ -0,0 +1,9 @@ +import org.xbib.datastructures.api.DataStructure; +import org.xbib.datastructures.yaml.tiny.Yaml; + +module org.xbib.datastructures.yaml.tiny { + exports org.xbib.datastructures.yaml.tiny; + requires org.xbib.datastructures.api; + requires org.xbib.datastructures.tiny; + provides DataStructure with Yaml; +} diff --git a/datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/Lexer.java b/datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/Lexer.java similarity index 91% rename from datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/Lexer.java rename to datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/Lexer.java index a88dded..5ea8cf2 100644 --- a/datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/Lexer.java +++ b/datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/Lexer.java @@ -1,4 +1,4 @@ -package org.xbib.datastructures.yaml; +package org.xbib.datastructures.yaml.tiny; import java.io.BufferedReader; import java.io.IOException; @@ -54,6 +54,9 @@ public class Lexer { } boolean isPipe = false; switch (token.getType()) { + case DOCUMENT_START: + case DOCUMENT_END: + return token; case VALUE: List values = new ArrayList<>(); int indent = token.getDepth(); @@ -65,7 +68,7 @@ public class Lexer { } else { break; } - } while ((token = nextToken()) != null && token.getDepth() > prevToken.getDepth()); + } while ((token = nextToken()) != null && prevToken != null && token.getDepth() > prevToken.getDepth()); nextToken = token; tokens.add(values.size() == 1 ? new Token(TokenType.VALUE_LINE, indent, values.get(0)) @@ -145,6 +148,12 @@ public class Lexer { indent = index; read(); return new Token(TokenType.ANGLE, indent); + } else if (isDocumentStart()) { + read(3); + return new Token(TokenType.DOCUMENT_START, 0); + } else if (isDocumentEnd()) { + read(3); + return new Token(TokenType.DOCUMENT_END, 0); } else if (isText()) { indent = index; value = extractText(); @@ -251,6 +260,10 @@ public class Lexer { return ch(index + 1); } + private char nextNextChar() { + return ch(index + 2); + } + private boolean hasLeftGap() { return prevChar() == ' ' || prevChar() == BOL; } @@ -279,6 +292,14 @@ public class Lexer { return current() == '-'; } + private boolean isDocumentStart() { + return current() == '-' && nextChar() == '-' && nextNextChar() == '-'; + } + + private boolean isDocumentEnd() { + return current() == '.' && nextChar() == '.' && nextNextChar() == '.'; + } + private boolean isColon() { return current() == ':'; } diff --git a/datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/ListNode.java b/datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/ListNode.java similarity index 92% rename from datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/ListNode.java rename to datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/ListNode.java index dd36a66..ccc165d 100644 --- a/datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/ListNode.java +++ b/datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/ListNode.java @@ -1,11 +1,12 @@ -package org.xbib.datastructures.yaml; +package org.xbib.datastructures.yaml.tiny; +import org.xbib.datastructures.api.Node; import org.xbib.datastructures.tiny.TinyList; import java.util.ArrayList; import java.util.List; import java.util.Map; -public class ListNode implements Node>> { +public class ListNode implements org.xbib.datastructures.api.ListNode { private final int depth; diff --git a/datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/MapNode.java b/datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/MapNode.java similarity index 71% rename from datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/MapNode.java rename to datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/MapNode.java index 8f46c71..ac78b4a 100644 --- a/datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/MapNode.java +++ b/datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/MapNode.java @@ -1,5 +1,6 @@ -package org.xbib.datastructures.yaml; +package org.xbib.datastructures.yaml.tiny; +import org.xbib.datastructures.api.Node; import org.xbib.datastructures.tiny.TinyMap; import java.util.ArrayList; import java.util.Collection; @@ -7,11 +8,11 @@ import java.util.Collections; import java.util.List; import java.util.Map; -public class MapNode implements Node>> { +public class MapNode implements org.xbib.datastructures.api.MapNode { private final int depth; - private final TinyMap.Builder> map; + private final TinyMap.Builder> map; private final TinyMap.Builder> comments; @@ -25,7 +26,7 @@ public class MapNode implements Node>> { return depth; } - public Map> get() { + public Map> get() { return map.build(); } @@ -33,9 +34,9 @@ public class MapNode implements Node>> { return comments.build(); } - public List, List>>> getChildCommentPairs() { - List, List>>> pairs = new ArrayList<>(); - for (Map.Entry> kv : map.entrySet()) { + public List, List>>> getChildCommentPairs() { + List, List>>> pairs = new ArrayList<>(); + for (Map.Entry> kv : map.entrySet()) { pairs.add(Map.entry(kv.getKey(), Map.entry(kv.getValue(), comments.getOrDefault(kv.getKey(), Collections.emptyList())))); } return pairs; diff --git a/datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/Token.java b/datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/Token.java similarity index 81% rename from datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/Token.java rename to datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/Token.java index 14a31e1..4faa73b 100644 --- a/datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/Token.java +++ b/datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/Token.java @@ -1,4 +1,4 @@ -package org.xbib.datastructures.yaml; +package org.xbib.datastructures.yaml.tiny; public class Token { @@ -29,4 +29,9 @@ public class Token { public String getValue() { return value; } + + @Override + public String toString() { + return type.name(); + } } diff --git a/datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/TokenType.java b/datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/TokenType.java similarity index 67% rename from datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/TokenType.java rename to datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/TokenType.java index 6ae8e9c..cbdff81 100644 --- a/datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/TokenType.java +++ b/datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/TokenType.java @@ -1,6 +1,8 @@ -package org.xbib.datastructures.yaml; +package org.xbib.datastructures.yaml.tiny; public enum TokenType { + DOCUMENT_START, + DOCUMENT_END, ITEM, KEY, VALUE, diff --git a/datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/ValueNode.java b/datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/ValueNode.java similarity index 83% rename from datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/ValueNode.java rename to datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/ValueNode.java index 864871d..103a938 100644 --- a/datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/ValueNode.java +++ b/datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/ValueNode.java @@ -1,15 +1,17 @@ -package org.xbib.datastructures.yaml; +package org.xbib.datastructures.yaml.tiny; + +import org.xbib.datastructures.api.Node; import java.util.Collection; import java.util.Collections; import java.util.ArrayList; import java.util.List; -public class ValueNode implements Node { +public class ValueNode implements org.xbib.datastructures.api.ValueNode { private final int depth; - private String value; + private Object value; private TextType type; @@ -34,12 +36,13 @@ public class ValueNode implements Node { return depth; } - public void set(String value) { + @Override + public void set(Object value) { this.value = value; } @Override - public String get() { + public Object get() { return value; } diff --git a/datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/Yaml.java b/datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/Yaml.java similarity index 80% rename from datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/Yaml.java rename to datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/Yaml.java index 0087f53..c62ad8e 100644 --- a/datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/Yaml.java +++ b/datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/Yaml.java @@ -1,11 +1,13 @@ -package org.xbib.datastructures.yaml; +package org.xbib.datastructures.yaml.tiny; -import java.io.IOException; -import java.io.Reader; -import java.io.Writer; +import org.xbib.datastructures.api.*; +import org.xbib.datastructures.api.Builder; + +import java.io.StringWriter; import java.time.Instant; +import java.util.function.Consumer; -public class Yaml { +public class Yaml implements DataStructure { private final char separator; @@ -24,28 +26,49 @@ public class Yaml { this.separator = separator; } - public void read(Reader reader) throws IOException { - Parser parser = new Parser(reader); - parser.parse(); - setRoot(parser.getNode()); + @Override + public Parser createParser() { + return new YamlParser(); } - public void save(Writer writer) throws IOException { - new Generator(getRoot()).generate(writer); + @Override + public Builder createBuilder() { + return new YamlBuilder(new StringWriter()); } + @Override + public Builder createBuilder(Consumer consumer) { + return new YamlBuilder(new StringWriter()) { + @Override + public String build() { + String string = super.build(); + consumer.accept(string); + return string; + } + }; + } + + @Override + public Generator createGenerator(Node root) { + return new YamlGenerator(root); + } + + @Override public void setRoot(Node root) { this.root = root; } + @Override public Node getRoot() { return root; } - public boolean exist(String path) { - return getNode(path) != null; + @Override + public Node getNode(String path) { + return path == null ? null : internalGetNode(root, path); } + @Override public Boolean getBoolean(String path) { String value = internalGet(path); if (value == null) { @@ -58,10 +81,7 @@ public class Yaml { } } - public Boolean getBoolean(String path, Boolean defaultval) { - return getBoolean(path) != null ? getBoolean(path) : defaultval; - } - + @Override public Byte getByte(String path) { String value = internalGet(path); if (value == null) { @@ -74,10 +94,7 @@ public class Yaml { } } - public Byte getByte(String path, Byte defaultval) { - return getByte(path) != null ? getByte(path) : defaultval; - } - + @Override public Short getShort(String path) { String value = internalGet(path); if (value == null) { @@ -90,10 +107,7 @@ public class Yaml { } } - public Short getShort(String path, Short defaultval) { - return getShort(path) != null ? getShort(path) : defaultval; - } - + @Override public Integer getInteger(String path) { String value = internalGet(path); if (value == null) { @@ -106,10 +120,7 @@ public class Yaml { } } - public Integer getInteger(String path, Integer defaultval) { - return getInteger(path) != null ? getInteger(path) : defaultval; - } - + @Override public Long getLong(String path) { String value = internalGet(path); if (value == null) { @@ -122,10 +133,7 @@ public class Yaml { } } - public Long getLong(String path, Long defaultval) { - return getLong(path) != null ? getLong(path) : defaultval; - } - + @Override public Float getFloat(String path) { String value = internalGet(path); if (value == null) { @@ -138,10 +146,7 @@ public class Yaml { } } - public Float getFloat(String path, Float defaultval) { - return getFloat(path) != null ? getFloat(path) : defaultval; - } - + @Override public Double getDouble(String path) { String value = internalGet(path); if (value == null) { @@ -154,10 +159,7 @@ public class Yaml { } } - public Double getDouble(String path, Double defaultval) { - return getDouble(path) != null ? getDouble(path) : defaultval; - } - + @Override public Character getCharacter(String path) { String value = internalGet(path); if (value == null) { @@ -170,18 +172,12 @@ public class Yaml { } } - public Character getCharacter(String path, Character defaultval) { - return getCharacter(path) != null ? getCharacter(path) : defaultval; - } - + @Override public String getString(String path) { return internalGet(path); } - public String getString(String path, String defaultval) { - return getString(path) != null ? getString(path) : defaultval; - } - + @Override public Instant getInstant(String path) { String value = internalGet(path); if (value == null) { @@ -190,11 +186,17 @@ public class Yaml { return Instant.parse(value); } - public Instant getInstant(String path, Instant defaultval) { - Instant instant = getInstant(path); - return instant != null ? instant : defaultval; + @Override + public TimeValue getAsTime(String path, TimeValue defaultValue) { + return TimeValue.parseTimeValue(getString(path), defaultValue); } + @Override + public ByteSizeValue getAsBytesSize(String path, ByteSizeValue defaultValue) { + return ByteSizeValue.parseBytesSizeValue(getString(path), defaultValue); + } + + @Override public boolean set(String path, Object value) { return set(path, value, true); } @@ -202,7 +204,6 @@ public class Yaml { private boolean set(String path, Object value, boolean rebuild) { if (rebuild) { if (value == null) { - //return removeNode(path); throw new IllegalStateException("value is null"); } makeNode(path); @@ -231,14 +232,6 @@ public class Yaml { return true; } - public boolean set(String path, Instant instant) { - return set(path, instant.toString()); - } - - public Node getNode(String path) { - return path == null ? null : internalGetNode(root, path); - } - private Node internalGetNode(Node node, String path) { if (node == null || path.isEmpty()) { return node; @@ -258,7 +251,7 @@ public class Yaml { return null; } - public void makeNode(String path) { + private void makeNode(String path) { if (path != null) { internalMakeNode(root, path); } @@ -341,8 +334,8 @@ public class Yaml { } private String internalGet(String path) { - Node node = getNode(dequote(path)); - return node instanceof ValueNode ? ((ValueNode) node).get() : null; + Node node = getNode(unquote(path)); + return node instanceof ValueNode ? ((ValueNode) node).get().toString() : null; } private static String[] cut(String string, char delimiter) { @@ -352,7 +345,7 @@ public class Yaml { return new String[]{seg, rest}; } - private static String dequote(String string) { + private static String unquote(String string) { if (string.startsWith("'") && string.endsWith("'") || string.startsWith("\"") && string.endsWith("\"")) { return string.substring(1, string.length() - 1); diff --git a/datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/YamlBuilder.java b/datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/YamlBuilder.java new file mode 100644 index 0000000..2a1ec2e --- /dev/null +++ b/datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/YamlBuilder.java @@ -0,0 +1,300 @@ +package org.xbib.datastructures.yaml.tiny; + +import org.xbib.datastructures.api.Builder; +import org.xbib.datastructures.api.ByteSizeValue; +import org.xbib.datastructures.api.TimeValue; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.io.Writer; +import java.time.Instant; +import java.util.Collection; +import java.util.Map; +import java.util.Objects; + +public class YamlBuilder implements Builder { + + public final Writer writer; + + private final int indent; + + private State state; + + public YamlBuilder(Writer writer) { + this(writer, 2); + } + + public YamlBuilder(Writer writer, int indent) { + this.writer = writer; + this.indent = indent; + this.state = new State(null, 0, Structure.MAP, false); + } + + @Override + public Builder beginCollection() { + this.state = new State(state, state.level + 1, Structure.COLLECTION, true); + return this; + } + + @Override + public Builder endCollection() { + if (state.structure != Structure.COLLECTION) { + throw new YamlException("no array to close"); + } + this.state = state.parent; + return this; + } + + @Override + public Builder beginMap() { + this.state = new State(state, state.level + 1, Structure.MAP, false); + return this; + } + + @Override + public Builder endMap() { + if (state.structure != Structure.MAP) { + throw new YamlException("no object to close"); + } + this.state = state.parent; + return this; + } + + @Override + public Builder buildMap(Map map) { + Objects.requireNonNull(map); + beginMap(); + map.forEach((k, v) -> { + try { + buildKey(k); + buildValue(v); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + endMap(); + return this; + } + + @Override + public Builder buildCollection(Collection collection) { + Objects.requireNonNull(collection); + beginCollection(); + collection.forEach(v -> { + try { + buildValue(v); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + endCollection(); + return this; + } + + @SuppressWarnings("unchecked") + @Override + public Builder buildValue(Object object) throws IOException { + if (state.structure == Structure.MAP) { + beginValue(object); + } + if (state.structure == Structure.COLLECTION) { + beginArrayValue(object); + } + if (object == null) { + buildNull(); + } else if (object instanceof Map) { + buildMap((Map) object); + } else if (object instanceof Collection) { + buildCollection((Collection) object); + } else if (object instanceof CharSequence) { + buildString((CharSequence) object, true); + } else if (object instanceof Boolean) { + buildBoolean((Boolean) object); + } else if (object instanceof Byte) { + buildNumber((byte) object); + } else if (object instanceof Integer) { + buildNumber((int) object); + } else if (object instanceof Long) { + buildNumber((long) object); + } else if (object instanceof Float) { + buildNumber((float) object); + } else if (object instanceof Double) { + buildNumber((double) object); + } else if (object instanceof Number) { + buildNumber((Number) object); + } else if (object instanceof Instant) { + buildInstant((Instant) object); + } else if (object instanceof ByteSizeValue) { + buildKey(object.toString()); + } else if (object instanceof TimeValue) { + buildKey(object.toString()); + } else { + throw new IllegalArgumentException("unable to write object class " + object.getClass()); + } + if (state.structure == Structure.MAP) { + endValue(object); + } + if (state.structure == Structure.COLLECTION) { + endArrayValue(object); + } + return this; + } + + @Override + public Builder buildKey(CharSequence charSequence) throws IOException { + if (state.structure == Structure.MAP) { + beginKey(charSequence.toString()); + } + buildString(charSequence, true); + if (state.structure == Structure.MAP) { + endKey(charSequence.toString()); + } + return this; + } + + @Override + public Builder buildNull() throws IOException { + buildString("null", false); + return this; + } + + @Override + public String build() { + return writer.toString(); + } + + private void buildNumber(Number number) throws IOException { + buildString(number != null ? number.toString() : null, false); + } + + private void buildBoolean(boolean bool) throws IOException { + buildString(bool ? "true" : "false", true); + } + + private void buildInstant(Instant instant) throws IOException{ + buildString(instant.toString(), false); + } + + private void buildString(CharSequence string, boolean escape) throws IOException { + String value = escape ? escapeString(string) : string.toString(); + if (!((value.startsWith("'") && value.endsWith("'")) || (value.startsWith("\"") && value.endsWith("\""))) && + value.matches(".*[?\\-#:>|$%&{}\\[\\]]+.*|[ ]+")) { + if (value.contains("\"")) { + value = "'" + value + "'"; + } else { + value = "\"" + value + "\""; + } + } + writer.write(value); + } + + private void beginKey(String k) throws IOException { + if (state.parent != null && state.parent.item) { + state.parent.item = false; + return; + } + writer.write(" ".repeat((state.level - 1) * indent)); + } + + private void endKey(String k) throws IOException { + writer.write(": "); + } + + private void beginValue(Object v) throws IOException { + if (v instanceof Map) { + writeLn(); + return; + } + if (v instanceof Collection) { + writeLn(); + } + } + + private void endValue(Object v) throws IOException{ + if (v instanceof Map) { + return; + } + if (v instanceof Collection) { + return; + } + writeLn(); + } + + private void beginArrayValue(Object v) throws IOException { + if (v instanceof Collection) { + return; + } + writer.write(" ".repeat((state.level - 1) * indent)); + writer.write("- "); + state.item = true; + } + + private void endArrayValue(Object v) throws IOException { + if (v instanceof Map) { + return; + } + if (v instanceof Collection) { + return; + } + writeLn(); + } + + private void writeLn() throws IOException{ + writer.write(System.lineSeparator()); + } + + private String escapeString(CharSequence string) { + StringBuilder sb = new StringBuilder(); + int start = 0; + int l = string.length(); + for (int i = 0; i < l; i++) { + char c = string.charAt(i); + if (c == '"' || c < 32 || c >= 127 || c == '\\') { + if (start < i) { + sb.append(string.toString(), start, i - start); + } + start = i; + sb.append(escapeCharacter(c)); + } + } + if (start < l) { + sb.append(string.toString(), start, l - start); + } + return sb.toString(); + } + + private static String escapeCharacter(char c) { + switch (c) { + case '\n': + return "\\n"; + case '\r': + return "\\r"; + case '\t': + return "\\t"; + case '\\': + return "\\\\"; + case '\'': + return "\\'"; + case '\"': + return "\\\""; + } + String hex = Integer.toHexString(c); + return "\\u0000".substring(0, 6 - hex.length()) + hex; + } + + private enum Structure { MAP, COLLECTION }; + + private static class State { + State parent; + int level; + Structure structure; + boolean item; + + State(State parent, int level, Structure structure, boolean item) { + this.parent = parent; + this.level = level; + this.structure = structure; + this.item = item; + } + } +} diff --git a/datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/YamlException.java b/datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/YamlException.java similarity index 78% rename from datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/YamlException.java rename to datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/YamlException.java index b0ebd49..c60afee 100644 --- a/datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/YamlException.java +++ b/datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/YamlException.java @@ -1,4 +1,4 @@ -package org.xbib.datastructures.yaml; +package org.xbib.datastructures.yaml.tiny; @SuppressWarnings("serial") public class YamlException extends RuntimeException { diff --git a/datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/Generator.java b/datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/YamlGenerator.java similarity index 53% rename from datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/Generator.java rename to datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/YamlGenerator.java index afe2eae..f902f5f 100644 --- a/datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/Generator.java +++ b/datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/YamlGenerator.java @@ -1,106 +1,121 @@ -package org.xbib.datastructures.yaml; +package org.xbib.datastructures.yaml.tiny; + +import org.xbib.datastructures.api.Generator; +import org.xbib.datastructures.api.Node; -import org.xbib.datastructures.yaml.ValueNode.TextType; import java.io.IOException; +import java.io.StringWriter; import java.io.Writer; import java.util.List; import java.util.Map; -public class Generator { +public class YamlGenerator implements Generator { private final Node root; private final int indent; - public Generator(Node root) { - this(root, 2); + private Writer writer; + + public YamlGenerator(Node root) { + this(root, 2, new StringWriter()); } - public Generator(Node root, int indent) { + public YamlGenerator(Node root, int indent, Writer writer) { this.root = root; + this.writer = writer; this.indent = indent; } + @Override public void generate(Writer writer) throws IOException { + this.writer = writer; try (writer) { if (root != null) { - if (internalWrite(writer, root, null)) { + if (internalWrite(root, null)) { writer.append('\n'); } } } } - private boolean internalWrite(Appendable appendable, Node curnode, Node prevnode) throws IOException { + private boolean internalWrite(Node curnode, Node prevnode) throws IOException { if (curnode == null) { return false; } boolean lf = false; String indent = " ".repeat(curnode.getDepth() * this.indent); if (curnode instanceof ValueNode) { - ValueNode txnode = (ValueNode) curnode; - lf = writeComments(appendable, txnode.getComments(), indent, false); - switch (txnode.getType()) { - case LINE: + ValueNode valueNode = (ValueNode) curnode; + lf = writeComments(valueNode.getComments(), indent, false); + switch (valueNode.getType()) { + case LINE: { if (lf) { - appendable.append('\n').append(indent); + writer.append('\n').append(indent); } - appendable.append(txnode.get()); + Object object = valueNode.get(); + String s = object == null ? "null" : object.toString(); + writer.append(s.isEmpty() ? "\"\"" : s); break; - case MULTILINE: + } + case MULTILINE: { if (lf) { - appendable.append('\n'); + writer.append('\n'); } - String s = prefix(linewrap(txnode.get()), indent); - appendable.append(s); + Object object = valueNode.get(); + String s = object == null ? "null" : object.toString(); + writer.append(prefix(linewrap(s), indent)); break; + } case TEXT: case TEXT_ANGLE: - if (txnode.getType() == TextType.TEXT) { - appendable.append("|\n"); + if (valueNode.getType() == ValueNode.TextType.TEXT) { + writer.append("|\n"); } - if (txnode.getType() == TextType.TEXT_ANGLE) { - appendable.append(">\n"); + if (valueNode.getType() == ValueNode.TextType.TEXT_ANGLE) { + writer.append(">\n"); } - appendable.append(prefix(txnode.get(), indent)); + Object object = valueNode.get(); + String s = object == null ? "null" : object.toString(); + writer.append(prefix(s, indent)); break; } return true; } else if (curnode instanceof MapNode) { MapNode mapNode = (MapNode) curnode; - for (Map.Entry, List>> knc : mapNode.getChildCommentPairs()) { - lf = writeComments(appendable, knc.getValue().getValue(), indent, lf); + for (Map.Entry, List>> knc : mapNode.getChildCommentPairs()) { + lf = writeComments(knc.getValue().getValue(), indent, lf); if (lf || (prevnode != null && !(prevnode instanceof ListNode))) { - appendable.append("\n").append(indent); + writer.append("\n").append(indent); } - appendable.append(knc.getKey()).append(": "); - lf = internalWrite(appendable, knc.getValue().getKey(), mapNode); + writer.append(knc.getKey()).append(": "); + lf = internalWrite(knc.getValue().getKey(), mapNode); } return lf; } else if (curnode instanceof ListNode) { ListNode listNode = (ListNode) curnode; for (Map.Entry, List> nc : listNode.getItemCommentPairs()) { - lf = writeComments(appendable, nc.getValue(), indent, lf); + lf = writeComments(nc.getValue(), indent, lf); if (lf || (prevnode != null && !(prevnode instanceof ListNode))) { - appendable.append("\n").append(indent); + writer.append("\n").append(indent); } - appendable.append("- "); - lf = internalWrite(appendable, nc.getKey(), listNode); + writer.append("- "); + lf = internalWrite(nc.getKey(), listNode); } return lf; } return false; } - private boolean writeComments(Appendable appendable, List comments, String indent, boolean lf) throws IOException { + private boolean writeComments(List comments, String indent, boolean lf) throws IOException { if (comments == null) { return false; } for (String comment : comments) { if (lf) { - appendable.append('\n'); + writer.append('\n'); } - appendable.append(indent).append('#').append(comment); + writer.append(indent).append('#').append(comment); lf = true; } return lf; @@ -141,5 +156,4 @@ public class Generator { } return sb.toString(); } - } diff --git a/datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/Parser.java b/datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/YamlParser.java similarity index 81% rename from datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/Parser.java rename to datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/YamlParser.java index 3417787..29212b7 100644 --- a/datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/Parser.java +++ b/datastructures-yaml-tiny/src/main/java/org/xbib/datastructures/yaml/tiny/YamlParser.java @@ -1,33 +1,31 @@ -package org.xbib.datastructures.yaml; +package org.xbib.datastructures.yaml.tiny; + +import org.xbib.datastructures.api.Node; +import org.xbib.datastructures.api.Parser; -import org.xbib.datastructures.yaml.ValueNode.TextType; import java.io.IOException; import java.io.Reader; import java.util.ArrayList; import java.util.List; import java.util.ListIterator; -public class Parser { +import static org.xbib.datastructures.yaml.tiny.TokenType.ITEM; - private final Lexer lexer; +public class YamlParser implements Parser { private final List comments; - private Node root; + private Lexer lexer; private Token token; - public Parser(Reader reader) { - lexer = new Lexer(reader); + public YamlParser() { comments = new ArrayList<>(); } - public void parse() throws IOException { - root = parseNode(null, -1); - } - - public Node getNode() { - return root; + public Node parse(Reader reader) throws IOException { + lexer = new Lexer(reader); + return parseNode(null, -1); } private Node parseNode(Node parent, int depth) throws IOException { @@ -37,10 +35,14 @@ public class Parser { return null; } } - if (token.getDepth() <= depth) { + if (token.getDepth() < depth) { return new ValueNode(parent); } switch (token.getType()) { + case DOCUMENT_START: + case DOCUMENT_END: + token = lexer.next(); + return parseNode(parent, depth); case COMMENT: stashComments(); return parseNode(parent, depth); @@ -62,7 +64,7 @@ public class Parser { private ListNode parseListNode(Node parent, int indent) throws IOException { ListNode node = new ListNode(parent); int cnt = 0; - while (token != null && token.getDepth() == indent && token.getType() == TokenType.ITEM) { + while (token != null && token.getDepth() == indent && token.getType() == ITEM) { token = lexer.next(); if (token != null) { node.addComments(cnt, collectComments(indent)); @@ -93,27 +95,28 @@ public class Parser { node.addComments(collectComments(indent)); switch (token.getType()) { case VALUE_LINE: - node.setType(TextType.LINE); + node.setType(ValueNode.TextType.LINE); node.set(token.getValue()); token = lexer.next(); break; case VALUE_MULTILINE: - node.setType(TextType.MULTILINE); + node.setType(ValueNode.TextType.MULTILINE); node.set(token.getValue()); token = lexer.next(); break; case VALUE_TEXT_PIPE: - node.setType(TextType.TEXT); + node.setType(ValueNode.TextType.TEXT); node.set(token.getValue()); token = lexer.next(); break; case VALUE_TEXT_ANGLE: - node.setType(TextType.TEXT_ANGLE); + node.setType(ValueNode.TextType.TEXT_ANGLE); node.set(token.getValue()); token = lexer.next(); break; default: node.set(""); + break; } return node; } diff --git a/datastructures-yaml/src/test/java/org/xbib/datastructures/yaml/test/Formatter.java b/datastructures-yaml-tiny/src/test/java/org/xbib/datastructures/yaml/tiny/test/Formatter.java similarity index 83% rename from datastructures-yaml/src/test/java/org/xbib/datastructures/yaml/test/Formatter.java rename to datastructures-yaml-tiny/src/test/java/org/xbib/datastructures/yaml/tiny/test/Formatter.java index f9fd9d9..91f5a89 100644 --- a/datastructures-yaml/src/test/java/org/xbib/datastructures/yaml/test/Formatter.java +++ b/datastructures-yaml-tiny/src/test/java/org/xbib/datastructures/yaml/tiny/test/Formatter.java @@ -1,14 +1,16 @@ -package org.xbib.datastructures.yaml.test; +package org.xbib.datastructures.yaml.tiny.test; -import org.xbib.datastructures.yaml.MapNode; -import org.xbib.datastructures.yaml.ListNode; -import org.xbib.datastructures.yaml.Node; -import org.xbib.datastructures.yaml.ValueNode; +import org.xbib.datastructures.yaml.tiny.MapNode; +import org.xbib.datastructures.yaml.tiny.ListNode; +import org.xbib.datastructures.api.Node; +import org.xbib.datastructures.yaml.tiny.ValueNode; + +import java.util.List; import java.util.Map; public class Formatter { - public String format(Node node) { + public String format(Node node) { if (node instanceof ValueNode) { return format((ValueNode) node); } @@ -27,6 +29,7 @@ public class Formatter { sb.append(indent); sb.append(" comments = mapNode.getComments().get(charSequence.toString()); + if (comments != null && !comments.isEmpty()) { sb.append("comments="); - StringBuilder comments = new StringBuilder(); - for (String comment : mapNode.getComments().get(name)) { - comments.append(comment).append(' '); + StringBuilder commentStr = new StringBuilder(); + for (String comment : comments) { + commentStr.append(comment).append(' '); } - String s = excerpt(comments.toString()); + String s = excerpt(commentStr.toString()); s = quote(s, true); sb.append(s); } return sb.toString(); } - private String excerpt(String string) { + private String excerpt(Object object) { + if (object == null) { + return "null"; + } + String string = object.toString(); int len = string.length(); if (len <= 24) { return excerpt(string, 24); @@ -168,5 +176,4 @@ public class Formatter { private String spaces(int count) { return " ".repeat(count); } - } diff --git a/datastructures-yaml-tiny/src/test/java/org/xbib/datastructures/yaml/tiny/test/YamlBuilderTest.java b/datastructures-yaml-tiny/src/test/java/org/xbib/datastructures/yaml/tiny/test/YamlBuilderTest.java new file mode 100644 index 0000000..3925268 --- /dev/null +++ b/datastructures-yaml-tiny/src/test/java/org/xbib/datastructures/yaml/tiny/test/YamlBuilderTest.java @@ -0,0 +1,107 @@ +package org.xbib.datastructures.yaml.tiny.test; + +import org.junit.jupiter.api.Test; +import org.xbib.datastructures.api.Builder; +import org.xbib.datastructures.api.DataStructure; +import org.xbib.datastructures.tiny.TinyMap; +import org.xbib.datastructures.yaml.tiny.Yaml; + +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class YamlBuilderTest { + + @Test + public void testSimpleMap() throws Exception { + DataStructure structure = new Yaml(); + Builder builder = structure.createBuilder(); + builder.buildMap(Map.of("a", "b")); + assertEquals("a: b\n", builder.build()); + } + + @Test + public void testDoubleMap() throws Exception { + DataStructure structure = new Yaml(); + Builder builder = structure.createBuilder(); + TinyMap.Builder mapBuilder = TinyMap.builder(); + mapBuilder.put("a", "b"); + mapBuilder.put("c", "d"); + builder.buildMap(mapBuilder.build()); + assertEquals("a: b\nc: d\n", builder.build()); + } + + @Test + public void testNestedMap() throws Exception { + DataStructure structure = new Yaml(); + Builder builder = structure.createBuilder(); + builder.buildMap(Map.of("a", Map.of("b", "c"))); + assertEquals("a: \n b: c\n", builder.build()); + } + + @Test + public void testMixedMap() throws Exception { + DataStructure structure = new Yaml(); + Builder builder = structure.createBuilder(); + TinyMap.Builder mapBuilder = TinyMap.builder(); + mapBuilder.put("a", Map.of("b", "c")); + mapBuilder.put("d", "e"); + builder.buildMap(mapBuilder.build()); + assertEquals("a: \n b: c\nd: e\n", builder.build()); + } + + @Test + public void testCollection() throws Exception { + DataStructure structure = new Yaml(); + Builder builder = structure.createBuilder(); + builder.buildCollection(List.of("a", "b", "c")); + assertEquals("- a\n- b\n- c\n", builder.build()); + } + + @Test + public void testNestedCollection() throws Exception { + DataStructure structure = new Yaml(); + Builder builder = structure.createBuilder(); + builder.buildCollection(List.of("a", "b", "c", List.of("d", "e", "f"))); + assertEquals("- a\n- b\n- c\n - d\n - e\n - f\n", builder.build()); + } + + @Test + public void testMapOfCollections() throws Exception { + DataStructure structure = new Yaml(); + Builder builder = structure.createBuilder(); + TinyMap.Builder mapBuilder = TinyMap.builder(); + mapBuilder.put("a", List.of("b", "c")); + mapBuilder.put("d", List.of("e", "f")); + builder.buildMap(mapBuilder.build()); + assertEquals("a: \n - b\n - c\nd: \n - e\n - f\n", builder.build()); + } + + @Test + public void testCollectionOfMaps() throws Exception { + DataStructure structure = new Yaml(); + Builder builder = structure.createBuilder(); + builder.buildCollection(List.of(Map.of("a", "b"), Map.of("c", "d"), Map.of("e", "f"))); + assertEquals("- a: b\n" + + "- c: d\n" + + "- e: f\n", builder.build()); + } + + @Test + public void testCollectionOfDoubleMaps() throws Exception { + DataStructure structure = new Yaml(); + Builder builder = structure.createBuilder(); + TinyMap.Builder mapBuilder1 = TinyMap.builder(); + mapBuilder1.put("a", "b"); + mapBuilder1.put("c", "d"); + TinyMap.Builder mapBuilder2 = TinyMap.builder(); + mapBuilder2.put("e", "f"); + mapBuilder2.put("g", "h"); + builder.buildCollection(List.of(mapBuilder1.build(), mapBuilder2.build())); + assertEquals("- a: b\n" + + " c: d\n" + + "- e: f\n" + + " g: h\n", builder.build()); + } +} diff --git a/datastructures-yaml-tiny/src/test/java/org/xbib/datastructures/yaml/tiny/test/YamlParserTest.java b/datastructures-yaml-tiny/src/test/java/org/xbib/datastructures/yaml/tiny/test/YamlParserTest.java new file mode 100644 index 0000000..6beeec2 --- /dev/null +++ b/datastructures-yaml-tiny/src/test/java/org/xbib/datastructures/yaml/tiny/test/YamlParserTest.java @@ -0,0 +1,82 @@ +package org.xbib.datastructures.yaml.tiny.test; + +import org.junit.jupiter.api.Test; +import org.xbib.datastructures.api.Node; +import org.xbib.datastructures.api.Parser; +import org.xbib.datastructures.yaml.tiny.Yaml; +import org.xbib.datastructures.yaml.tiny.YamlParser; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class YamlParserTest { + + @Test + public void parseList() throws Exception { + Reader reader = new StringReader("test:\n- a\n- b\n"); + Parser parser = new YamlParser(); + Node node = parser.parse(reader); + Yaml yaml = new Yaml(node); + StringWriter writer = new StringWriter(); + yaml.createGenerator(node).generate(writer); + String s1 = writer.toString(); + Logger.getAnonymousLogger().log(Level.INFO, "s1 = " + s1); + } + + @Test + public void example() throws Exception { + InputStream inputStream = YamlTest.class.getResourceAsStream("/org/xbib/datastructures/yaml/test/example.yml"); + if (inputStream != null) { + roundTrip(inputStream); + } + } + + @Test + public void test() throws Exception { + InputStream inputStream = YamlTest.class.getResourceAsStream("/org/xbib/datastructures/yaml/test/test.yml"); + if (inputStream != null) { + roundTrip(inputStream); + } + } + + @Test + public void hippie() throws Exception { + InputStream inputStream = YamlTest.class.getResourceAsStream("/org/xbib/datastructures/yaml/test/hippie.yml"); + if (inputStream != null) { + roundTrip(inputStream); + } + } + + @Test + public void interlibrary() throws Exception { + InputStream inputStream = Files.newInputStream(Paths.get("/Users/joerg/.config/interlibrary/test.yaml")); + roundTrip(inputStream); + } + + private static void roundTrip(InputStream inputStream) throws Exception { + Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); + Parser parser = new YamlParser(); + Node node = parser.parse(reader); + Yaml yaml = new Yaml(node); + StringWriter writer = new StringWriter(); + yaml.createGenerator(node).generate(writer); + String s1 = writer.toString(); + Node node2 = parser.parse(new StringReader(writer.toString())); + writer = new StringWriter(); + yaml.createGenerator(node2).generate(writer); + String s2 = writer.toString(); + Logger.getAnonymousLogger().log(Level.INFO, "s1 = " + s1); + Logger.getAnonymousLogger().log(Level.INFO, "s2 = " + s2); + assertEquals(s1, s2); + } +} diff --git a/datastructures-yaml-tiny/src/test/java/org/xbib/datastructures/yaml/tiny/test/YamlTest.java b/datastructures-yaml-tiny/src/test/java/org/xbib/datastructures/yaml/tiny/test/YamlTest.java new file mode 100644 index 0000000..4b4b63f --- /dev/null +++ b/datastructures-yaml-tiny/src/test/java/org/xbib/datastructures/yaml/tiny/test/YamlTest.java @@ -0,0 +1,173 @@ +package org.xbib.datastructures.yaml.tiny.test; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.xbib.datastructures.api.Builder; +import org.xbib.datastructures.api.DataStructure; +import org.xbib.datastructures.api.Generator; +import org.xbib.datastructures.api.Parser; +import org.xbib.datastructures.yaml.tiny.YamlGenerator; +import org.xbib.datastructures.yaml.tiny.ListNode; +import org.xbib.datastructures.yaml.tiny.MapNode; +import org.xbib.datastructures.api.Node; +import org.xbib.datastructures.yaml.tiny.ValueNode; +import org.xbib.datastructures.yaml.tiny.Yaml; +import org.xbib.datastructures.yaml.tiny.YamlParser; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; + +public class YamlTest { + + private Node node; + + private Yaml yaml; + + @BeforeEach + public void setup() throws IOException { + InputStream inputStream = YamlTest.class.getResourceAsStream("/org/xbib/datastructures/yaml/test/test.yml"); + if (inputStream != null) { + Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); + Parser parser = new YamlParser(); + node = parser.parse(reader); + yaml = new Yaml(node); + } + } + + @Test + public void testNode() { + MapNode hsnode; + ListNode lsnode; + ValueNode valueNode; + hsnode = (MapNode) node; + valueNode = (ValueNode) hsnode.get("test"); + assertEquals("", valueNode.get()); + hsnode = (MapNode) node; + hsnode = (MapNode) hsnode.get("types"); + valueNode = (ValueNode) hsnode.get("multiline"); + assertEquals("line 1 line 2 line 3", valueNode.get()); + valueNode = (ValueNode) hsnode.get("text"); + assertEquals("def func(x) do\n # do something\n print x = x * 2\nend", valueNode.get()); + hsnode = (MapNode) node; + lsnode = (ListNode) hsnode.get("hash1"); + hsnode = (MapNode) lsnode.get(0); + lsnode = (ListNode) hsnode.get("hash2"); + valueNode = (ValueNode) lsnode.get(0); + assertEquals("the treasure is deep", valueNode.get()); + valueNode.set("new val"); + assertEquals("new val", valueNode.get()); + } + + @Test + public void testRead() { + assertEquals("", yaml.getString("test")); + assertEquals("the value", yaml.getString("the key")); + assertTrue(yaml.getBoolean("types.bool")); + assertEquals((byte) -1, yaml.getByte("types.byte")); + assertEquals((short)(3200), yaml.getShort("types.short")); + assertEquals(-2100000000, yaml.getInteger("types.int")); + assertEquals(1234321425321L, yaml.getLong("types.long")); + assertEquals(3.1415926F, yaml.getFloat("types.float")); + assertEquals(123413.4567654567654, yaml.getDouble("types.double")); + assertEquals('c', yaml.getCharacter("types.char")); + assertEquals("this is a string", yaml.getString("types.string")); + assertEquals("line 1 line 2 line 3", yaml.getString("types.multiline")); + assertEquals("def func(x) do\n # do something\n print x = x * 2\nend", yaml.getString("types.text")); + assertEquals(Instant.parse("1997-01-06T23:12:10Z"), yaml.getInstant("types.datetime")); + assertEquals("Text", yaml.getString("list.0")); + assertEquals("val", yaml.getString("list.1.key")); + assertEquals("ListOfList", yaml.getString("list.2.0")); + assertEquals("Text", yaml.getString("hash.name1")); + assertEquals("val", yaml.getString("hash.name2.key")); + assertEquals("HashOfList", yaml.getString("hash.name3.0")); + assertEquals("the treasure is deep", yaml.getString("hash1.0.hash2.0")); + } + + @Test + public void testMapListNodes() { + Yaml yaml = new Yaml(); + yaml.set("key1", 123); + assertEquals( (byte)123, yaml.getByte("key1")); + yaml.set("key2.key3", 3.14); + assertTrue(3.14F - yaml.getFloat("key2.key3") < 1e-8); + yaml.set("list.0", "line1"); + yaml.set("list.1", "line2"); + assertEquals("line1", yaml.getString("list.0")); + assertEquals("line2", yaml.getString("list.1")); + } + + @Test + public void testGenerator() throws IOException { + Writer writer = new FileWriter("build/test.yml"); + Generator generator = new YamlGenerator(node); + String text1 = writeToString(generator); + generator.generate(writer); + Reader reader = new FileReader("build/test.yml"); + Parser parser = new YamlParser(); + String text2 = writeToString(new YamlGenerator(parser.parse(reader))); + assertEquals(text1, text2); + } + + @Test + public void testBuilder() throws IOException { + List list = List.of("Text", Map.of("key", "val"), List.of("ListOfList")); + Yaml yaml = new Yaml(); + Builder builder = yaml.createBuilder(); + builder.buildCollection(list); + Parser parser = yaml.createParser(); + Node node = parser.parse(new StringReader(builder.build())); + String text1 = writeToString(new YamlGenerator(node)); + parser = new YamlParser(); + Node node1 = parser.parse(new StringReader(text1)); + String text2 = writeToString(new YamlGenerator(node1)); + assertEquals(text1, text2); + } + + @Test + public void testStructure() throws IOException { + DataStructure structure = new Yaml(); + Parser parser = structure.createParser(); + InputStream inputStream = YamlTest.class.getResourceAsStream("/org/xbib/datastructures/yaml/test/example.yml"); + if (inputStream == null) { + fail(); + } + structure.setRoot(parser.parse(new InputStreamReader(inputStream, StandardCharsets.UTF_8))); + structure.set("I.hate", "Yaml"); + structure.set("I.love", "Ruby"); + structure.set("I.love", "Java"); + structure.set("You.are", "Foolish"); + structure.set("a.0", "one"); + structure.set("a.1", "two"); + structure.set("a.2", "three"); + Generator generator = structure.createGenerator(structure.getRoot()); + generator.generate(new FileWriter("build/example.yml")); + structure.setRoot(parser.parse((new FileReader("build/example.yml")))); + assertEquals("Yaml", structure.getString("I.hate")); + assertEquals("Java", structure.getString("I.love")); + assertEquals("Foolish", structure.getString("You.are")); + assertNull(structure.getString("no.where")); + } + + @Test + public void testObject() throws Exception { + DataStructure structure = new Yaml(); + Builder builder = structure.createBuilder(); + builder.beginMap() + .buildKey("a") + .buildValue("b") + .endMap(); + String s = builder.build(); + assertEquals("a: b\n", s); + } + + private String writeToString(Generator generator) throws IOException { + StringWriter stringWriter = new StringWriter(); + generator.generate(stringWriter); + return stringWriter.toString(); + } +} diff --git a/datastructures-yaml/src/test/resources/org/xbib/datastructures/yaml/test/example.yml b/datastructures-yaml-tiny/src/test/resources/org/xbib/datastructures/yaml/test/example.yml similarity index 100% rename from datastructures-yaml/src/test/resources/org/xbib/datastructures/yaml/test/example.yml rename to datastructures-yaml-tiny/src/test/resources/org/xbib/datastructures/yaml/test/example.yml diff --git a/datastructures-yaml-tiny/src/test/resources/org/xbib/datastructures/yaml/test/hippie.yml b/datastructures-yaml-tiny/src/test/resources/org/xbib/datastructures/yaml/test/hippie.yml new file mode 100644 index 0000000..aa39368 --- /dev/null +++ b/datastructures-yaml-tiny/src/test/resources/org/xbib/datastructures/yaml/test/hippie.yml @@ -0,0 +1,10 @@ +de: + javahippie: + mpadmin: + instances: + - + name: "Bing" + uri: "https://bing.com" + - + name: "Google" + uri: "https://www.google.com" \ No newline at end of file diff --git a/datastructures-yaml/src/test/resources/org/xbib/datastructures/yaml/test/test.yml b/datastructures-yaml-tiny/src/test/resources/org/xbib/datastructures/yaml/test/test.yml similarity index 93% rename from datastructures-yaml/src/test/resources/org/xbib/datastructures/yaml/test/test.yml rename to datastructures-yaml-tiny/src/test/resources/org/xbib/datastructures/yaml/test/test.yml index e27bc2a..f4038e1 100644 --- a/datastructures-yaml/src/test/resources/org/xbib/datastructures/yaml/test/test.yml +++ b/datastructures-yaml-tiny/src/test/resources/org/xbib/datastructures/yaml/test/test.yml @@ -1,6 +1,6 @@ # here is a full test for the library! -# let's start form empty string value :) -test: +# let's start from empty string value :) +test: "" # key contains space is ok the key: the value @@ -80,5 +80,3 @@ hash1: # the treasure is deep - -# finally, this is an orphan comment (will be removed) \ No newline at end of file diff --git a/datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/Builder.java b/datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/Builder.java deleted file mode 100644 index f0083f4..0000000 --- a/datastructures-yaml/src/main/java/org/xbib/datastructures/yaml/Builder.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.xbib.datastructures.yaml; - -import java.util.List; -import java.util.Map; - -public class Builder { - - public Object object; - - public Builder(Object object) { - this.object = object; - } - - public Node build() { - return object == null ? null : internalBuild(null, object); - } - - @SuppressWarnings("unchecked") - private Node internalBuild(Node node, Object object) { - if (object instanceof List) { - ListNode listNode = new ListNode(node); - for (Object item : (List) object) { - listNode.add(internalBuild(listNode, item)); - } - return listNode; - } else if (object instanceof Map) { - MapNode mapNode = new MapNode(node); - for (Map.Entry kv : ((Map) object).entrySet()) { - mapNode.put(deliteral(kv.getKey().toString()), internalBuild(mapNode, kv.getValue())); - } - return mapNode; - } else { - return new ValueNode(node, object.toString()); - } - } - - private static String deliteral(String string) { - if (!string.contains(" ")) { - return string; - } - if (string.contains("'")) { - return "\"" + string.replace("\"", "\\\"") + "\""; - } - return "'" + string.replace("'", "\\'") + "'"; - } -} diff --git a/datastructures-yaml/src/test/java/org/xbib/datastructures/yaml/test/YamlTest.java b/datastructures-yaml/src/test/java/org/xbib/datastructures/yaml/test/YamlTest.java deleted file mode 100644 index ba0eac8..0000000 --- a/datastructures-yaml/src/test/java/org/xbib/datastructures/yaml/test/YamlTest.java +++ /dev/null @@ -1,176 +0,0 @@ -package org.xbib.datastructures.yaml.test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.xbib.datastructures.yaml.Builder; -import org.xbib.datastructures.yaml.Generator; -import org.xbib.datastructures.yaml.MapNode; -import org.xbib.datastructures.yaml.ListNode; -import org.xbib.datastructures.yaml.Node; -import org.xbib.datastructures.yaml.Parser; -import org.xbib.datastructures.yaml.ValueNode; -import org.xbib.datastructures.yaml.Yaml; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.StringReader; -import java.io.StringWriter; -import java.io.Writer; -import java.time.Instant; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class YamlTest { - - private static final Logger logger = Logger.getLogger(YamlTest.class.getName()); - - private Node root; - - private Yaml yaml; - - @BeforeEach - public void setup() throws IOException { - Reader reader = new InputStreamReader(YamlTest.class.getResourceAsStream("/org/xbib/datastructures/yaml/test/test.yml")); - Parser parser = new Parser(reader); - parser.parse(); - root = parser.getNode(); - yaml = new Yaml(root); - } - - @Test - public void testParser() { logger.log(Level.INFO, yaml.toString()); } - - @Test - public void testNode() { - MapNode hsnode; - ListNode lsnode; - ValueNode txnode; - hsnode = (MapNode) root; - txnode = (ValueNode) hsnode.get("test"); - assertEquals("", txnode.get()); - hsnode = (MapNode) root; - hsnode = (MapNode) hsnode.get("types"); - txnode = (ValueNode) hsnode.get("multiline"); - assertEquals("line 1 line 2 line 3", txnode.get()); - txnode = (ValueNode) hsnode.get("text"); - assertEquals("def func(x) do\n # do something\n print x = x * 2\nend", txnode.get()); - hsnode = (MapNode) root; - lsnode = (ListNode) hsnode.get("hash1"); - hsnode = (MapNode) lsnode.get(0); - lsnode = (ListNode) hsnode.get("hash2"); - txnode = (ValueNode) lsnode.get(0); - assertEquals("the treasure is deep", txnode.get()); - txnode.set("new val"); - assertEquals("new val", txnode.get()); - } - - @Test - public void testReadTree() { - assertEquals("", yaml.getString("test")); - assertEquals("the value", yaml.getString("the key")); - assertTrue(yaml.getBoolean("types.bool")); - assertEquals((byte) -1, yaml.getByte("types.byte")); - assertEquals((short)(3200), yaml.getShort("types.short")); - assertEquals(-2100000000, yaml.getInteger("types.int")); - assertEquals(1234321425321L, yaml.getLong("types.long")); - assertEquals(3.1415926F, yaml.getFloat("types.float")); - assertEquals(123413.4567654567654, yaml.getDouble("types.double")); - assertEquals('c', yaml.getCharacter("types.char")); - assertEquals("this is a string", yaml.getString("types.string")); - assertEquals("line 1 line 2 line 3", yaml.getString("types.multiline")); - assertEquals("def func(x) do\n # do something\n print x = x * 2\nend", yaml.getString("types.text")); - assertEquals(Instant.parse("1997-01-06T23:12:10Z"), yaml.getInstant("types.datetime")); - assertEquals("Text", yaml.getString("list.0")); - assertEquals("val", yaml.getString("list.1.key")); - assertEquals("ListOfList", yaml.getString("list.2.0")); - assertEquals("Text", yaml.getString("hash.name1")); - assertEquals("val", yaml.getString("hash.name2.key")); - assertEquals("HashOfList", yaml.getString("hash.name3.0")); - assertEquals("the treasure is deep", yaml.getString("hash1.0.hash2.0")); - } - - @Test - public void testWriteTree() { - Yaml yaml = new Yaml(); - yaml.set("key1", 123); - assertEquals( (byte)123, yaml.getByte("key1")); - yaml.set("key2.key3", 3.14); - assertTrue(3.14F - yaml.getFloat("key2.key3") < 1e-8); - yaml.set("list.0", "line1"); - yaml.set("list.1", "line2"); - assertEquals("line1", yaml.getString("list.0")); - assertEquals("line2", yaml.getString("list.1")); - logger.log(Level.INFO, yaml.toString()); - } - - @Test - public void testGenerator() throws IOException { - Writer writer = new FileWriter("build/test.yml"); - Generator generator = new Generator(root); - String text1 = writeToString(generator); - logger.log(Level.INFO, "text1 = " + text1); - generator.generate(writer); - Reader reader = new FileReader("build/test.yml"); - Parser parser = new Parser(reader); - parser.parse(); - String text2 = writeToString(new Generator(parser.getNode())); - logger.log(Level.INFO, "text2 = " + text2); - assertEquals(text1, text2); - } - - @Test - public void testBuilder() throws IOException { - List struct = List.of("Text", Map.of("key", "val"), List.of("ListOfList")); - Builder builder = new Builder(struct); - Yaml yaml = new Yaml(builder.build()); - Formatter formatter = new Formatter(); - logger.log(Level.INFO, "node1 = " + formatter.format(yaml.getRoot())); - String text1 = writeToString(new Generator(yaml.getRoot())); - logger.log(Level.INFO, "text1 = " + text1); - Parser parser = new Parser(new StringReader(text1)); - parser.parse(); - Node node = parser.getNode(); - logger.log(Level.INFO, "node2 = " + formatter.format(node)); - String text2 = writeToString(new Generator(node)); - logger.log(Level.INFO, "text2 = " + text2); - assertEquals(text1, text2); - } - - @Test - public void testYamlSetter() throws IOException { - Yaml yaml = new Yaml(); - yaml.read(new InputStreamReader(YamlTest.class.getResourceAsStream("/org/xbib/datastructures/yaml/test/example.yml"))); - yaml.set("I.hate", "Yaml"); - yaml.set("I.love", "Ruby"); - yaml.set("I.love", "Java"); - yaml.set("You.are", "Foolish"); - yaml.set("a.0", "one"); - yaml.set("a.1", "two"); - yaml.set("a.2", "three"); - yaml.save(new FileWriter("build/example.yml")); - yaml.read(new FileReader("build/example.yml")); - assertEquals("Yaml", yaml.getString("I.hate")); - assertEquals("Java", yaml.getString("I.love")); - assertEquals("Foolish", yaml.getString("You.are")); - assertNull(yaml.getString("no.where")); - assertEquals("novalue", yaml.getString("no.where", "novalue")); - } - - private String writeToString(Generator generator) { - StringWriter stringWriter = new StringWriter(); - try { - generator.generate(stringWriter); - } catch (IOException e) { - // ignore - } - return stringWriter.toString(); - } - -} diff --git a/gradle.properties b/gradle.properties index 36cd346..e366705 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ group = org.xbib name = datastructures -version = 0.2.0 +version = 1.0.0 gradle.wrapper.version = 6.6.1 mockito.version = 3.10.0 diff --git a/settings.gradle b/settings.gradle index 911572d..a7a2a39 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,19 +1,21 @@ +include 'datastructures-api' include 'datastructures-io' include 'datastructures-bytes' include 'datastructures-charset' include 'datastructures-common' -include 'datastructures-tiny' -include 'datastructures-yaml' include 'datastructures-xml' include 'datastructures-csv' include 'datastructures-xslx' -include 'datastructures-json' include 'datastructures-json-dsl' include 'datastructures-json-flat' include 'datastructures-json-iterator' +include 'datastructures-json-jackson' include 'datastructures-json-micro' include 'datastructures-json-minimal' include 'datastructures-json-noggit' include 'datastructures-json-simple' include 'datastructures-queue-tape' +include 'datastructures-tiny' +include 'datastructures-json-tiny' +include 'datastructures-yaml-tiny' include 'benchmark'