initial commit

This commit is contained in:
2018-09-09 13:37:13 +01:00
commit 5087e092c1
31 changed files with 1991 additions and 0 deletions

33
build.sbt Normal file
View File

@@ -0,0 +1,33 @@
name := "worth"
organization := "org.oggio88"
version := "1.0"
resolvers += Resolver.mavenLocal
scalaVersion := "2.12.6"
scalacOptions ++= Seq(
"-unchecked",
"-deprecation",
"-language:_",
"-opt:l:inline", "-opt-inline-from",
"-target:jvm-1.8",
"-encoding", "UTF-8"
)
git.useGitDescribe := true
fork := true
//javaOptions in Test += "-Dorg.oggio88.javason.value.ObjectValue.listBasedImplementation=true"
javaOptions in Test += "-Xmx6G"
//scalafmtOnCompile := true
libraryDependencies += "org.projectlombok" % "lombok" % "1.18.2"
libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test"
libraryDependencies += "com.fasterxml.jackson.core" % "jackson-databind" % "2.9.6" % "test"
libraryDependencies += "org.antlr" % "antlr4" % "4.7.1" % "compile"
libraryDependencies += "org.antlr" % "antlr4-runtime" % "4.7.1" % "test"
libraryDependencies += "org.tukaani" % "xz" % "1.8" % "test"
artifactName := { (sv: ScalaVersion, module: ModuleID, artifact: Artifact) =>
artifact.name + "-" + module.revision + "." + artifact.extension
}

9
project/plugins.sbt Normal file
View File

@@ -0,0 +1,9 @@
//addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "4.0.2")
addSbtPlugin("com.dwijnand" % "sbt-travisci" % "1.1.1")
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.0")
addSbtPlugin("com.lucidchart" % "sbt-scalafmt" % "1.15")
//addSbtPlugin("com.geirsson" % "sbt-scalafmt" % "1.5.1")
addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "0.9.3")
addSbtPlugin("de.heikoseeberger" % "sbt-header" % "4.1.0")
addSbtPlugin("org.foundweekends" % "sbt-bintray" % "0.5.3")
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.6")

View File

@@ -0,0 +1,38 @@
package org.oggio88.worth.buffer;
import lombok.SneakyThrows;
import java.io.Reader;
public class CircularBuffer {
private int[] buffer;
private Reader reader;
private int delta = 0, cursor = 0;
public CircularBuffer(Reader reader, int size) {
this.reader = reader;
buffer = new int[size];
}
@SneakyThrows
public int next() {
if (delta < 0)
return buffer[Math.floorMod(cursor + delta++, buffer.length)];
else {
int result = reader.read();
if (result < 0) return result;
buffer[cursor] = result;
cursor = (cursor + 1) % buffer.length;
return result;
}
}
public int prev() {
return buffer[cursor + --delta >= 0 ? cursor + delta : cursor + delta + buffer.length];
}
public int size() {
return buffer.length;
}
}

View File

@@ -0,0 +1,7 @@
package org.oggio88.worth.exception;
public class IOException extends WorthException {
public IOException(String msg) {
super(msg);
}
}

View File

@@ -0,0 +1,7 @@
package org.oggio88.worth.exception;
public class NotImplementedException extends WorthException {
public NotImplementedException(String msg) {
super(msg);
}
}

View File

@@ -0,0 +1,7 @@
package org.oggio88.worth.exception;
public class ParseException extends WorthException {
public ParseException(String msg) {
super(msg);
}
}

View File

@@ -0,0 +1,7 @@
package org.oggio88.worth.exception;
public class TypeException extends WorthException {
public TypeException(String msg) {
super(msg);
}
}

View File

@@ -0,0 +1,7 @@
package org.oggio88.worth.exception;
public class WorthException extends RuntimeException {
public WorthException(String msg) {
super(msg);
}
}

View File

@@ -0,0 +1,105 @@
package org.oggio88.worth.serialization;
import lombok.RequiredArgsConstructor;
import org.oggio88.worth.exception.NotImplementedException;
import org.oggio88.worth.value.ArrayValue;
import org.oggio88.worth.value.ObjectValue;
import org.oggio88.worth.xface.Dumper;
import org.oggio88.worth.xface.Value;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Map;
import java.util.Stack;
public abstract class ValueDumper implements Dumper {
@RequiredArgsConstructor
protected static class StackLevel {
public int index = 0;
public final Value value;
}
protected static class ArrayStackLevel extends StackLevel implements Iterator<Value> {
private final Iterator<Value> iterator = value.asArray().iterator();
@Override
public Value next() {
++index;
return iterator.next();
}
@Override
public boolean hasNext() {
return iterator.hasNext();
}
public ArrayStackLevel(ArrayValue value) {
super(value);
}
}
protected static class ObjectStackLevel extends StackLevel implements Iterator<Map.Entry<String, Value>> {
private final Iterator<Map.Entry<String, Value>> iterator = value.asObject().entrySet().iterator();
@Override
public Map.Entry<String, Value> next() {
++index;
return iterator.next();
}
@Override
public boolean hasNext() {
return iterator.hasNext();
}
public ObjectStackLevel(ObjectValue value) {
super(value);
}
}
protected Stack<StackLevel> stack;
protected ValueDumper() {
stack = new Stack<>();
}
@Override
public void dump(Value value, OutputStream stream) {
throw new NotImplementedException("Method not implemented");
}
@Override
public void dump(Value value, Writer writer) {
throw new NotImplementedException("Method not implemented");
}
@Override
public void dump(Value value, OutputStream stream, Charset encoding) {
dump(value, new OutputStreamWriter(stream, encoding));
}
protected abstract void beginObject();
protected abstract void endObject();
protected abstract void beginArray();
protected abstract void endArray();
protected abstract void objectKey(String key);
protected abstract void stringValue(String value);
protected abstract void integerValue(long value);
protected abstract void floatValue(double value);
protected abstract void booleanValue(boolean value);
protected abstract void nullValue();
}

