implement lightweight, minimal JSON/YAML on tiny datastructures
This commit is contained in:
parent
44b7ae7de2
commit
f9adea4e18
83 changed files with 2744 additions and 1142 deletions
|
@ -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')
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
3
datastructures-api/src/main/java/module-info.java
Normal file
3
datastructures-api/src/main/java/module-info.java
Normal file
|
@ -0,0 +1,3 @@
|
|||
module org.xbib.datastructures.api {
|
||||
exports org.xbib.datastructures.api;
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package org.xbib.datastructures.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ListNode extends Node<List<Node<?>>> {
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package org.xbib.datastructures.api;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface MapNode extends Node<Map<CharSequence, Node<?>>> {
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package org.xbib.datastructures.yaml;
|
||||
package org.xbib.datastructures.api;
|
||||
|
||||
public interface Node<T> {
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package org.xbib.datastructures.api;
|
||||
|
||||
public interface ValueNode extends Node<Object> {
|
||||
|
||||
void set(Object value);
|
||||
|
||||
@Override
|
||||
Object get();
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -15,7 +15,7 @@ public class JSONParser {
|
|||
public static final int LONG = 2;
|
||||
/**
|
||||
* Event indicating a JSON number value which has a fractional part or an exponent
|
||||
* and with string length <= 23 chars not including sign. This covers
|
||||
* and with string length <= 23 chars not including sign. This covers
|
||||
* all representations of normal values for Double.toString().
|
||||
*/
|
||||
public static final int NUMBER = 3;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
dependencies {
|
||||
api project(':datastructures-api')
|
||||
api project(':datastructures-tiny')
|
||||
}
|
9
datastructures-json-tiny/src/main/java/module-info.java
Normal file
9
datastructures-json-tiny/src/main/java/module-info.java
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package org.xbib.datastructures.json;
|
||||
package org.xbib.datastructures.json.tiny;
|
||||
|
||||
import java.util.Objects;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package org.xbib.datastructures.json;
|
||||
package org.xbib.datastructures.json.tiny;
|
||||
|
||||
public class FastDoubleParser {
|
||||
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package org.xbib.datastructures.json.tiny;
|
||||
|
||||
import org.xbib.datastructures.api.Node;
|
||||
|
||||
public interface JsonResult extends JsonListener {
|
||||
|
||||
Node<?> getResult();
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
module org.xbib.datastructures.json {
|
||||
exports org.xbib.datastructures.json;
|
||||
requires org.xbib.datastructures.tiny;
|
||||
requires java.logging;
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package org.xbib.datastructures.json;
|
||||
|
||||
import java.util.Deque;
|
||||
|
||||
public interface JsonDeserializer extends JsonListener {
|
||||
|
||||
Deque<Node<?>> getStack();
|
||||
|
||||
Node<?> getResult();
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
package org.xbib.datastructures.json;
|
||||
|
||||
public interface Node<T> {
|
||||
|
||||
T get();
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
dependencies {
|
||||
api project(':datastructures-common')
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
module org.xbib.datastructures.tiny {
|
||||
exports org.xbib.datastructures.tiny;
|
||||
requires org.xbib.datastructures.common;
|
||||
requires java.logging;
|
||||
}
|
||||
|
|
7
datastructures-yaml-tiny/NOTICE.txt
Normal file
7
datastructures-yaml-tiny/NOTICE.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
This is a modfied work of
|
||||
|
||||
https://github.com/Kahsolt/IfYaml
|
||||
|
||||
as of May, 2021
|
||||
|
||||
WTFPL License
|
|
@ -1,3 +1,4 @@
|
|||
dependencies {
|
||||
api project(':datastructures-api')
|
||||
api project(':datastructures-tiny')
|
||||
}
|
9
datastructures-yaml-tiny/src/main/java/module-info.java
Normal file
9
datastructures-yaml-tiny/src/main/java/module-info.java
Normal 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;
|
||||
}
|
|
@ -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() == ':';
|
||||
}
|
|
@ -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;
|
||||
|
|
@ -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;
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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,
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package org.xbib.datastructures.yaml;
|
||||
package org.xbib.datastructures.yaml.tiny;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class YamlException extends RuntimeException {
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
de:
|
||||
javahippie:
|
||||
mpadmin:
|
||||
instances:
|
||||
-
|
||||
name: "Bing"
|
||||
uri: "https://bing.com"
|
||||
-
|
||||
name: "Google"
|
||||
uri: "https://www.google.com"
|
|
@ -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)
|
|
@ -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("'", "\\'") + "'";
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
Loading…
Reference in a new issue