initial commit
This commit is contained in:
33
build.sbt
Normal file
33
build.sbt
Normal 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
9
project/plugins.sbt
Normal 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")
|
38
src/main/java/org/oggio88/worth/buffer/CircularBuffer.java
Normal file
38
src/main/java/org/oggio88/worth/buffer/CircularBuffer.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,7 @@
|
|||||||
|
package org.oggio88.worth.exception;
|
||||||
|
|
||||||
|
public class IOException extends WorthException {
|
||||||
|
public IOException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,7 @@
|
|||||||
|
package org.oggio88.worth.exception;
|
||||||
|
|
||||||
|
public class NotImplementedException extends WorthException {
|
||||||
|
public NotImplementedException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,7 @@
|
|||||||
|
package org.oggio88.worth.exception;
|
||||||
|
|
||||||
|
public class ParseException extends WorthException {
|
||||||
|
public ParseException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,7 @@
|
|||||||
|
package org.oggio88.worth.exception;
|
||||||
|
|
||||||
|
public class TypeException extends WorthException {
|
||||||
|
public TypeException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,7 @@
|
|||||||
|
package org.oggio88.worth.exception;
|
||||||
|
|
||||||
|
public class WorthException extends RuntimeException {
|
||||||
|
public WorthException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
105
src/main/java/org/oggio88/worth/serialization/ValueDumper.java
Normal file
105
src/main/java/org/oggio88/worth/serialization/ValueDumper.java
Normal 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();
|
||||||
|
}
|
116
src/main/java/org/oggio88/worth/serialization/ValueParser.java
Normal file
116
src/main/java/org/oggio88/worth/serialization/ValueParser.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
src/main/java/org/oggio88/worth/utils/WorthUtils.java
Normal file
26
src/main/java/org/oggio88/worth/utils/WorthUtils.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
69
src/main/java/org/oggio88/worth/value/ArrayValue.java
Normal file
69
src/main/java/org/oggio88/worth/value/ArrayValue.java
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
24
src/main/java/org/oggio88/worth/value/BooleanValue.java
Normal file
24
src/main/java/org/oggio88/worth/value/BooleanValue.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
24
src/main/java/org/oggio88/worth/value/FloatValue.java
Normal file
24
src/main/java/org/oggio88/worth/value/FloatValue.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
24
src/main/java/org/oggio88/worth/value/IntegerValue.java
Normal file
24
src/main/java/org/oggio88/worth/value/IntegerValue.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
13
src/main/java/org/oggio88/worth/value/NullValue.java
Normal file
13
src/main/java/org/oggio88/worth/value/NullValue.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
185
src/main/java/org/oggio88/worth/value/ObjectValue.java
Normal file
185
src/main/java/org/oggio88/worth/value/ObjectValue.java
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
25
src/main/java/org/oggio88/worth/value/StringValue.java
Normal file
25
src/main/java/org/oggio88/worth/value/StringValue.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
13
src/main/java/org/oggio88/worth/xface/Dumper.java
Normal file
13
src/main/java/org/oggio88/worth/xface/Dumper.java
Normal 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);
|
||||||
|
}
|
14
src/main/java/org/oggio88/worth/xface/Parser.java
Normal file
14
src/main/java/org/oggio88/worth/xface/Parser.java
Normal 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);
|
||||||
|
}
|
82
src/main/java/org/oggio88/worth/xface/Value.java
Normal file
82
src/main/java/org/oggio88/worth/xface/Value.java
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
134
src/test/java/org/oggio88/worth/serialization/json/JSONTest.java
Normal file
134
src/test/java/org/oggio88/worth/serialization/json/JSONTest.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
80
src/test/resources/JSON.g4
Normal file
80
src/test/resources/JSON.g4
Normal 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
|
||||||
|
;
|
BIN
src/test/resources/citylots.json.xz
Normal file
BIN
src/test/resources/citylots.json.xz
Normal file
Binary file not shown.
36
src/test/resources/logging.properties
Normal file
36
src/test/resources/logging.properties
Normal 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 =
|
31
src/test/resources/test.json
Normal file
31
src/test/resources/test.json
Normal 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;"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
318
src/test/resources/wordpress.json
Normal file
318
src/test/resources/wordpress.json
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user