View File

@@ -0,0 +1,116 @@
package org.oggio88.worth.serialization;
import lombok.RequiredArgsConstructor;
import org.oggio88.worth.exception.NotImplementedException;
import org.oggio88.worth.utils.WorthUtils;
import org.oggio88.worth.value.ArrayValue;
import org.oggio88.worth.value.BooleanValue;
import org.oggio88.worth.value.FloatValue;
import org.oggio88.worth.value.IntegerValue;
import org.oggio88.worth.value.ObjectValue;
import org.oggio88.worth.value.StringValue;
import org.oggio88.worth.xface.Parser;
import org.oggio88.worth.xface.Value;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.Charset;
import java.util.Stack;
public class ValueParser implements Parser {
@RequiredArgsConstructor
protected static class StackLevel {
public final Value value;
}
protected static class ArrayStackLevel extends StackLevel {
public ArrayStackLevel() {
super(new ArrayValue());
}
}
protected static class ObjectStackLevel extends StackLevel {
public String currentKey;
public ObjectStackLevel() {
super(ObjectValue.newInstance());
}
}
protected Stack<StackLevel> stack;
private void add2Last(Value value) {
StackLevel last = stack.lastElement();
ArrayStackLevel asl;
ObjectStackLevel osl;
if ((asl = WorthUtils.dynamicCast(last, ArrayStackLevel.class)) != null)
asl.value.add(value);
else if ((osl = WorthUtils.dynamicCast(last, ObjectStackLevel.class)) != null) {
osl.value.put(osl.currentKey, value);
osl.currentKey = null;
}
}
protected ValueParser() {
stack = new Stack<>();
stack.push(new ArrayStackLevel());
}
@Override
public Value parse(InputStream is) {
throw new NotImplementedException("Method not implemented");
}
@Override
public Value parse(Reader reader) {
throw new NotImplementedException("Method not implemented");
}
@Override
public Value parse(InputStream stream, Charset encoding) {
return parse(new InputStreamReader(stream, encoding));
}
protected void beginObject() {
stack.push(new ObjectStackLevel());
}
protected void endObject() {
add2Last(stack.pop().value);
}
protected void beginArray() {
stack.push(new ArrayStackLevel());
}
protected void endArray() {
add2Last(stack.pop().value);
}
protected void objectKey(String key) {
ObjectStackLevel osl = (ObjectStackLevel) stack.lastElement();
osl.currentKey = key;
}
protected void stringValue(String value) {
add2Last(new StringValue(value));
}
protected void integerValue(long value) {
add2Last(new IntegerValue(value));
}
protected void floatValue(double value) {
add2Last(new FloatValue(value));
}
protected void booleanValue(boolean value) {
add2Last(new BooleanValue(value));
}
protected void nullValue() {
add2Last(Value.Null);
}
}

View File

