implement lightweight, minimal JSON/YAML on tiny datastructures

This commit is contained in:
Jörg Prante 2021-10-12 11:41:30 +02:00
parent 44b7ae7de2
commit f9adea4e18
83 changed files with 2744 additions and 1142 deletions

View file

@ -8,7 +8,7 @@ jmhReport {
} }
dependencies { dependencies {
implementation project(':datastructures-json') implementation project(':datastructures-json-tiny')
implementation project(':datastructures-json-dsl') implementation project(':datastructures-json-dsl')
implementation project(':datastructures-json-flat') implementation project(':datastructures-json-flat')
implementation project(':datastructures-json-iterator') implementation project(':datastructures-json-iterator')

View file

@ -15,10 +15,10 @@ import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Threads; import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.annotations.Timeout; import org.openjdk.jmh.annotations.Timeout;
import org.openjdk.jmh.annotations.Warmup; import org.openjdk.jmh.annotations.Warmup;
import org.xbib.datastructures.json.EmptyJsonListener; import org.xbib.datastructures.json.tiny.EmptyJsonListener;
import org.xbib.datastructures.json.StandardJsonListener; import org.xbib.datastructures.json.tiny.StandardJsonListener;
import org.xbib.datastructures.json.StringParser; import org.xbib.datastructures.json.tiny.StringParser;
import org.xbib.datastructures.json.TinyJsonListener; import org.xbib.datastructures.json.tiny.TinyJsonListener;
import org.xbib.datastructures.json.flat.Json; import org.xbib.datastructures.json.flat.Json;
import org.xbib.datastructures.json.noggit.ObjectBuilder; import org.xbib.datastructures.json.noggit.ObjectBuilder;
import org.xbib.datastructures.json.simple.JSONParser; import org.xbib.datastructures.json.simple.JSONParser;

View file

@ -15,10 +15,10 @@ import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Threads; import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.annotations.Warmup; import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.annotations.Timeout; import org.openjdk.jmh.annotations.Timeout;
import org.xbib.datastructures.json.EmptyJsonListener; import org.xbib.datastructures.json.tiny.EmptyJsonListener;
import org.xbib.datastructures.json.StandardJsonListener; import org.xbib.datastructures.json.tiny.StandardJsonListener;
import org.xbib.datastructures.json.StringParser; import org.xbib.datastructures.json.tiny.StringParser;
import org.xbib.datastructures.json.TinyJsonListener; import org.xbib.datastructures.json.tiny.TinyJsonListener;
import org.xbib.datastructures.json.flat.Json; import org.xbib.datastructures.json.flat.Json;
import org.xbib.datastructures.json.noggit.ObjectBuilder; import org.xbib.datastructures.json.noggit.ObjectBuilder;
import org.xbib.datastructures.json.simple.JSONParser; import org.xbib.datastructures.json.simple.JSONParser;

View file

@ -15,10 +15,10 @@ import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Threads; import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.annotations.Timeout; import org.openjdk.jmh.annotations.Timeout;
import org.openjdk.jmh.annotations.Warmup; import org.openjdk.jmh.annotations.Warmup;
import org.xbib.datastructures.json.EmptyJsonListener; import org.xbib.datastructures.json.tiny.EmptyJsonListener;
import org.xbib.datastructures.json.StandardJsonListener; import org.xbib.datastructures.json.tiny.StandardJsonListener;
import org.xbib.datastructures.json.StringParser; import org.xbib.datastructures.json.tiny.StringParser;
import org.xbib.datastructures.json.TinyJsonListener; import org.xbib.datastructures.json.tiny.TinyJsonListener;
import org.xbib.datastructures.json.flat.Json; import org.xbib.datastructures.json.flat.Json;
import org.xbib.datastructures.json.noggit.ObjectBuilder; import org.xbib.datastructures.json.noggit.ObjectBuilder;
import org.xbib.datastructures.json.simple.JSONParser; import org.xbib.datastructures.json.simple.JSONParser;

View file

@ -0,0 +1,3 @@
module org.xbib.datastructures.api {
exports org.xbib.datastructures.api;
}

View file

@ -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<String, Object> map) throws IOException;
Builder buildCollection(Collection<Object> collection) throws IOException;
Builder buildKey(CharSequence charSequence) throws IOException;
Builder buildValue(Object object) throws IOException;
Builder buildNull() throws IOException;
String build();
}

View file

@ -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);
}

View file

@ -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;
}
}

View file

@ -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<String> 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);
}

View file

@ -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;
}

View file

@ -0,0 +1,6 @@
package org.xbib.datastructures.api;
import java.util.List;
public interface ListNode extends Node<List<Node<?>>> {
}

View file

@ -0,0 +1,6 @@
package org.xbib.datastructures.api;
import java.util.Map;
public interface MapNode extends Node<Map<CharSequence, Node<?>>> {
}

View file

@ -1,4 +1,4 @@
package org.xbib.datastructures.yaml; package org.xbib.datastructures.api;
public interface Node<T> { public interface Node<T> {

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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;
}
}

View file

@ -0,0 +1,9 @@
package org.xbib.datastructures.api;
public interface ValueNode extends Node<Object> {
void set(Object value);
@Override
Object get();
}

View file

