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 {
implementation project(':datastructures-json')
implementation project(':datastructures-json-tiny')
implementation project(':datastructures-json-dsl')
implementation project(':datastructures-json-flat')
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.Timeout;
import org.openjdk.jmh.annotations.Warmup;
import org.xbib.datastructures.json.EmptyJsonListener;
import org.xbib.datastructures.json.StandardJsonListener;
import org.xbib.datastructures.json.StringParser;
import org.xbib.datastructures.json.TinyJsonListener;
import org.xbib.datastructures.json.tiny.EmptyJsonListener;
import org.xbib.datastructures.json.tiny.StandardJsonListener;
import org.xbib.datastructures.json.tiny.StringParser;
import org.xbib.datastructures.json.tiny.TinyJsonListener;
import org.xbib.datastructures.json.flat.Json;
import org.xbib.datastructures.json.noggit.ObjectBuilder;
import org.xbib.datastructures.json.simple.JSONParser;

View file

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

View file

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

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> {

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

View file

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

View file

@ -1,3 +1,4 @@
dependencies {
api project(':datastructures-api')
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;
public class EmptyJsonListener implements JsonDeserializer {
public class EmptyJsonListener implements JsonResult {
@Override
public void begin() {
@ -48,18 +50,15 @@ public class EmptyJsonListener implements JsonDeserializer {
}
@Override
public void beginList() {
public void beginCollection() {
}
@Override
public void endList() {
public void endCollection() {
}
@Override
public void beginMap() {
}
@Override
@ -67,7 +66,6 @@ public class EmptyJsonListener implements JsonDeserializer {
}
@Override
public Deque<Node<?>> getStack() {
return null;
}

View file

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

View file

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

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 {
@ -20,9 +20,9 @@ public interface JsonListener {
void onDouble(Double value);
void beginList();
void beginCollection();
void endList();
void endCollection();
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> {
@ -12,6 +14,11 @@ public class KeyNode implements Node<CharSequence> {
this.value = value;
}
@Override
public int getDepth() {
return 0;
}
@Override
public CharSequence get() {
return value;
@ -19,6 +26,6 @@ public class KeyNode implements Node<CharSequence> {
@Override
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.LinkedList;
public class StandardJsonListener implements JsonDeserializer {
public class StandardJsonListener implements JsonResult {
private Node<?> node;
@ -15,7 +17,6 @@ public class StandardJsonListener implements JsonDeserializer {
private final ValueNode FALSE_NODE = new ValueNode(Boolean.FALSE);
@Override
public Deque<Node<?>> getStack() {
return stack;
}
@ -69,12 +70,12 @@ public class StandardJsonListener implements JsonDeserializer {
}
@Override
public void beginList() {
public void beginCollection() {
stack.push(new StandardListNode());
}
@Override
public void endList() {
public void endCollection() {
node = stack.pop();
tryAppend(node);
}

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.List;
@ -14,6 +16,11 @@ public class StandardListNode extends ArrayList<Object> implements Node<List<Obj
return contains(node);
}
@Override
public int getDepth() {
return 0;
}
@Override
public List<Object> get() {
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.Map;
@ -10,6 +12,11 @@ public class StandardMapNode extends LinkedHashMap<CharSequence, Object> impleme
return containsKey(name);
}
@Override
public int getDepth() {
return 0;
}
@Override
public Map<CharSequence, Object> get() {
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.Reader;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
public class StreamParser {
public class StreamParser implements Parser {
private final JsonDeserializer listener;
private final JsonResult listener;
private Reader reader;
@ -18,15 +19,12 @@ public class StreamParser {
this(new TinyJsonListener());
}
public StreamParser(JsonDeserializer listener) {
public StreamParser(JsonResult listener) {
this.listener = listener;
}
public Node<?> getNode() {
return listener.getResult();
}
public void parse(Reader reader) throws JsonException, IOException {
@Override
public Node<?> parse(Reader reader) throws IOException {
Objects.requireNonNull(reader);
Objects.requireNonNull(listener);
this.reader = reader;
@ -39,6 +37,7 @@ public class StreamParser {
throw new JsonException("malformed json: " + ch);
}
listener.end();
return listener.getResult();
}
private void parseValue() throws IOException, JsonException {
@ -189,12 +188,12 @@ public class StreamParser {
private void parseList() throws IOException {
int count = 0;
listener.beginList();
listener.beginCollection();
ch = reader.read();
while (true) {
skipWhitespace();
if (ch == ']') {
listener.endList();
listener.endCollection();
ch = reader.read();
return;
}
@ -270,9 +269,9 @@ public class StreamParser {
ch = reader.read();
}
private void expectChar(int expected) throws JsonException {
private void expectChar(char expected) throws JsonException {
if (ch != expected) {
throw new JsonException("expected char " + (char)expected + " but got " + (char)ch + " " + listener.getStack());
throw new JsonException("expected char " + (char)expected + " but got " + (char)ch);
}
}

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

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

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;
@ -8,7 +8,13 @@ public class ValueNode implements Node<Object> {
this.value = value;
}
public void setValue(Object value) {
@Override
public int getDepth() {
return 0;
}
@Override
public void set(Object 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.json.KeyNode;
import org.xbib.datastructures.json.ListNode;
import org.xbib.datastructures.json.MapNode;
import org.xbib.datastructures.json.Node;
import org.xbib.datastructures.json.ValueNode;
import org.xbib.datastructures.api.Node;
import org.xbib.datastructures.json.tiny.TinyJsonListener;
import org.xbib.datastructures.json.tiny.KeyNode;
import org.xbib.datastructures.json.tiny.ListNode;
import org.xbib.datastructures.json.tiny.MapNode;
import org.xbib.datastructures.json.tiny.ValueNode;
import java.util.Stack;
import java.util.logging.Logger;
@ -19,9 +19,9 @@ public class DebugJsonListener extends TinyJsonListener {
private final ValueNode NULL_NODE = new ValueNode(null);
private final ValueNode TRUE_NODE = new ValueNode(Boolean.TRUE);
private final ValueNode TRUE_NODE = new ValueNode(Boolean.TRUE.toString());
private final ValueNode FALSE_NODE = new ValueNode(Boolean.FALSE);
private final ValueNode FALSE_NODE = new ValueNode(Boolean.FALSE.toString());
public Node<?> getResult() {
return node;
@ -49,26 +49,26 @@ public class DebugJsonListener extends TinyJsonListener {
@Override
public void onValue(CharSequence value) {
valueNode(new ValueNode(value));
valueNode(new ValueNode(value.toString()));
}
@Override
public void onLong(Long value) {
valueNode(new ValueNode(value));
valueNode(new ValueNode(value.toString()));
}
@Override
public void onDouble(Double value) {
valueNode(new ValueNode(value));
valueNode(new ValueNode(value.toString()));
}
@Override
public void beginList() {
public void beginCollection() {
stack.push(new ListNode());
}
@Override
public void endList() {
public void endCollection() {
node = stack.pop();
tryAppend(node);
}

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

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 {
api project(':datastructures-common')
}

View file

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

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

View file

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

View file

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

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

View file

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

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

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

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

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!
# let's start form empty string value :)
test:
# let's start from empty string value :)
test: ""
# key contains space is ok
the key: the value
@ -80,5 +80,3 @@ hash1:
# <hs.ls.hs.ls.Text>
the treasure
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
name = datastructures
version = 0.2.0
version = 1.0.0
gradle.wrapper.version = 6.6.1
mockito.version = 3.10.0

View file

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