@@ -0,0 +1,183 @@
package org.oggio88.worth.serialization.json;
import lombok.SneakyThrows;
import org.oggio88.worth.serialization.ValueDumper;
import org.oggio88.worth.value.ArrayValue;
import org.oggio88.worth.value.ObjectValue;
import org.oggio88.worth.xface.Dumper;
import org.oggio88.worth.xface.Value;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Map;
import java.util.function.Consumer;
import static org.oggio88.worth.utils.WorthUtils.dynamicCast;
public class JSONDumper extends ValueDumper {
public static Dumper newInstance() {
return new JSONDumper();
}
protected Writer writer;
@Override
public void dump(Value value, OutputStream stream) {
dump(value, new OutputStreamWriter(stream));
}
@Override
@SneakyThrows
public void dump(Value value, Writer writer) {
this.writer = writer;
final Consumer<Value> handle_value = (v) -> {
switch (v.type()) {
case NULL:
nullValue();
break;
case BOOLEAN:
booleanValue(v.asBoolean());
break;
case INTEGER:
integerValue(v.asInteger());
break;
case DOUBLE:
floatValue(v.asFloat());
break;
case STRING:
stringValue(v.asString());
break;
case ARRAY:
stack.push(new ArrayStackLevel(dynamicCast(v, ArrayValue.class)));
beginArray();
break;
case OBJECT:
stack.push(new ObjectStackLevel(dynamicCast(v, ObjectValue.class)));
beginObject();
break;
}
};
handle_value.accept(value);
while (stack.size() > 0) {
StackLevel last = stack.lastElement();
ArrayStackLevel arrayStackLevel;
ObjectStackLevel objectStackLevel;
if ((arrayStackLevel = dynamicCast(last, ArrayStackLevel.class)) != null) {
if (arrayStackLevel.hasNext()) {
if (arrayStackLevel.index > 0) {
writer.write(",");
}
handle_value.accept(arrayStackLevel.next());
} else {
endArray();
stack.pop();
}
} else if ((objectStackLevel = dynamicCast(last, ObjectStackLevel.class)) != null) {
if (objectStackLevel.hasNext()) {
if (objectStackLevel.index > 0) {
writer.write(",");
}
Map.Entry<String, Value> entry = objectStackLevel.next();
objectKey(entry.getKey());
writer.write(":");
handle_value.accept(entry.getValue());
} else {
endObject();
stack.pop();
}
}
}
this.writer.flush();
this.writer = null;
}
@Override
@SneakyThrows
protected void beginObject() {
this.writer.write("{");
}
@Override
@SneakyThrows
protected void endObject() {
this.writer.write("}");
}
@Override
@SneakyThrows
protected void beginArray() {
this.writer.write("[");
}
@Override
@SneakyThrows
protected void endArray() {
this.writer.write("]");
}
@Override
@SneakyThrows
protected void objectKey(String key) {
this.writer.write("\"" + key + "\"");
}
@Override
@SneakyThrows
protected void stringValue(String value) {
StringBuilder sb = new StringBuilder();
for (char c : value.toCharArray()) {
switch (c) {
case '"':
sb.append("\\\"");
break;
case '\r':
sb.append("\\r");
break;
case '\n':
sb.append("\\n");
break;
case '\t':
sb.append("\\t");
break;
case '\\':
sb.append("\\\\");
break;
default: {
if (c < 128)
sb.append(c);
else {
sb.append("\\u").append(String.format("%04X", (int) c));
}
}
}
}
this.writer.write("\"" + sb.toString() + "\"");
}
@Override
@SneakyThrows
protected void integerValue(long value) {
this.writer.write(Long.toString(value));
}
@Override
@SneakyThrows
protected void floatValue(double value) {
this.writer.write(Double.toString(value));
}
@Override
@SneakyThrows
protected void booleanValue(boolean value) {
this.writer.write(Boolean.toString(value));
}
@Override
@SneakyThrows
protected void nullValue() {
this.writer.write("null");
}
}

View File