@ -25,14 +25,14 @@ public class Reader {
private static final Map<Character, Character> escapes = new HashMap<Character, Character>(); private static final Map<Character, Character> escapes = new HashMap<Character, Character>();
static { static {
escapes.put(new Character('"'), new Character('"')); escapes.put('"', '"');
escapes.put(new Character('\\'), new Character('\\')); escapes.put('\\', '\\');
escapes.put(new Character('/'), new Character('/')); escapes.put('/', '/');
escapes.put(new Character('b'), new Character('\b')); escapes.put('b', '\b');
escapes.put(new Character('f'), new Character('\f')); escapes.put('f', '\f');
escapes.put(new Character('n'), new Character('\n')); escapes.put('n', '\n');
escapes.put(new Character('r'), new Character('\r')); escapes.put('r', '\r');
escapes.put(new Character('t'), new Character('\t')); escapes.put('t', '\t');
} }
private CharacterIterator it; private CharacterIterator it;
@ -272,9 +272,9 @@ public class Reader {
if (c == 'u') { if (c == 'u') {
add(unicode()); add(unicode());
} else { } else {
Object value = escapes.get(new Character(c)); Character value = escapes.get(c);
if (value != null) { if (value != null) {
add(((Character) value).charValue()); add(value);
} }
} }
} else { } else {

View file

@ -15,7 +15,7 @@ public class JSONParser {
public static final int LONG = 2; public static final int LONG = 2;
/** /**
* Event indicating a JSON number value which has a fractional part or an exponent * 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 &lt;= 23 chars not including sign. This covers
* all representations of normal values for Double.toString(). * all representations of normal values for Double.toString().
*/ */
public static final int NUMBER = 3; public static final int NUMBER = 3;

View file

@ -1,3 +1,4 @@
dependencies { dependencies {
api project(':datastructures-api')
api project(':datastructures-tiny') api project(':datastructures-tiny')
} }

View file

@ -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;
}

View file

@ -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; import java.util.Deque;
public class EmptyJsonListener implements JsonDeserializer { public class EmptyJsonListener implements JsonResult {
@Override @Override
public void begin() { public void begin() {
@ -48,18 +50,15 @@ public class EmptyJsonListener implements JsonDeserializer {
} }
@Override @Override
public void beginList() { public void beginCollection() {
} }
@Override @Override
public void endList() { public void endCollection() {
} }
@Override @Override
public void beginMap() { public void beginMap() {
} }
@Override @Override
@ -67,7 +66,6 @@ public class EmptyJsonListener implements JsonDeserializer {
} }
@Override
public Deque<Node<?>> getStack() { public Deque<Node<?>> getStack() {
return null; return null;
} }

View file

@ -1,4 +1,4 @@
package org.xbib.datastructures.json; package org.xbib.datastructures.json.tiny;
import java.util.Objects; import java.util.Objects;

View file

@ -1,4 +1,4 @@
package org.xbib.datastructures.json; package org.xbib.datastructures.json.tiny;
public class FastDoubleParser { public class FastDoubleParser {

View file

@ -1,11 +1,12 @@
package org.xbib.datastructures.json; package org.xbib.datastructures.json.tiny;
import java.io.IOException; import org.xbib.datastructures.api.*;
import java.io.Reader;
import java.io.Writer; import java.io.StringWriter;
import java.time.Instant; import java.time.Instant;
import java.util.function.Consumer;
public class Json { public class Json implements DataStructure {
private final char separator; private final char separator;
@ -24,28 +25,49 @@ public class Json {
this.separator = separator; this.separator = separator;
} }
public void read(Reader reader) throws IOException { @Override
StreamParser streamParser = new StreamParser(); public Parser createParser() {
streamParser.parse(reader); return new StreamParser();
setRoot(streamParser.getNode());
} }
public void save(Writer writer) throws IOException { @Override
new Generator(getRoot(), writer).generate(); public Generator createGenerator(Node<?> root) {
return new JsonGenerator(root);
} }
@Override
public Builder createBuilder() {
return new JsonBuilder(new StringWriter());
}
@Override
public Builder createBuilder(Consumer<String> 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) { public void setRoot(Node<?> root) {
this.root = root; this.root = root;
} }
@Override
public Node<?> getRoot() { public Node<?> getRoot() {
return root; return root;
} }
public boolean exist(String path) { @Override
return getNode(path) != null; public Node<?> getNode(String path) {
return path == null ? null : internalGetNode(root, path);
} }
@Override
public Boolean getBoolean(String path) { public Boolean getBoolean(String path) {
String value = internalGet(path); String value = internalGet(path);
if (value == null) { if (value == null) {
@ -58,10 +80,7 @@ public class Json {
} }
} }
public Boolean getBoolean(String path, Boolean defaultval) { @Override
return getBoolean(path) != null ? getBoolean(path) : defaultval;
}
public Byte getByte(String path) { public Byte getByte(String path) {
String value = internalGet(path); String value = internalGet(path);
if (value == null) { if (value == null) {
@ -74,10 +93,7 @@ public class Json {
} }
} }
public Byte getByte(String path, Byte defaultval) { @Override
return getByte(path) != null ? getByte(path) : defaultval;
}
public Short getShort(String path) { public Short getShort(String path) {
String value = internalGet(path); String value = internalGet(path);
if (value == null) { if (value == null) {
@ -90,10 +106,7 @@ public class Json {
} }
} }
public Short getShort(String path, Short defaultval) { @Override
return getShort(path) != null ? getShort(path) : defaultval;
}
public Integer getInteger(String path) { public Integer getInteger(String path) {
String value = internalGet(path); String value = internalGet(path);
if (value == null) { if (value == null) {
@ -106,10 +119,7 @@ public class Json {
} }
} }
public Integer getInteger(String path, Integer defaultval) { @Override
return getInteger(path) != null ? getInteger(path) : defaultval;
}
public Long getLong(String path) { public Long getLong(String path) {
String value = internalGet(path); String value = internalGet(path);
if (value == null) { if (value == null) {
@ -122,10 +132,7 @@ public class Json {
} }
} }
public Long getLong(String path, Long defaultval) { @Override
return getLong(path) != null ? getLong(path) : defaultval;
}
public Float getFloat(String path) { public Float getFloat(String path) {
String value = internalGet(path); String value = internalGet(path);
if (value == null) { if (value == null) {
@ -138,10 +145,7 @@ public class Json {
} }
} }
public Float getFloat(String path, Float defaultval) { @Override
return getFloat(path) != null ? getFloat(path) : defaultval;
}
public Double getDouble(String path) { public Double getDouble(String path) {
String value = internalGet(path); String value = internalGet(path);
if (value == null) { if (value == null) {
@ -154,10 +158,7 @@ public class Json {
} }
} }
public Double getDouble(String path, Double defaultval) { @Override
return getDouble(path) != null ? getDouble(path) : defaultval;
}
public Character getCharacter(String path) { public Character getCharacter(String path) {
String value = internalGet(path); String value = internalGet(path);
if (value == null) { if (value == null) {
@ -170,18 +171,12 @@ public class Json {
} }
} }
public Character getCharacter(String path, Character defaultval) { @Override
return getCharacter(path) != null ? getCharacter(path) : defaultval;
}
public String getString(String path) { public String getString(String path) {
return internalGet(path); return internalGet(path);
} }
public String getString(String path, String defaultval) { @Override
return getString(path) != null ? getString(path) : defaultval;
}
public Instant getInstant(String path) { public Instant getInstant(String path) {
String value = internalGet(path); String value = internalGet(path);
if (value == null) { if (value == null) {
@ -190,11 +185,17 @@ public class Json {
return Instant.parse(value); return Instant.parse(value);
} }
public Instant getInstant(String path, Instant defaultval) { @Override
Instant instant = getInstant(path); public TimeValue getAsTime(String path, TimeValue defaultValue) {
return instant != null ? instant : defaultval; 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) { public boolean set(String path, Object value) {
return set(path, value, true); return set(path, value, true);
} }
@ -210,7 +211,7 @@ public class Json {
if (!(node instanceof ValueNode)) { if (!(node instanceof ValueNode)) {
return false; return false;
} }
ValueNode txnode = (ValueNode) node; ValueNode valueNode = (ValueNode) node;
if (value instanceof String if (value instanceof String
|| value instanceof Integer || value instanceof Integer
|| value instanceof Double || value instanceof Double
@ -220,12 +221,12 @@ public class Json {
|| value instanceof Short || value instanceof Short
|| value instanceof Character || value instanceof Character
|| value instanceof Byte) { || value instanceof Byte) {
txnode.setValue(String.valueOf(value)); valueNode.set(String.valueOf(value));
} else if (value instanceof Instant) { } else if (value instanceof Instant) {
Instant instant = (Instant) value; Instant instant = (Instant) value;
txnode.setValue(instant.toString()); valueNode.set(instant.toString());
} else { } else {
txnode.setValue(value == null ? null : value.toString()); valueNode.set(value == null ? null : value.toString());
} }
return true; return true;
} }
@ -234,10 +235,6 @@ public class Json {
return set(path, instant.toString()); return set(path, instant.toString());
} }
public Node<?> getNode(String path) {
return path == null ? null : internalGetNode(root, path);
}
private Node<?> internalGetNode(Node<?> node, String path) { private Node<?> internalGetNode(Node<?> node, String path) {
if (node == null || path.isEmpty()) { if (node == null || path.isEmpty()) {
return node; return node;
@ -257,7 +254,7 @@ public class Json {
return null; return null;
} }
public void makeNode(String path) { private void makeNode(String path) {
if (path != null) { if (path != null) {
internalMakeNode(root, path); internalMakeNode(root, path);
} }
@ -341,7 +338,7 @@ public class Json {
} }
private String internalGet(String path) { private String internalGet(String path) {
Node<?> node = getNode(dequote(path)); Node<?> node = getNode(unquote(path));
return node instanceof ValueNode ? node.toString() : null; return node instanceof ValueNode ? node.toString() : null;
} }
@ -352,7 +349,7 @@ public class Json {
return new String[]{seg, rest}; return new String[]{seg, rest};
} }
private static String dequote(String string) { private static String unquote(String string) {
if (string.startsWith("'") && string.endsWith("'") if (string.startsWith("'") && string.endsWith("'")
|| string.startsWith("\"") && string.endsWith("\"")) { || string.startsWith("\"") && string.endsWith("\"")) {
return string.substring(1, string.length() - 1); return string.substring(1, string.length() - 1);

View file

@ -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<String, Object> 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<Object> 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<String, Object>) object);
} else if (object instanceof Collection) {
buildCollection((Collection<Object>) 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;
}
}
}

View file

@ -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);
}
}

View file

@ -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<CharSequence, Node<?>> 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();
}
}
}

View file

@ -1,4 +1,4 @@
package org.xbib.datastructures.json; package org.xbib.datastructures.json.tiny;
public interface JsonListener { public interface JsonListener {
@ -20,9 +20,9 @@ public interface JsonListener {
void onDouble(Double value); void onDouble(Double value);
void beginList(); void beginCollection();
void endList(); void endCollection();
void beginMap(); void beginMap();

View file

@ -0,0 +1,8 @@
package org.xbib.datastructures.json.tiny;
import org.xbib.datastructures.api.Node;
public interface JsonResult extends JsonListener {
Node<?> getResult();
}

View file

@ -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<CharSequence> { public class KeyNode implements Node<CharSequence> {
@ -12,6 +14,11 @@ public class KeyNode implements Node<CharSequence> {
this.value = value; this.value = value;
} }
@Override
public int getDepth() {
return 0;
}
@Override @Override
public CharSequence get() { public CharSequence get() {
return value; return value;
@ -19,6 +26,6 @@ public class KeyNode implements Node<CharSequence> {
@Override @Override
public String toString() { public String toString() {
return value.toString(); return value != null ? value.toString() : null;
} }
} }

View file

@ -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<Node<?>> 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<Node<?>> get() {
return this;
}
}

View file

@ -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<CharSequence, Node<?>> implements org.xbib.datastructures.api.MapNode {
public boolean has(String name) {
return containsKey(name);
}
@Override
public int getDepth() {
return 0;
}
@Override
public Map<CharSequence, Node<?>> get() {
return this;
}
}

View file

@ -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.Deque;
import java.util.LinkedList; import java.util.LinkedList;
public class StandardJsonListener implements JsonDeserializer { public class StandardJsonListener implements JsonResult {
private Node<?> node; private Node<?> node;
@ -15,7 +17,6 @@ public class StandardJsonListener implements JsonDeserializer {
private final ValueNode FALSE_NODE = new ValueNode(Boolean.FALSE); private final ValueNode FALSE_NODE = new ValueNode(Boolean.FALSE);
@Override
public Deque<Node<?>> getStack() { public Deque<Node<?>> getStack() {
return stack; return stack;
} }
@ -69,12 +70,12 @@ public class StandardJsonListener implements JsonDeserializer {
} }
@Override @Override
public void beginList() { public void beginCollection() {
stack.push(new StandardListNode()); stack.push(new StandardListNode());
} }
@Override @Override
public void endList() { public void endCollection() {
node = stack.pop(); node = stack.pop();
tryAppend(node); tryAppend(node);
} }

View file

@ -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.ArrayList;
import java.util.List; import java.util.List;
@ -14,6 +16,11 @@ public class StandardListNode extends ArrayList<Object> implements Node<List<Obj
return contains(node); return contains(node);
} }
@Override
public int getDepth() {
return 0;
}
@Override @Override
public List<Object> get() { public List<Object> get() {
return this; return this;

View file

@ -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.LinkedHashMap;
import java.util.Map; import java.util.Map;
@ -10,6 +12,11 @@ public class StandardMapNode extends LinkedHashMap<CharSequence, Object> impleme
return containsKey(name); return containsKey(name);
} }
@Override
public int getDepth() {
return 0;
}
@Override @Override
public Map<CharSequence, Object> get() { public Map<CharSequence, Object> get() {
return this; return this;

View file

@ -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.IOException;
import java.io.Reader; import java.io.Reader;
import java.util.Objects; 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; private Reader reader;
@ -18,15 +19,12 @@ public class StreamParser {
this(new TinyJsonListener()); this(new TinyJsonListener());
} }
public StreamParser(JsonDeserializer listener) { public StreamParser(JsonResult listener) {
this.listener = listener; this.listener = listener;
} }
public Node<?> getNode() { @Override
return listener.getResult(); public Node<?> parse(Reader reader) throws IOException {
}
public void parse(Reader reader) throws JsonException, IOException {
Objects.requireNonNull(reader); Objects.requireNonNull(reader);
Objects.requireNonNull(listener); Objects.requireNonNull(listener);
this.reader = reader; this.reader = reader;
@ -39,6 +37,7 @@ public class StreamParser {
throw new JsonException("malformed json: " + ch); throw new JsonException("malformed json: " + ch);
} }
listener.end(); listener.end();
return listener.getResult();
} }
private void parseValue() throws IOException, JsonException { private void parseValue() throws IOException, JsonException {
@ -189,12 +188,12 @@ public class StreamParser {
private void parseList() throws IOException { private void parseList() throws IOException {
int count = 0; int count = 0;
listener.beginList(); listener.beginCollection();
ch = reader.read(); ch = reader.read();
while (true) { while (true) {
skipWhitespace(); skipWhitespace();
if (ch == ']') { if (ch == ']') {
listener.endList(); listener.endCollection();
ch = reader.read(); ch = reader.read();
return; return;
} }
@ -270,9 +269,9 @@ public class StreamParser {
ch = reader.read(); ch = reader.read();
} }
private void expectChar(int expected) throws JsonException { private void expectChar(char expected) throws JsonException {
if (ch != expected) { 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);
} }
} }

View file

@ -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; import java.util.Objects;
@ -22,7 +24,7 @@ public class StringParser {
private static final char COLON = ':'; private static final char COLON = ':';
private final JsonDeserializer deserializer; private final JsonResult listener;
private String input; private String input;
@ -30,16 +32,16 @@ public class StringParser {
private char ch; private char ch;
public StringParser(JsonDeserializer deserializer) { public StringParser(JsonResult listener) {
this.deserializer = deserializer; this.listener = listener;
} }
public void parse(String input) throws JsonException { public void parse(String input) throws JsonException {
Objects.requireNonNull(input); Objects.requireNonNull(input);
Objects.requireNonNull(deserializer); Objects.requireNonNull(listener);
this.input = input; this.input = input;
this.i = 0; this.i = 0;
deserializer.begin(); listener.begin();
ch = next(); ch = next();
skipWhitespace(); skipWhitespace();
parseValue(); parseValue();
@ -47,11 +49,11 @@ public class StringParser {
if (ch != EOS) { if (ch != EOS) {
throw new JsonException("malformed json: " + ch); throw new JsonException("malformed json: " + ch);
} }
deserializer.end(); listener.end();
} }
public Node<?> getNode() { public Node<?> getNode() {
return deserializer.getResult(); return listener.getResult();
} }
private void parseValue() throws JsonException { private void parseValue() throws JsonException {
@ -139,9 +141,9 @@ public class StringParser {
throw new JsonException("isolated minus"); throw new JsonException("isolated minus");
} }
if (dot || exponent) { if (dot || exponent) {
deserializer.onDouble(FastDoubleParser.parseDouble(input.substring(start, i - 1))); listener.onDouble(FastDoubleParser.parseDouble(input.substring(start, i - 1)));
} else { } 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) { if (escaped) {
CharSequence s = unescape(input.substring(start, i - 1)); CharSequence s = unescape(input.substring(start, i - 1));
if (isKey) { if (isKey) {
deserializer.onKey(s); listener.onKey(s);
} else { } else {
deserializer.onValue(s); listener.onValue(s);
} }
} else { } else {
if (isKey) { if (isKey) {
deserializer.onKey(input.substring(start, i - 1)); listener.onKey(input.substring(start, i - 1));
} else { } else {
deserializer.onValue(input.substring(start, i - 1)); listener.onValue(input.substring(start, i - 1));
} }
} }
ch = next(); ch = next();
@ -189,12 +191,12 @@ public class StringParser {
private void parseList() { private void parseList() {
int count = 0; int count = 0;
deserializer.beginList(); listener.beginCollection();
ch = next(); ch = next();
while (true) { while (true) {
skipWhitespace(); skipWhitespace();
if (ch == CLOSE_LIST) { if (ch == CLOSE_LIST) {
deserializer.endList(); listener.endCollection();
ch = next(); ch = next();
return; return;
} }
@ -210,12 +212,12 @@ public class StringParser {
private void parseMap() { private void parseMap() {
int count = 0; int count = 0;
deserializer.beginMap(); listener.beginMap();
ch = next(); ch = next();
while (true) { while (true) {
skipWhitespace(); skipWhitespace();
if (ch == CLOSE_MAP) { if (ch == CLOSE_MAP) {
deserializer.endMap(); listener.endMap();
ch = next(); ch = next();
return; return;
} }
@ -243,7 +245,7 @@ public class StringParser {
expectChar('l'); expectChar('l');
ch = next(); ch = next();
expectChar('l'); expectChar('l');
deserializer.onNull(); listener.onNull();
ch = next(); ch = next();
} }
@ -254,7 +256,7 @@ public class StringParser {
expectChar('u'); expectChar('u');
ch = next(); ch = next();
expectChar('e'); expectChar('e');
deserializer.onTrue(); listener.onTrue();
ch = next(); ch = next();
} }
@ -267,13 +269,13 @@ public class StringParser {
expectChar('s'); expectChar('s');
ch = next(); ch = next();
expectChar('e'); expectChar('e');
deserializer.onFalse(); listener.onFalse();
ch = next(); ch = next();
} }
private void expectChar(char expected) throws JsonException { private void expectChar(char expected) throws JsonException {
if (ch != expected) { if (ch != expected) {
throw new JsonException("expected char " + expected + " but got " + ch + " " + deserializer.getStack()); throw new JsonException("expected char " + expected + " but got " + ch);
} }
} }

View file

@ -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.Deque;
import java.util.LinkedList; import java.util.LinkedList;
public class TinyJsonListener implements JsonDeserializer { public class TinyJsonListener implements JsonResult {
private Node<?> node; private Node<?> node;
@ -15,7 +17,6 @@ public class TinyJsonListener implements JsonDeserializer {
private final ValueNode FALSE_NODE = new ValueNode(Boolean.FALSE); private final ValueNode FALSE_NODE = new ValueNode(Boolean.FALSE);
@Override
public Deque<Node<?>> getStack() { public Deque<Node<?>> getStack() {
return stack; return stack;
} }
@ -70,12 +71,12 @@ public class TinyJsonListener implements JsonDeserializer {
} }
@Override @Override
public void beginList() { public void beginCollection() {
stack.push(new ListNode()); stack.push(new ListNode());
} }
@Override @Override
public void endList() { public void endCollection() {
node = stack.pop(); node = stack.pop();
tryAppend(node); tryAppend(node);
} }

View file

@ -1,6 +1,6 @@
package org.xbib.datastructures.json; package org.xbib.datastructures.json.tiny;
public class ValueNode implements Node<Object> { public class ValueNode implements org.xbib.datastructures.api.ValueNode {
private Object value; private Object value;
@ -8,7 +8,13 @@ public class ValueNode implements Node<Object> {
this.value = value; this.value = value;
} }
public void setValue(Object value) { @Override
public int getDepth() {
return 0;
}
@Override
public void set(Object value) {
this.value = value; this.value = value;
} }

View file

@ -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.api.Node;
import org.xbib.datastructures.json.KeyNode; import org.xbib.datastructures.json.tiny.TinyJsonListener;
import org.xbib.datastructures.json.ListNode; import org.xbib.datastructures.json.tiny.KeyNode;
import org.xbib.datastructures.json.MapNode; import org.xbib.datastructures.json.tiny.ListNode;
import org.xbib.datastructures.json.Node; import org.xbib.datastructures.json.tiny.MapNode;
import org.xbib.datastructures.json.ValueNode; import org.xbib.datastructures.json.tiny.ValueNode;
import java.util.Stack; import java.util.Stack;
import java.util.logging.Logger; 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 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() { public Node<?> getResult() {
return node; return node;
@ -49,26 +49,26 @@ public class DebugJsonListener extends TinyJsonListener {
@Override @Override
public void onValue(CharSequence value) { public void onValue(CharSequence value) {
valueNode(new ValueNode(value)); valueNode(new ValueNode(value.toString()));
} }
@Override @Override
public void onLong(Long value) { public void onLong(Long value) {
valueNode(new ValueNode(value)); valueNode(new ValueNode(value.toString()));
} }
@Override @Override
public void onDouble(Double value) { public void onDouble(Double value) {
valueNode(new ValueNode(value)); valueNode(new ValueNode(value.toString()));
} }
@Override @Override
public void beginList() { public void beginCollection() {
stack.push(new ListNode()); stack.push(new ListNode());
} }
@Override @Override
public void endList() { public void endCollection() {
node = stack.pop(); node = stack.pop();
tryAppend(node); tryAppend(node);
} }

View file

@ -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());
}
}

View file

@ -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));
}
}
}
}

View file

@ -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.junit.jupiter.api.Test;
import org.xbib.datastructures.json.TinyJsonListener; import org.xbib.datastructures.json.tiny.TinyJsonListener;
import org.xbib.datastructures.json.StreamParser; import org.xbib.datastructures.json.tiny.StreamParser;
import org.xbib.datastructures.json.StringParser; import org.xbib.datastructures.json.tiny.StringParser;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -16,7 +16,7 @@ public class ParserTest {
@Test @Test
public void testStringParser() throws IOException { 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(); byte [] b = inputStream.readAllBytes();
String string = new String(b, StandardCharsets.UTF_8); String string = new String(b, StandardCharsets.UTF_8);
StringParser stringParser = new StringParser(new TinyJsonListener()); StringParser stringParser = new StringParser(new TinyJsonListener());
@ -29,10 +29,9 @@ public class ParserTest {
@Test @Test
public void testStreamParser() throws IOException { 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 streamParser = new StreamParser(new TinyJsonListener());
streamParser.parse(reader); Logger.getLogger("").log(Level.INFO, streamParser.parse(reader).get().toString());
Logger.getLogger("").log(Level.INFO, streamParser.getNode().get().toString());
} }
} }
} }

View file

@ -1,5 +0,0 @@
module org.xbib.datastructures.json {
exports org.xbib.datastructures.json;
requires org.xbib.datastructures.tiny;
requires java.logging;
}

View file

@ -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<String, Object> 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<Object> 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();
}
}

View file

@ -1,11 +0,0 @@
package org.xbib.datastructures.json;
import java.util.Deque;
public interface JsonDeserializer extends JsonListener {
Deque<Node<?>> getStack();
Node<?> getResult();
}

View file

@ -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);
}
}

View file

@ -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<Object> implements Node<List<Object>> {
public boolean has(int i) {
return i >= 0 && i < size() && get(i) == null;
}
public boolean has(Node<?> node) {
return contains(node);
}
@Override
public List<Object> get() {
return this;
}
}

View file

@ -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<CharSequence, Object> implements Node<Map<CharSequence, Object>> {
public boolean has(String name) {
return containsKey(name);
}
@Override
public Map<CharSequence, Object> get() {
return this;
}
}

View file

@ -1,6 +0,0 @@
package org.xbib.datastructures.json;
public interface Node<T> {
T get();
}

View file

@ -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());
}
}

View file

@ -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));
}
}
}
}

View file

@ -1,4 +1,3 @@
dependencies { dependencies {
api project(':datastructures-common') api project(':datastructures-common')
} }

View file

@ -1,5 +1,4 @@
module org.xbib.datastructures.tiny { module org.xbib.datastructures.tiny {
exports org.xbib.datastructures.tiny; exports org.xbib.datastructures.tiny;
requires org.xbib.datastructures.common; requires org.xbib.datastructures.common;
requires java.logging;
} }

View file

@ -0,0 +1,7 @@
This is a modfied work of
https://github.com/Kahsolt/IfYaml
as of May, 2021
WTFPL License

View file

@ -1,3 +1,4 @@
dependencies { dependencies {
api project(':datastructures-api')
api project(':datastructures-tiny') api project(':datastructures-tiny')
} }

View file

@ -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;
}

View file

@ -1,4 +1,4 @@
package org.xbib.datastructures.yaml; package org.xbib.datastructures.yaml.tiny;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
@ -54,6 +54,9 @@ public class Lexer {
} }
boolean isPipe = false; boolean isPipe = false;
switch (token.getType()) { switch (token.getType()) {
case DOCUMENT_START:
case DOCUMENT_END:
return token;
case VALUE: case VALUE:
List<String> values = new ArrayList<>(); List<String> values = new ArrayList<>();
int indent = token.getDepth(); int indent = token.getDepth();
@ -65,7 +68,7 @@ public class Lexer {
} else { } else {
break; break;
} }
} while ((token = nextToken()) != null && token.getDepth() > prevToken.getDepth()); } while ((token = nextToken()) != null && prevToken != null && token.getDepth() > prevToken.getDepth());
nextToken = token; nextToken = token;
tokens.add(values.size() == 1 tokens.add(values.size() == 1
? new Token(TokenType.VALUE_LINE, indent, values.get(0)) ? new Token(TokenType.VALUE_LINE, indent, values.get(0))
@ -145,6 +148,12 @@ public class Lexer {
indent = index; indent = index;
read(); read();
return new Token(TokenType.ANGLE, indent); 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()) { } else if (isText()) {
indent = index; indent = index;
value = extractText(); value = extractText();
@ -251,6 +260,10 @@ public class Lexer {
return ch(index + 1); return ch(index + 1);
} }
private char nextNextChar() {
return ch(index + 2);
}
private boolean hasLeftGap() { private boolean hasLeftGap() {
return prevChar() == ' ' || prevChar() == BOL; return prevChar() == ' ' || prevChar() == BOL;
} }
@ -279,6 +292,14 @@ public class Lexer {
return current() == '-'; return current() == '-';
} }
private boolean isDocumentStart() {
return current() == '-' && nextChar() == '-' && nextNextChar() == '-';
}
private boolean isDocumentEnd() {
return current() == '.' && nextChar() == '.' && nextNextChar() == '.';
}
private boolean isColon() { private boolean isColon() {
return current() == ':'; return current() == ':';
} }

View file

@ -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 org.xbib.datastructures.tiny.TinyList;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
public class ListNode implements Node<List<Node<?>>> { public class ListNode implements org.xbib.datastructures.api.ListNode {
private final int depth; private final int depth;

View file

@ -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 org.xbib.datastructures.tiny.TinyMap;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@ -7,11 +8,11 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
public class MapNode implements Node<Map<String, Node<?>>> { public class MapNode implements org.xbib.datastructures.api.MapNode {
private final int depth; private final int depth;
private final TinyMap.Builder<String, Node<?>> map; private final TinyMap.Builder<CharSequence, Node<?>> map;
private final TinyMap.Builder<String, List<String>> comments; private final TinyMap.Builder<String, List<String>> comments;
@ -25,7 +26,7 @@ public class MapNode implements Node<Map<String, Node<?>>> {
return depth; return depth;
} }
public Map<String, Node<?>> get() { public Map<CharSequence, Node<?>> get() {
return map.build(); return map.build();
} }
@ -33,9 +34,9 @@ public class MapNode implements Node<Map<String, Node<?>>> {
return comments.build(); return comments.build();
} }
public List<Map.Entry<String, Map.Entry<Node<?>, List<String>>>> getChildCommentPairs() { public List<Map.Entry<CharSequence, Map.Entry<Node<?>, List<String>>>> getChildCommentPairs() {
List<Map.Entry<String, Map.Entry<Node<?>, List<String>>>> pairs = new ArrayList<>(); List<Map.Entry<CharSequence, Map.Entry<Node<?>, List<String>>>> pairs = new ArrayList<>();
for (Map.Entry<String, Node<?>> kv : map.entrySet()) { for (Map.Entry<CharSequence, Node<?>> kv : map.entrySet()) {
pairs.add(Map.entry(kv.getKey(), Map.entry(kv.getValue(), comments.getOrDefault(kv.getKey(), Collections.emptyList())))); pairs.add(Map.entry(kv.getKey(), Map.entry(kv.getValue(), comments.getOrDefault(kv.getKey(), Collections.emptyList()))));
} }
return pairs; return pairs;

View file

@ -1,4 +1,4 @@
package org.xbib.datastructures.yaml; package org.xbib.datastructures.yaml.tiny;
public class Token { public class Token {
@ -29,4 +29,9 @@ public class Token {
public String getValue() { public String getValue() {
return value; return value;
} }
@Override
public String toString() {
return type.name();
}
} }

View file

@ -1,6 +1,8 @@
package org.xbib.datastructures.yaml; package org.xbib.datastructures.yaml.tiny;
public enum TokenType { public enum TokenType {
DOCUMENT_START,
DOCUMENT_END,
ITEM, ITEM,
KEY, KEY,
VALUE, VALUE,

View file

@ -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.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class ValueNode implements Node<String> { public class ValueNode implements org.xbib.datastructures.api.ValueNode {
private final int depth; private final int depth;
private String value; private Object value;
private TextType type; private TextType type;
@ -34,12 +36,13 @@ public class ValueNode implements Node<String> {
return depth; return depth;
} }
public void set(String value) { @Override
public void set(Object value) {
this.value = value; this.value = value;
} }
@Override @Override
public String get() { public Object get() {
return value; return value;
} }

View file

@ -1,11 +1,13 @@
package org.xbib.datastructures.yaml; package org.xbib.datastructures.yaml.tiny;
import java.io.IOException; import org.xbib.datastructures.api.*;
import java.io.Reader; import org.xbib.datastructures.api.Builder;
import java.io.Writer;
import java.io.StringWriter;
import java.time.Instant; import java.time.Instant;
import java.util.function.Consumer;
public class Yaml { public class Yaml implements DataStructure {
private final char separator; private final char separator;
@ -24,28 +26,49 @@ public class Yaml {
this.separator = separator; this.separator = separator;
} }
public void read(Reader reader) throws IOException { @Override
Parser parser = new Parser(reader); public Parser createParser() {
parser.parse(); return new YamlParser();
setRoot(parser.getNode());
} }
public void save(Writer writer) throws IOException { @Override
new Generator(getRoot()).generate(writer); public Builder createBuilder() {
return new YamlBuilder(new StringWriter());
} }
@Override
public Builder createBuilder(Consumer<String> 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) { public void setRoot(Node<?> root) {
this.root = root; this.root = root;
} }
@Override
public Node<?> getRoot() { public Node<?> getRoot() {
return root; return root;
} }
public boolean exist(String path) { @Override
return getNode(path) != null; public Node<?> getNode(String path) {
return path == null ? null : internalGetNode(root, path);
} }
@Override
public Boolean getBoolean(String path) { public Boolean getBoolean(String path) {
String value = internalGet(path); String value = internalGet(path);
if (value == null) { if (value == null) {
@ -58,10 +81,7 @@ public class Yaml {
} }
} }
public Boolean getBoolean(String path, Boolean defaultval) { @Override
return getBoolean(path) != null ? getBoolean(path) : defaultval;
}
public Byte getByte(String path) { public Byte getByte(String path) {
String value = internalGet(path); String value = internalGet(path);
if (value == null) { if (value == null) {
@ -74,10 +94,7 @@ public class Yaml {
} }
} }
public Byte getByte(String path, Byte defaultval) { @Override
return getByte(path) != null ? getByte(path) : defaultval;
}
public Short getShort(String path) { public Short getShort(String path) {
String value = internalGet(path); String value = internalGet(path);
if (value == null) { if (value == null) {
@ -90,10 +107,7 @@ public class Yaml {
} }
} }
public Short getShort(String path, Short defaultval) { @Override
return getShort(path) != null ? getShort(path) : defaultval;
}
public Integer getInteger(String path) { public Integer getInteger(String path) {
String value = internalGet(path); String value = internalGet(path);
if (value == null) { if (value == null) {
@ -106,10 +120,7 @@ public class Yaml {
} }
} }
public Integer getInteger(String path, Integer defaultval) { @Override
return getInteger(path) != null ? getInteger(path) : defaultval;
}
public Long getLong(String path) { public Long getLong(String path) {
String value = internalGet(path); String value = internalGet(path);
if (value == null) { if (value == null) {
@ -122,10 +133,7 @@ public class Yaml {
} }
} }
public Long getLong(String path, Long defaultval) { @Override
return getLong(path) != null ? getLong(path) : defaultval;
}
public Float getFloat(String path) { public Float getFloat(String path) {
String value = internalGet(path); String value = internalGet(path);
if (value == null) { if (value == null) {
@ -138,10 +146,7 @@ public class Yaml {
} }
} }
public Float getFloat(String path, Float defaultval) { @Override
return getFloat(path) != null ? getFloat(path) : defaultval;
}
public Double getDouble(String path) { public Double getDouble(String path) {
String value = internalGet(path); String value = internalGet(path);
if (value == null) { if (value == null) {
@ -154,10 +159,7 @@ public class Yaml {
} }
} }
public Double getDouble(String path, Double defaultval) { @Override
return getDouble(path) != null ? getDouble(path) : defaultval;
}
public Character getCharacter(String path) { public Character getCharacter(String path) {
String value = internalGet(path); String value = internalGet(path);
if (value == null) { if (value == null) {
@ -170,18 +172,12 @@ public class Yaml {
} }
} }
public Character getCharacter(String path, Character defaultval) { @Override
return getCharacter(path) != null ? getCharacter(path) : defaultval;
}
public String getString(String path) { public String getString(String path) {
return internalGet(path); return internalGet(path);
} }
public String getString(String path, String defaultval) { @Override
return getString(path) != null ? getString(path) : defaultval;
}
public Instant getInstant(String path) { public Instant getInstant(String path) {
String value = internalGet(path); String value = internalGet(path);
if (value == null) { if (value == null) {
@ -190,11 +186,17 @@ public class Yaml {
return Instant.parse(value); return Instant.parse(value);
} }
public Instant getInstant(String path, Instant defaultval) { @Override
Instant instant = getInstant(path); public TimeValue getAsTime(String path, TimeValue defaultValue) {
return instant != null ? instant : defaultval; 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) { public boolean set(String path, Object value) {
return set(path, value, true); return set(path, value, true);
} }
@ -202,7 +204,6 @@ public class Yaml {
private boolean set(String path, Object value, boolean rebuild) { private boolean set(String path, Object value, boolean rebuild) {
if (rebuild) { if (rebuild) {
if (value == null) { if (value == null) {
//return removeNode(path);
throw new IllegalStateException("value is null"); throw new IllegalStateException("value is null");
} }
makeNode(path); makeNode(path);
@ -231,14 +232,6 @@ public class Yaml {
return true; 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) { private Node<?> internalGetNode(Node<?> node, String path) {
if (node == null || path.isEmpty()) { if (node == null || path.isEmpty()) {
return node; return node;
@ -258,7 +251,7 @@ public class Yaml {
return null; return null;
} }
public void makeNode(String path) { private void makeNode(String path) {
if (path != null) { if (path != null) {
internalMakeNode(root, path); internalMakeNode(root, path);
} }
@ -341,8 +334,8 @@ public class Yaml {
} }
private String internalGet(String path) { private String internalGet(String path) {
Node<?> node = getNode(dequote(path)); Node<?> node = getNode(unquote(path));
return node instanceof ValueNode ? ((ValueNode) node).get() : null; return node instanceof ValueNode ? ((ValueNode) node).get().toString() : null;
} }
private static String[] cut(String string, char delimiter) { private static String[] cut(String string, char delimiter) {
@ -352,7 +345,7 @@ public class Yaml {
return new String[]{seg, rest}; return new String[]{seg, rest};
} }
private static String dequote(String string) { private static String unquote(String string) {
if (string.startsWith("'") && string.endsWith("'") if (string.startsWith("'") && string.endsWith("'")
|| string.startsWith("\"") && string.endsWith("\"")) { || string.startsWith("\"") && string.endsWith("\"")) {
return string.substring(1, string.length() - 1); return string.substring(1, string.length() - 1);

View file

@ -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<String, Object> 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<Object> 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<String, Object>) object);
} else if (object instanceof Collection) {
buildCollection((Collection<Object>) 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;
}
}
}

View file

@ -1,4 +1,4 @@
package org.xbib.datastructures.yaml; package org.xbib.datastructures.yaml.tiny;
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class YamlException extends RuntimeException { public class YamlException extends RuntimeException {

View file

@ -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.IOException;
import java.io.StringWriter;
import java.io.Writer; import java.io.Writer;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
public class Generator { public class YamlGenerator implements Generator {
private final Node<?> root; private final Node<?> root;
private final int indent; private final int indent;
public Generator(Node<?> root) { private Writer writer;
this(root, 2);
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.root = root;
this.writer = writer;
this.indent = indent; this.indent = indent;
} }
@Override
public void generate(Writer writer) throws IOException { public void generate(Writer writer) throws IOException {
this.writer = writer;
try (writer) { try (writer) {
if (root != null) { if (root != null) {
if (internalWrite(writer, root, null)) { if (internalWrite(root, null)) {
writer.append('\n'); 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) { if (curnode == null) {
return false; return false;
} }
boolean lf = false; boolean lf = false;
String indent = " ".repeat(curnode.getDepth() * this.indent); String indent = " ".repeat(curnode.getDepth() * this.indent);
if (curnode instanceof ValueNode) { if (curnode instanceof ValueNode) {
ValueNode txnode = (ValueNode) curnode; ValueNode valueNode = (ValueNode) curnode;
lf = writeComments(appendable, txnode.getComments(), indent, false); lf = writeComments(valueNode.getComments(), indent, false);
switch (txnode.getType()) { switch (valueNode.getType()) {
case LINE: case LINE: {
if (lf) { 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; break;
case MULTILINE: }
case MULTILINE: {
if (lf) { if (lf) {
appendable.append('\n'); writer.append('\n');
} }
String s = prefix(linewrap(txnode.get()), indent); Object object = valueNode.get();
appendable.append(s); String s = object == null ? "null" : object.toString();
writer.append(prefix(linewrap(s), indent));
break; break;
}
case TEXT: case TEXT:
case TEXT_ANGLE: case TEXT_ANGLE:
if (txnode.getType() == TextType.TEXT) { if (valueNode.getType() == ValueNode.TextType.TEXT) {
appendable.append("|\n"); writer.append("|\n");
} }
if (txnode.getType() == TextType.TEXT_ANGLE) { if (valueNode.getType() == ValueNode.TextType.TEXT_ANGLE) {
appendable.append(">\n"); 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; break;
} }
return true; return true;
} else if (curnode instanceof MapNode) { } else if (curnode instanceof MapNode) {
MapNode mapNode = (MapNode) curnode; MapNode mapNode = (MapNode) curnode;
for (Map.Entry<String, Map.Entry<Node<?>, List<String>>> knc : mapNode.getChildCommentPairs()) { for (Map.Entry<CharSequence, Map.Entry<Node<?>, List<String>>> knc : mapNode.getChildCommentPairs()) {
lf = writeComments(appendable, knc.getValue().getValue(), indent, lf); lf = writeComments(knc.getValue().getValue(), indent, lf);
if (lf || (prevnode != null && !(prevnode instanceof ListNode))) { if (lf || (prevnode != null && !(prevnode instanceof ListNode))) {
appendable.append("\n").append(indent); writer.append("\n").append(indent);
} }
appendable.append(knc.getKey()).append(": "); writer.append(knc.getKey()).append(": ");
lf = internalWrite(appendable, knc.getValue().getKey(), mapNode); lf = internalWrite(knc.getValue().getKey(), mapNode);
} }
return lf; return lf;
} else if (curnode instanceof ListNode) { } else if (curnode instanceof ListNode) {
ListNode listNode = (ListNode) curnode; ListNode listNode = (ListNode) curnode;
for (Map.Entry<Node<?>, List<String>> nc : listNode.getItemCommentPairs()) { for (Map.Entry<Node<?>, List<String>> nc : listNode.getItemCommentPairs()) {
lf = writeComments(appendable, nc.getValue(), indent, lf); lf = writeComments(nc.getValue(), indent, lf);
if (lf || (prevnode != null && !(prevnode instanceof ListNode))) { if (lf || (prevnode != null && !(prevnode instanceof ListNode))) {
appendable.append("\n").append(indent); writer.append("\n").append(indent);
} }
appendable.append("- "); writer.append("- ");
lf = internalWrite(appendable, nc.getKey(), listNode); lf = internalWrite(nc.getKey(), listNode);
} }
return lf; return lf;
} }
return false; return false;
} }
private boolean writeComments(Appendable appendable, List<String> comments, String indent, boolean lf) throws IOException { private boolean writeComments(List<String> comments, String indent, boolean lf) throws IOException {
if (comments == null) { if (comments == null) {
return false; return false;
} }
for (String comment : comments) { for (String comment : comments) {
if (lf) { if (lf) {
appendable.append('\n'); writer.append('\n');
} }
appendable.append(indent).append('#').append(comment); writer.append(indent).append('#').append(comment);
lf = true; lf = true;
} }
return lf; return lf;
@ -141,5 +156,4 @@ public class Generator {
} }
return sb.toString(); return sb.toString();
} }
} }

View file

@ -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.IOException;
import java.io.Reader; import java.io.Reader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.ListIterator; 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<Token> comments; private final List<Token> comments;
private Node<?> root; private Lexer lexer;
private Token token; private Token token;
public Parser(Reader reader) { public YamlParser() {
lexer = new Lexer(reader);
comments = new ArrayList<>(); comments = new ArrayList<>();
} }
public void parse() throws IOException { public Node<?> parse(Reader reader) throws IOException {
root = parseNode(null, -1); lexer = new Lexer(reader);
} return parseNode(null, -1);
public Node<?> getNode() {
return root;
} }
private Node<?> parseNode(Node<?> parent, int depth) throws IOException { private Node<?> parseNode(Node<?> parent, int depth) throws IOException {
@ -37,10 +35,14 @@ public class Parser {
return null; return null;
} }
} }
if (token.getDepth() <= depth) { if (token.getDepth() < depth) {
return new ValueNode(parent); return new ValueNode(parent);
} }
switch (token.getType()) { switch (token.getType()) {
case DOCUMENT_START:
case DOCUMENT_END:
token = lexer.next();
return parseNode(parent, depth);
case COMMENT: case COMMENT:
stashComments(); stashComments();
return parseNode(parent, depth); return parseNode(parent, depth);
@ -62,7 +64,7 @@ public class Parser {
private ListNode parseListNode(Node<?> parent, int indent) throws IOException { private ListNode parseListNode(Node<?> parent, int indent) throws IOException {
ListNode node = new ListNode(parent); ListNode node = new ListNode(parent);
int cnt = 0; 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(); token = lexer.next();
if (token != null) { if (token != null) {
node.addComments(cnt, collectComments(indent)); node.addComments(cnt, collectComments(indent));
@ -93,27 +95,28 @@ public class Parser {
node.addComments(collectComments(indent)); node.addComments(collectComments(indent));
switch (token.getType()) { switch (token.getType()) {
case VALUE_LINE: case VALUE_LINE:
node.setType(TextType.LINE); node.setType(ValueNode.TextType.LINE);
node.set(token.getValue()); node.set(token.getValue());
token = lexer.next(); token = lexer.next();
break; break;
case VALUE_MULTILINE: case VALUE_MULTILINE:
node.setType(TextType.MULTILINE); node.setType(ValueNode.TextType.MULTILINE);
node.set(token.getValue()); node.set(token.getValue());
token = lexer.next(); token = lexer.next();
break; break;
case VALUE_TEXT_PIPE: case VALUE_TEXT_PIPE:
node.setType(TextType.TEXT); node.setType(ValueNode.TextType.TEXT);
node.set(token.getValue()); node.set(token.getValue());
token = lexer.next(); token = lexer.next();
break; break;
case VALUE_TEXT_ANGLE: case VALUE_TEXT_ANGLE:
node.setType(TextType.TEXT_ANGLE); node.setType(ValueNode.TextType.TEXT_ANGLE);
node.set(token.getValue()); node.set(token.getValue());
token = lexer.next(); token = lexer.next();
break; break;
default: default:
node.set(""); node.set("");
break;
} }
return node; return node;
} }

View file

@ -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.tiny.MapNode;
import org.xbib.datastructures.yaml.ListNode; import org.xbib.datastructures.yaml.tiny.ListNode;
import org.xbib.datastructures.yaml.Node; import org.xbib.datastructures.api.Node;
import org.xbib.datastructures.yaml.ValueNode; import org.xbib.datastructures.yaml.tiny.ValueNode;
import java.util.List;
import java.util.Map; import java.util.Map;
public class Formatter { public class Formatter {
public String format(Node node) { public String format(Node<?> node) {
if (node instanceof ValueNode) { if (node instanceof ValueNode) {
return format((ValueNode) node); return format((ValueNode) node);
} }
@ -27,6 +29,7 @@ public class Formatter {
sb.append(indent); sb.append(indent);
sb.append("<TextNode value="); sb.append("<TextNode value=");
if (valueNode.get() != null) { if (valueNode.get() != null) {
String s = excerpt(valueNode.get()); String s = excerpt(valueNode.get());
s = escape(s); s = escape(s);
s = quote(s, true); s = quote(s, true);
@ -104,7 +107,7 @@ public class Formatter {
if (!mapNode.get().isEmpty()) { if (!mapNode.get().isEmpty()) {
sb.append('{'); sb.append('{');
boolean isFirst = true; boolean isFirst = true;
for (Map.Entry<String, Node<?>> kv : mapNode.get().entrySet()) { for (Map.Entry<CharSequence, Node<?>> kv : mapNode.get().entrySet()) {
if (!isFirst) { if (!isFirst) {
sb.append(", "); sb.append(", ");
} }
@ -119,22 +122,27 @@ public class Formatter {
return sb.toString(); return sb.toString();
} }
private String toStringOfComments(MapNode mapNode, String name) { private String toStringOfComments(MapNode mapNode, CharSequence charSequence) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
if (mapNode.getComments().get(name) != null && !mapNode.getComments().get(name).isEmpty()) { List<String> comments = mapNode.getComments().get(charSequence.toString());
if (comments != null && !comments.isEmpty()) {
sb.append("comments="); sb.append("comments=");
StringBuilder comments = new StringBuilder(); StringBuilder commentStr = new StringBuilder();
for (String comment : mapNode.getComments().get(name)) { for (String comment : comments) {
comments.append(comment).append(' '); commentStr.append(comment).append(' ');
} }
String s = excerpt(comments.toString()); String s = excerpt(commentStr.toString());
s = quote(s, true); s = quote(s, true);
sb.append(s); sb.append(s);
} }
return sb.toString(); 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(); int len = string.length();
if (len <= 24) { if (len <= 24) {
return excerpt(string, 24); return excerpt(string, 24);
@ -168,5 +176,4 @@ public class Formatter {
private String spaces(int count) { private String spaces(int count) {
return " ".repeat(count); return " ".repeat(count);
} }
} }

View file

@ -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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> mapBuilder1 = TinyMap.builder();
mapBuilder1.put("a", "b");
mapBuilder1.put("c", "d");
TinyMap.Builder<String, Object> 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());
}
}

View file

@ -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);
}
}

View file

@ -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<Object> 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();
}
}

View file

@ -0,0 +1,10 @@
de:
javahippie:
mpadmin:
instances:
-
name: "Bing"
uri: "https://bing.com"
-
name: "Google"
uri: "https://www.google.com"

View file

@ -1,6 +1,6 @@
# here is a full test for the library! # here is a full test for the library!
# let's start form empty string value :) # let's start from empty string value :)
test: test: ""
# key contains space is ok # key contains space is ok
the key: the value the key: the value
@ -80,5 +80,3 @@ hash1:
# <hs.ls.hs.ls.Text> # <hs.ls.hs.ls.Text>
the treasure the treasure
is deep is deep
# finally, this is an orphan comment (will be removed)

View file

@ -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>) object) {
listNode.add(internalBuild(listNode, item));
}
return listNode;
} else if (object instanceof Map) {
MapNode mapNode = new MapNode(node);
for (Map.Entry<Object, Object> kv : ((Map<Object, Object>) 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("'", "\\'") + "'";
}
}

View file

@ -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<Object> 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();
}
}

View file

@ -1,6 +1,6 @@
group = org.xbib group = org.xbib
name = datastructures name = datastructures
version = 0.2.0 version = 1.0.0
gradle.wrapper.version = 6.6.1 gradle.wrapper.version = 6.6.1
mockito.version = 3.10.0 mockito.version = 3.10.0

View file

@ -1,19 +1,21 @@
include 'datastructures-api'
include 'datastructures-io' include 'datastructures-io'
include 'datastructures-bytes' include 'datastructures-bytes'
include 'datastructures-charset' include 'datastructures-charset'
include 'datastructures-common' include 'datastructures-common'
include 'datastructures-tiny'
include 'datastructures-yaml'
include 'datastructures-xml' include 'datastructures-xml'
include 'datastructures-csv' include 'datastructures-csv'
include 'datastructures-xslx' include 'datastructures-xslx'
include 'datastructures-json'
include 'datastructures-json-dsl' include 'datastructures-json-dsl'
include 'datastructures-json-flat' include 'datastructures-json-flat'
include 'datastructures-json-iterator' include 'datastructures-json-iterator'
include 'datastructures-json-jackson'
include 'datastructures-json-micro' include 'datastructures-json-micro'
include 'datastructures-json-minimal' include 'datastructures-json-minimal'
include 'datastructures-json-noggit' include 'datastructures-json-noggit'
include 'datastructures-json-simple' include 'datastructures-json-simple'
include 'datastructures-queue-tape' include 'datastructures-queue-tape'
include 'datastructures-tiny'
include 'datastructures-json-tiny'
include 'datastructures-yaml-tiny'
include 'benchmark' include 'benchmark'