@@ -0,0 +1,227 @@
package org.oggio88.worth.serialization.json;
import lombok.SneakyThrows;
import org.oggio88.worth.buffer.CircularBuffer;
import org.oggio88.worth.exception.IOException;
import org.oggio88.worth.exception.NotImplementedException;
import org.oggio88.worth.exception.ParseException;
import org.oggio88.worth.serialization.ValueParser;
import org.oggio88.worth.utils.WorthUtils;
import org.oggio88.worth.xface.Parser;
import org.oggio88.worth.xface.Value;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.function.Function;
public class JSONParser extends ValueParser {
private int currentLine = 1, currentColumn = 1;
private static boolean isBlank(int c) {
return c == ' ' || c == '\t' || c == '\n' || c == '\r';
}
private static boolean isDecimal(int c) {
return c >= '0' && c <= '9' || c == '+' || c == '-' || c == '.' || c == 'e';
}
private static int parseHex(CircularBuffer circularBuffer) {
int result = 0;
while (true) {
int c = circularBuffer.next();
if (c >= '0' && c <= '9') {
result = result << 4;
result += (c - '0');
} else if (c >= 'a' && c <= 'f') {
result = result << 4;
result += 10 + (c - 'a');
} else if (c >= 'A' && c <= 'F') {
result = result << 4;
result += 10 + (c - 'A');
} else {
circularBuffer.prev();
break;
}
}
return result;
}
private final void parseNumber(CircularBuffer circularBuffer) {
StringBuilder sb = new StringBuilder();
while (true) {
int b = circularBuffer.next();
if (isDecimal(b)) {
sb.appendCodePoint(b);
} else {
circularBuffer.prev();
break;
}
}
String text = sb.toString();
if (text.indexOf('.') > 0) {
floatValue(Double.valueOf(text));
} else {
integerValue(Long.valueOf(text));
}
}
private final String readString(CircularBuffer circularBuffer) {
StringBuilder sb = new StringBuilder();
boolean escape = false;
while (true) {
int c = circularBuffer.next();
if (c < 0) {
circularBuffer.prev();
break;
} else if (escape) {
switch (c) {
case '"':
sb.append('\"');
break;
case 'r':
sb.append('\r');
break;
case 'n':
sb.append('\n');
break;
case 't':
sb.append('\t');
break;
case '\\':
sb.append('\\');
break;
case 'u':
int codePoint = parseHex(circularBuffer);
sb.appendCodePoint(codePoint);
break;
default:
throw error(ParseException::new, "Unrecognized escape sequence '\\%c'", c);
}
escape = false;
} else if (c == '\\') {
escape = true;
} else if (c == '\"') {
break;
} else {
sb.appendCodePoint(c);
}
}
return sb.toString();
}
private final void consumeExpected(CircularBuffer circularBuffer, String expected, String errorMessage) {
for (int i = 0; i < expected.length(); i++) {
int c = circularBuffer.next();
if (c < 0) {
throw error(IOException::new, "Unexpected end of stream");
}
if (c != expected.codePointAt(i)) throw error(ParseException::new, errorMessage);
}
}
private <T extends RuntimeException> T error(Function<String, T> constructor, String fmt, Object... args) {
return constructor.apply(
String.format("Error at line %d column %d: %s",
currentLine, currentColumn, String.format(fmt, args)));
}
public static Parser newInstance() {
return new JSONParser();
}
@Override
public Value parse(InputStream stream) {
return parse(new InputStreamReader(stream));
}
@Override
@SneakyThrows
public Value parse(Reader reader) {
final CircularBuffer circularBuffer = new CircularBuffer(reader, 8) {
@Override
public int next() {
int result = super.next();
if (result == '\n') {
++currentLine;
currentColumn = 1;
} else {
++currentColumn;
}
return result;
}
@Override
public int prev() {
int result = super.prev();
if (result == '\n') {
--currentLine;
} else {
--currentColumn;
}
return result;
}
};
try {
while (true) {
int c = circularBuffer.next();
if (c == -1) {
break;
} else if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
continue;
} else if (c == '{') {
beginObject();
} else if (c == '}') {
endObject();
} else if (c == '[') {
beginArray();
} else if (c == ']') {
endArray();
} else if (isDecimal(c)) {
circularBuffer.prev();
try {
parseNumber(circularBuffer);
} catch (NumberFormatException nfe) {
}
} else if (c == '\"') {
String text = readString(circularBuffer);
ObjectStackLevel osl;
if ((osl = WorthUtils.dynamicCast(stack.lastElement(), ObjectStackLevel.class)) != null && osl.currentKey == null) {
objectKey(text);
} else {
stringValue(text);
}
} else if (c == 't') {
consumeExpected(circularBuffer, "rue", "Unrecognized boolean value");
booleanValue(true);
} else if (c == 'f') {
consumeExpected(circularBuffer, "alse", "Unrecognized boolean value");
booleanValue(false);
} else if (c == 'n') {
consumeExpected(circularBuffer, "ull", "Unrecognized null value");
nullValue();
}
}
if (stack.size() > 1) {
char c;
if (stack.lastElement() instanceof ArrayStackLevel) {
c = ']';
} else if (stack.lastElement() instanceof ObjectStackLevel) {
c = '}';
} else {
throw new NotImplementedException("This should never happen");
}
throw error(ParseException::new, "Missing '%c' token", c);
}
return WorthUtils.dynamicCast(stack.lastElement(), ArrayStackLevel.class).value.get(0);
} catch (NumberFormatException e) {
throw error(ParseException::new, e.getMessage());
} finally {
stack.clear();
}
}
}

View File

@@ -0,0 +1,26 @@
package org.oggio88.worth.utils;
import lombok.SneakyThrows;
import java.util.concurrent.Callable;
public class WorthUtils {
@SneakyThrows
public static <T> T uncheckCall(final Callable<T> callable) {
return callable.call();
}
public static <T> T dynamicCast(final Object o, final Class<T> cls) {
if (cls.isInstance(o)) {
return (T) o;
} else {
return null;
}
}
public static boolean equalsNullSafe(Object o1, Object o2) {
if (o1 == null) return o2 == null;
else return o1.equals(o2);
}
}

View File

@@ -0,0 +1,69 @@
package org.oggio88.worth.value;
import lombok.EqualsAndHashCode;
import org.oggio88.worth.xface.Value;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@EqualsAndHashCode
public class ArrayValue implements Value, Iterable<Value> {
private final List<Value> value;
public ArrayValue() {
this.value = new ArrayList();
}
public ArrayValue(List<Value> value) {
this.value = value;
}
@Override
public Type type() {
return Type.ARRAY;
}
@Override
public void add(Value value) {
this.value.add(value);
}
@Override
public Value get(int index) {
return value.get(index);
}
@Override
public Value pop() {
Value last = tail();
value.remove(value.size() - 1);
return last;
}
@Override
public Value head() {
return value.get(0);
}
@Override
public Value tail() {
return value.get(value.size() - 1);
}
@Override
public List<Value> asArray() {
return value;
}
@Override
public Iterator<Value> iterator() {
return value.iterator();
}
public int size() {
return value.size();
}
}

View File

@@ -0,0 +1,24 @@
package org.oggio88.worth.value;
import lombok.EqualsAndHashCode;
import org.oggio88.worth.xface.Value;
@EqualsAndHashCode
public class BooleanValue implements Value {
private final boolean value;
public BooleanValue(boolean value) {
this.value = value;
}
@Override
public Type type() {
return Type.BOOLEAN;
}
@Override
public boolean asBoolean() {
return value;
}
}

View File

@@ -0,0 +1,24 @@
package org.oggio88.worth.value;
import lombok.EqualsAndHashCode;
import org.oggio88.worth.xface.Value;
@EqualsAndHashCode
public class FloatValue implements Value {
private final double value;
public FloatValue(double value) {
this.value = value;
}
@Override
public Type type() {
return Type.DOUBLE;
}
@Override
public double asFloat() {
return value;
}
}

View File

@@ -0,0 +1,24 @@
package org.oggio88.worth.value;
import lombok.EqualsAndHashCode;
import org.oggio88.worth.xface.Value;
@EqualsAndHashCode
public class IntegerValue implements Value {
private final long value;
public IntegerValue(long value) {
this.value = value;
}
@Override
public Type type() {
return Type.INTEGER;
}
@Override
public long asInteger() {
return value;
}
}

View File

@@ -0,0 +1,13 @@
package org.oggio88.worth.value;
import lombok.EqualsAndHashCode;
import org.oggio88.worth.xface.Value;
@EqualsAndHashCode
public class NullValue implements Value {
@Override
public Type type() {
return Type.NULL;
}
}

View File

@@ -0,0 +1,185 @@
package org.oggio88.worth.value;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.oggio88.worth.xface.Value;
import java.util.*;
import static org.oggio88.worth.utils.WorthUtils.equalsNullSafe;
public interface ObjectValue extends Value, Iterable<Map.Entry<String, Value>> {
boolean listBasedImplementation = Boolean.valueOf(
System.getProperty("org.oggio88.javason.value.ObjectValue.listBasedImplementation", "false"));
boolean preserveKeyOrder = Boolean.valueOf(
System.getProperty("org.oggio88.javason.value.MapObjectValue.preserveKeyOrder", "false"));
static ObjectValue newInstance() {
if (listBasedImplementation) {
return new MapObjectValue();
} else {
return new MapObjectValue();
}
}
@Override
default Type type() {
return Type.OBJECT;
}
}
final class ObjectEntry<K, V> implements Map.Entry<K, V> {
private final K key;
private V value;
public ObjectEntry(K key, V value) {
this.key = key;
this.value = value;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
@Override
public V setValue(V value) {
V old = this.value;
this.value = value;
return old;
}
}
@EqualsAndHashCode
class MapObjectValue implements ObjectValue {
private final Map<String, Value> value;
public MapObjectValue() {
this.value = ObjectValue.preserveKeyOrder ? new LinkedHashMap() : new HashMap();
}
public MapObjectValue(Map<String, Value> value) {
this.value = value;
}
@Override
public Map<String, Value> asObject() {
return value;
}
@Override
public Value get(String key) {
Value result = value.get(key);
if (result == null) {
result = Value.Null;
value.put(key, result);
}
return result;
}
@Override
public Value getOrDefault(String key, Value defaultValue) {
if (value.containsKey(key))
return value.get(key);
else
return defaultValue;
}
@Override
public Value getOrPut(String key, Value value2Put) {
if (value.containsKey(key))
return value.get(key);
else {
put(key, value2Put);
return value2Put;
}
}
@Override
public void put(String key, Value value2Put) {
this.value.put(key, value2Put);
}
@Override
public boolean has(String key) {
return value.containsKey(key);
}
@Override
public Iterator<Map.Entry<String, Value>> iterator() {
return value.entrySet().iterator();
}
}
@NoArgsConstructor
@EqualsAndHashCode
class ListObjectValue implements ObjectValue {
private final List<Map.Entry<String, Value>> value = new ArrayList();
public ListObjectValue(Map<String, Value> map) {
this.value.addAll(map.entrySet());
}
@Override
public Map<String, Value> asObject() {
Map<String, Value> result = preserveKeyOrder ? new LinkedHashMap() : new HashMap();
for (Map.Entry<String, Value> entry : value) {
result.put(entry.getKey(), entry.getValue());
}
return result;
}
@Override
public Value get(String key) {
for (Map.Entry<String, Value> entry : value) {
if(equalsNullSafe(entry.getKey(), key)) return entry.getValue();
}
return Value.Null;
}
@Override
public Value getOrDefault(String key, Value defaultValue) {
for (Map.Entry<String, Value> entry : value) {
if(equalsNullSafe(entry.getKey(), key)) return entry.getValue();
}
return defaultValue;
}
@Override
public Value getOrPut(String key, Value value2Put) {
for (Map.Entry<String, Value> entry : value) {
if(equalsNullSafe(entry.getKey(), key)) return entry.getValue();
}
put(key, value2Put);
return value2Put;
}
@Override
public void put(String key, Value value2Put) {
value.add(new ObjectEntry(key, value2Put));
}
@Override
public boolean has(String key) {
for (Map.Entry<String, Value> entry : value) {
if(equalsNullSafe(entry.getKey(), key)) return true;
}
return false;
}
@Override
public Iterator<Map.Entry<String, Value>> iterator() {
return value.iterator();
}
}

View File

@@ -0,0 +1,25 @@
package org.oggio88.worth.value;
import lombok.EqualsAndHashCode;
import org.oggio88.worth.xface.Value;
@EqualsAndHashCode
public class StringValue implements Value {
private final String value;
public StringValue(String value)
{
this.value = value;
}
@Override
public Type type() {
return Type.STRING;
}
@Override
public String asString() {
return value;
}
}

View File

@@ -0,0 +1,13 @@
package org.oggio88.worth.xface;
import java.io.OutputStream;
import java.io.Writer;
import java.nio.charset.Charset;
public interface Dumper {
void dump(Value value, OutputStream is);
void dump(Value value, Writer reader);
void dump(Value value, OutputStream stream, Charset encoding);
}

View File

@@ -0,0 +1,14 @@
package org.oggio88.worth.xface;
import java.io.InputStream;
import java.io.Reader;
import java.nio.charset.Charset;
public interface Parser {
Value parse(InputStream is);
Value parse(Reader reader);
Value parse(InputStream stream, Charset encoding);
}

View File

@@ -0,0 +1,82 @@
package org.oggio88.worth.xface;
import org.oggio88.worth.exception.TypeException;
import org.oggio88.worth.value.NullValue;
import java.util.List;
import java.util.Map;
public interface Value {
Value Null = new NullValue();
enum Type {
OBJECT, ARRAY, STRING, DOUBLE, INTEGER, BOOLEAN, NULL
}
Type type();
default boolean asBoolean() {
throw new TypeException("Not a boolean");
}
default long asInteger() {
throw new TypeException("Not an integer");
}
default double asFloat() {
throw new TypeException("Not a float");
}
default String asString() {
throw new TypeException("Not a String");
}
default List<Value> asArray() {
throw new TypeException("Not an array");
}
default Map<String, Value> asObject() {
throw new TypeException("Not an object");
}
default void add(Value value) {
throw new TypeException("Not an array");
}
default Value pop() {
throw new TypeException("Not an array");
}
default Value head() {
throw new TypeException("Not an array");
}
default Value tail() {
throw new TypeException("Not an array");
}
default Value get(int index) {
throw new TypeException("Not an array");
}
default void put(String key, Value value) {
throw new TypeException("Not an object");
}
default Value get(String key) {
throw new TypeException("Not an object");
}
default Value getOrDefault(String key, Value defaultValue) {
throw new TypeException("Not an object");
}
default Value getOrPut(String key, Value value2Put) {
throw new TypeException("Not an object");
}
default boolean has(String key) {
throw new TypeException("Not an object");
}
}

View File

@@ -0,0 +1,36 @@
package org.oggio88.worth.buffer;
import lombok.SneakyThrows;
import org.junit.Assert;
import org.junit.Test;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.util.Random;
public class CircularBufferTest {
@Test
@SneakyThrows
public void test() {
MessageDigest streamDigest = MessageDigest.getInstance("MD5"), outputDigest = MessageDigest.getInstance("MD5");
InputStream is = new DigestInputStream(getClass().getResourceAsStream("/logging.properties"), streamDigest);
CircularBuffer cb = new CircularBuffer(new InputStreamReader(is), 32);
Random rand = new Random();
while (true) {
int b = cb.next();
if (b < 0) break;
if (rand.nextInt() % 2 == 0) {
cb.prev();
} else {
char c = (char) b;
outputDigest.update((byte) b);
System.out.print(c);
}
}
System.out.println();
Assert.assertArrayEquals(streamDigest.digest(), outputDigest.digest());
}
}

View File

@@ -0,0 +1,134 @@
package org.oggio88.worth.serialization.json;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import lombok.SneakyThrows;
import org.junit.Assert;
import org.junit.Test;
import org.oggio88.worth.buffer.CircularBuffer;
import org.oggio88.worth.exception.NotImplementedException;
import org.oggio88.worth.utils.WorthUtils;
import org.oggio88.worth.value.ArrayValue;
import org.oggio88.worth.value.ObjectValue;
import org.oggio88.worth.xface.Parser;
import org.oggio88.worth.xface.Value;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.util.Map;
public class JSONTest {
private String[] testFiles = new String[]{"/test.json", "/wordpress.json"};
private InputStream getTestSource(String filename) {
return getClass().getResourceAsStream(filename);
}
private boolean compareValueAndJsonNode(Value value, JsonNode jsonNode) {
switch (value.type()) {
case NULL:
return jsonNode.getNodeType() == JsonNodeType.NULL;
case INTEGER:
if (jsonNode.getNodeType() == JsonNodeType.NUMBER) {
return value.asInteger() == jsonNode.asLong();
} else {
return false;
}
case DOUBLE:
if (jsonNode.getNodeType() == JsonNodeType.NUMBER) {
return value.asFloat() == jsonNode.asDouble();
} else {
return false;
}
case BOOLEAN:
if (jsonNode.getNodeType() == JsonNodeType.BOOLEAN) {
return value.asBoolean() == jsonNode.asBoolean();
} else {
return false;
}
case STRING:
if (jsonNode.getNodeType() == JsonNodeType.STRING) {
return value.asString().equals(jsonNode.asText());
} else {
return false;
}
case ARRAY:
ArrayValue array = WorthUtils.dynamicCast(value, ArrayValue.class);
if (jsonNode.getNodeType() == JsonNodeType.ARRAY && array.size() == jsonNode.size()) {
for (int i = 0; i < array.size(); i++) {
if (!compareValueAndJsonNode(array.get(i), jsonNode.get(i))) {
return false;
}
}
return true;
} else {
return false;
}
case OBJECT:
ObjectValue object = WorthUtils.dynamicCast(value, ObjectValue.class);
if (jsonNode.getNodeType() == JsonNodeType.OBJECT) {
for (Map.Entry<String, Value> entry : object) {
if (!jsonNode.has(entry.getKey())) {
return false;
} else if (!compareValueAndJsonNode(entry.getValue(), jsonNode.get(entry.getKey())))
return false;
}
return true;
} else {
return false;
}
default:
throw new NotImplementedException("This should never happen");
}
}
@Test
@SneakyThrows
public void consistencyTest() {
System.setProperty("org.oggio88.javason.value.ObjectValue.preserveKeyOrder", "true");
for (String testFile : testFiles) {
Parser parser = new JSONParser();
Value parsedValue = parser.parse(getTestSource(testFile));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
JSONDumper.newInstance().dump(parsedValue, baos);
String dumpedJSON = new String(baos.toByteArray());
byte[] barray = baos.toByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(barray);
parser = new JSONParser();
Value reParsedValue = parser.parse(bais);
Assert.assertEquals(parsedValue, reParsedValue);
baos = new ByteArrayOutputStream();
JSONDumper.newInstance().dump(reParsedValue, baos);
String reDumpedJSON = new String(baos.toByteArray());
Assert.assertEquals(dumpedJSON, reDumpedJSON);
}
}
@Test
@SneakyThrows
public void comparativeTest() {
for (String testFile : testFiles) {
ObjectMapper om = new ObjectMapper();
JsonNode jsonNode = om.readTree(getTestSource(testFile));
Value value = new JSONParser().parse(getTestSource(testFile));
Assert.assertTrue(compareValueAndJsonNode(value, jsonNode));
}
}
@Test
@SneakyThrows
public void hexTest() {
String hex = "1F608";
byte[] buffer = new String(hex).getBytes();
ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
Method method = JSONParser.class.getDeclaredMethod("parseHex", CircularBuffer.class);
method.setAccessible(true);
int result = (int) method.invoke(null, new CircularBuffer(new InputStreamReader(bais), 5));
Assert.assertEquals((int) Integer.valueOf(hex, 16), result);
}
}

View File

@@ -0,0 +1,111 @@
package org.oggio88.worth.serialization.json;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import org.junit.Ignore;
import org.junit.Test;
import org.oggio88.worth.xface.Value;
import org.tukaani.xz.XZInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.file.Paths;
class Chronometer {
public enum TimeUnit {
NANOSECOND(1e-9), MICROSECOND(1e-6), MILLISECOND(1e-3), SECOND(1);
private double factor;
TimeUnit(double factor) {
this.factor = factor;
}
}
private long start = System.nanoTime();
public void start() {
start = System.nanoTime();
}
public void reset() {
start();
}
public double stop(TimeUnit unit) {
return (System.nanoTime() - start) / (1e9 * unit.factor);
}
public double stop() {
return stop(TimeUnit.MILLISECOND);
}
}
public class PerformanceTest {
@SneakyThrows
private static byte[] extractTestData() {
ByteArrayOutputStream baous = new ByteArrayOutputStream();
byte[] buffer = new byte[1024 * 1024];
try (InputStream is = new XZInputStream(PerformanceTest.class.getResourceAsStream("/citylots.json.xz"))) {
while (true) {
int read = is.read(buffer);
if (read < 0) break;
baous.write(buffer, 0, read);
}
}
return baous.toByteArray();
}
@Test
@SneakyThrows
public void loopTest() {
double jacksonTime, worthTime;
final int loops = 100;
Chronometer chr = new Chronometer();
{
chr.reset();
for (int i = 0; i < loops; i++) {
ObjectMapper om = new ObjectMapper();
JsonNode jsonNode = om.readTree(getClass().getResourceAsStream("/wordpress.json"));
}
jacksonTime = chr.stop(Chronometer.TimeUnit.MILLISECOND);
System.out.printf("Jackson time: %8s msec\n", String.format("%.3f", jacksonTime));
}
{
chr.reset();
for (int i = 0; i < loops; i++) {
Value value = new JSONParser().parse(getClass().getResourceAsStream("/wordpress.json"));
}
worthTime = chr.stop(Chronometer.TimeUnit.MILLISECOND);
System.out.printf("Worth time: %8s msec\n", String.format("%.3f", worthTime));
}
}
@Test
@Ignore
@SneakyThrows
public void hugeJSONTest() {
byte[] testData = extractTestData();
double jacksonTime, worthTime;
Chronometer chr = new Chronometer();
{
chr.reset();
ObjectMapper om = new ObjectMapper();
JsonNode jsonNode = om.readTree(new ByteArrayInputStream(testData));
jacksonTime = chr.stop(Chronometer.TimeUnit.SECOND);
System.out.printf("Jackson time: %8s sec\n", String.format("%.3f", jacksonTime));
}
{
chr.reset();
Value value = new JSONParser().parse(new ByteArrayInputStream(testData));
worthTime = chr.stop(Chronometer.TimeUnit.SECOND);
System.out.printf("Worth time: %8s sec\n", String.format("%.3f", worthTime));
}
}
}

View File

@@ -0,0 +1,80 @@
/** Taken from "The Definitive ANTLR 4 Reference" by Terence Parr */
// Derived from http://json.org
grammar JSON;
json
: value
;
obj
: '{' pair (',' pair)* '}'
| '{' '}'
;
pair
: STRING ':' value
;
array
: '[' value (',' value)* ']'
| '[' ']'
;
value
: STRING
| NUMBER
| obj
| array
| 'true'
| 'false'
| 'null'
;
STRING
: '"' (ESC | SAFECODEPOINT)* '"'
;
fragment ESC
: '\\' (["\\/bfnrt] | UNICODE)
;
fragment UNICODE
: 'u' HEX HEX HEX HEX
;
fragment HEX
: [0-9a-fA-F]
;
fragment SAFECODEPOINT
: ~ ["\\\u0000-\u001F]
;
NUMBER
: '-'? INT ('.' [0-9] +)? EXP?
;
fragment INT
: '0' | [1-9] [0-9]*
;
// no leading zeros
fragment EXP
: [Ee] [+\-]? INT
;
// \- since - means "range" inside [...]
WS
: [ \t\n\r] + -> skip
;

Binary file not shown.

View File

@@ -0,0 +1,36 @@
handlers=java.util.logging.ConsoleHandler
config=
sun.net.www.protocol.http.HttpURLConnection.handlers=java.util.logging.ConsoleHandler
#sun.net.www.protocol.http.HttpURLConnection.level=ALL
java.util.logging.FileHandler.level = WARNING
java.util.logging.FileHandler.filter =
java.util.logging.FileHandler.formatter =
java.util.logging.FileHandler.encoding =
java.util.logging.FileHandler.limit =
java.util.logging.FileHandler.count =
java.util.logging.FileHandler.append = false
java.util.logging.FileHandler.pattern = log.%u.%g.txt
#java.util.logging.ConsoleHandler.level = ALL
java.util.logging.ConsoleHandler.filter =
java.util.logging.ConsoleHandler.formatter =
java.util.logging.ConsoleHandler.encoding =
java.util.logging.StreamHandler.level = WARNING
java.util.logging.StreamHandler.filter =
java.util.logging.StreamHandler.formatter =
java.util.logging.StreamHandler.encoding =
java.util.logging.SocketHandler.level = WARNING
java.util.logging.SocketHandler.filter =
java.util.logging.SocketHandler.formatter =
java.util.logging.SocketHandler.encoding =
java.util.logging.SocketHandler.host =
java.util.logging.SocketHandler.port =
java.util.logging.MemoryHandler.level = WARNING
java.util.logging.MemoryHandler.filter =
java.util.logging.MemoryHandler.size =
java.util.logging.MemoryHandler.push =
java.util.logging.MemoryHandler.target =

View File

@@ -0,0 +1,31 @@
{
"widget": {
"debug": "on",
"window": {
"parent" : null,
"title": "Sample Konfabulator Widget",
"name": "main_window",
"width": 500,
"height": 500
},
"image": {
"src": "Images/Sun.png",
"name": "sun1",
"hOffset": 250,
"vOffset": 250,
"alignment": "center",
"tags" : ["Ireland", "Amazon", "development"],
"monochromatic" : false
},
"text": {
"data": "Click Here",
"size": 36,
"style": "bold",
"name": "text1",
"hOffset": 250,
"vOffset": 100,
"alignment": "center",
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
}
}
}

File diff suppressed because one or more lines are too long