Compare commits
10 Commits
d15172d41a
...
c4ccec0fc3
Author | SHA1 | Date | |
---|---|---|---|
c4ccec0fc3
|
|||
9cb37790c2
|
|||
b7d00d9d9c
|
|||
e75a0e551b
|
|||
1747d946ec
|
|||
67161e92aa
|
|||
809a6c9408
|
|||
969b0075f6
|
|||
e3c1dfca5e
|
|||
d6ba855001
|
4
Jenkinsfile
vendored
4
Jenkinsfile
vendored
@@ -6,10 +6,10 @@ pipeline {
|
||||
stages {
|
||||
stage("Build") {
|
||||
steps {
|
||||
sh "./gradlew clean build"
|
||||
sh "./gradlew clean assemble build"
|
||||
junit testResults: "build/test-results/test/*.xml"
|
||||
javadoc javadocDir: "build/docs/javadoc", keepAll: true
|
||||
archiveArtifacts artifacts: 'build/libs/*.jar,benchmark/build/libs/*.jar,wson-cli/build/distributions/wson-cli-envelope-*.jar',
|
||||
archiveArtifacts artifacts: 'build/libs/*.jar,benchmark/build/libs/*.jar,wson-cli/build/libs/wson-cli-envelope-*.jar,wson-cli/build/libs/wson-cli',
|
||||
allowEmptyArchive: true,
|
||||
fingerprint: true,
|
||||
onlyIfSuccessful: true
|
||||
|
@@ -4,15 +4,14 @@ plugins {
|
||||
|
||||
dependencies {
|
||||
implementation rootProject
|
||||
runtimeOnly catalog.antlr.runtime
|
||||
|
||||
testImplementation group: "net.woggioni", name: "jwo"
|
||||
testImplementation catalog.jwo
|
||||
testImplementation project(':test-utils')
|
||||
testImplementation group: "com.fasterxml.jackson.core", name: "jackson-databind"
|
||||
testImplementation group: "org.tukaani", name: "xz"
|
||||
testImplementation catalog.jackson.databind
|
||||
testImplementation catalog.xz
|
||||
|
||||
antlr platform(group: 'com.lys', name: 'lys-dependencies', version: getProperty('lys.version'))
|
||||
antlr group: "org.antlr", name: "antlr4"
|
||||
antlr group: "org.antlr", name: "antlr4-runtime"
|
||||
antlr catalog.antlr
|
||||
}
|
||||
|
||||
generateGrammarSource {
|
||||
|
@@ -1,23 +1,36 @@
|
||||
plugins {
|
||||
id 'application'
|
||||
alias(catalog.plugins.envelope)
|
||||
id 'me.champeau.jmh' version '0.7.1'
|
||||
}
|
||||
|
||||
application {
|
||||
mainClassName = 'net.woggioni.wson.benchmark.Main'
|
||||
applicationDefaultJvmArgs = ['-Xmx8G']
|
||||
envelopeJar {
|
||||
mainClass = 'net.woggioni.wson.benchmark.Main'
|
||||
}
|
||||
|
||||
run {
|
||||
commandLine
|
||||
envelopeRun {
|
||||
jvmArgs = ['-Xmx8G']
|
||||
args = ['jacksonBenchmark', 'worthJsonBenchmark', 'worthJbonBenchmark', 'antlrBenchmark']
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation rootProject
|
||||
implementation project(':antlr')
|
||||
implementation group: "net.woggioni", name: "jwo"
|
||||
implementation group: 'org.tukaani', name: 'xz'
|
||||
implementation group: 'com.beust', name: 'jcommander'
|
||||
implementation group: "com.fasterxml.jackson.core", name: "jackson-databind"
|
||||
|
||||
implementation catalog.jwo
|
||||
implementation catalog.xz
|
||||
implementation catalog.jcommander
|
||||
implementation catalog.jackson.databind
|
||||
|
||||
runtimeOnly project(':test-utils')
|
||||
|
||||
jmhAnnotationProcessor catalog.lombok
|
||||
}
|
||||
|
||||
jmh {
|
||||
threads = 1
|
||||
iterations = 2
|
||||
fork = 1
|
||||
warmupIterations = 1
|
||||
warmupForks = 0
|
||||
resultFormat = 'JSON'
|
||||
}
|
@@ -0,0 +1,135 @@
|
||||
package net.woggioni.wson.benchmark;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.SneakyThrows;
|
||||
import net.woggioni.wson.antlr.JSONLexer;
|
||||
import net.woggioni.wson.antlr.JSONListenerImpl;
|
||||
import net.woggioni.wson.serialization.binary.JBONParser;
|
||||
import net.woggioni.wson.serialization.json.JSONParser;
|
||||
import net.woggioni.wson.value.ObjectValue;
|
||||
import net.woggioni.wson.xface.Value;
|
||||
import org.antlr.v4.runtime.CharStream;
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.antlr.v4.runtime.tree.ParseTreeWalker;
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.tukaani.xz.XZInputStream;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class WsonParserBenchmark {
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
public static class ExecutionPlan {
|
||||
|
||||
@SneakyThrows
|
||||
public InputStream hugeTestData() {
|
||||
return new XZInputStream(Main.class.getResourceAsStream("/citylots.json.xz"));
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public InputStream hugeBinaryTestData() {
|
||||
return new XZInputStream(new BufferedInputStream(WsonParserBenchmark.class.getResourceAsStream("/citylots.jbon.xz")));
|
||||
}
|
||||
|
||||
public InputStream smallTestData() {
|
||||
return new BufferedInputStream(WsonParserBenchmark.class.getResourceAsStream("/wordpress.json"));
|
||||
}
|
||||
|
||||
public InputStream smallBinaryTestData() {
|
||||
return new BufferedInputStream(WsonParserBenchmark.class.getResourceAsStream("/wordpress.jbon"));
|
||||
}
|
||||
}
|
||||
|
||||
private static Value.Configuration buildConfiguration() {
|
||||
return Value.Configuration.builder()
|
||||
.objectValueImplementation(ObjectValue.Implementation.ArrayList)
|
||||
.build();
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Benchmark
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.MICROSECONDS)
|
||||
public void antlr(ExecutionPlan plan) {
|
||||
try (InputStream is = plan.smallTestData()) {
|
||||
CharStream inputStream = CharStreams.fromReader(new InputStreamReader(is));
|
||||
JSONLexer lexer = new JSONLexer(inputStream);
|
||||
CommonTokenStream commonTokenStream = new CommonTokenStream(lexer);
|
||||
net.woggioni.wson.antlr.JSONParser parser = new net.woggioni.wson.antlr.JSONParser(commonTokenStream);
|
||||
JSONListenerImpl listener = new JSONListenerImpl();
|
||||
ParseTreeWalker walker = new ParseTreeWalker();
|
||||
walker.walk(listener, parser.json());
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Benchmark
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.MICROSECONDS)
|
||||
public void worthJson(ExecutionPlan plan) {
|
||||
try (InputStream is = plan.smallTestData()) {
|
||||
new JSONParser(buildConfiguration()).parse(is);
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Benchmark
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.MICROSECONDS)
|
||||
public void worthJbon(ExecutionPlan plan) {
|
||||
try (InputStream is = plan.smallBinaryTestData()) {
|
||||
new JBONParser(buildConfiguration()).parse(is);
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Benchmark
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.SECONDS)
|
||||
public void worthJbonHuge(ExecutionPlan plan) {
|
||||
try (InputStream is = plan.hugeBinaryTestData()) {
|
||||
new JBONParser(buildConfiguration()).parse(is);
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Benchmark
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.SECONDS)
|
||||
public void worthHuge(ExecutionPlan plan) {
|
||||
try (InputStream is = plan.hugeTestData()) {
|
||||
new JSONParser(buildConfiguration()).parse(is);
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Benchmark
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.MICROSECONDS)
|
||||
public void jackson(ExecutionPlan plan) {
|
||||
try (InputStream is = plan.smallTestData()) {
|
||||
ObjectMapper om = new ObjectMapper();
|
||||
om.readTree(new InputStreamReader(is));
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Benchmark
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.SECONDS)
|
||||
public void jacksonHuge(ExecutionPlan plan) {
|
||||
try (InputStream is = plan.hugeTestData()) {
|
||||
ObjectMapper om = new ObjectMapper();
|
||||
om.readTree(new InputStreamReader(is));
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,11 +3,11 @@ package net.woggioni.wson.benchmark;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.SneakyThrows;
|
||||
import net.woggioni.jwo.Chronometer;
|
||||
import net.woggioni.wson.antlr.JSONLexer;
|
||||
import net.woggioni.wson.antlr.JSONListenerImpl;
|
||||
import net.woggioni.wson.serialization.binary.JBONParser;
|
||||
import net.woggioni.wson.serialization.json.JSONParser;
|
||||
import net.woggioni.jwo.Chronometer;
|
||||
import net.woggioni.wson.value.ObjectValue;
|
||||
import net.woggioni.wson.xface.Value;
|
||||
import org.antlr.v4.runtime.CharStream;
|
||||
|
55
build.gradle
55
build.gradle
@@ -1,51 +1,73 @@
|
||||
plugins {
|
||||
id 'maven-publish'
|
||||
id 'net.woggioni.gradle.lombok' apply false
|
||||
id 'net.woggioni.gradle.multi-release-jar'
|
||||
alias catalog.plugins.envelope apply false
|
||||
alias catalog.plugins.lombok apply false
|
||||
}
|
||||
|
||||
|
||||
allprojects {
|
||||
apply plugin: 'java-library'
|
||||
apply plugin: 'net.woggioni.gradle.lombok'
|
||||
apply plugin: catalog.plugins.lombok.get().pluginId
|
||||
|
||||
group = "net.woggioni"
|
||||
version = getProperty('wson.version')
|
||||
|
||||
java {
|
||||
modularity.inferModulePath = true
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(17)
|
||||
vendor = JvmVendorSpec.GRAAL_VM
|
||||
}
|
||||
}
|
||||
|
||||
lombok {
|
||||
version = getProperty('lombok.version')
|
||||
version = catalog.versions.lombok.get()
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
url = "https://woggioni.net/mvn/"
|
||||
content {
|
||||
includeGroup 'net.woggioni'
|
||||
includeGroup 'com.beust'
|
||||
includeGroup 'com.lys'
|
||||
includeGroup 'net.woggioni'
|
||||
}
|
||||
}
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation platform(group: 'com.lys', name: 'lys-dependencies', version: getProperty('lys.version'))
|
||||
implementation group: "org.tukaani", name: "xz"
|
||||
implementation catalog.xz
|
||||
|
||||
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api'
|
||||
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-params'
|
||||
testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine'
|
||||
testImplementation catalog.junit.jupiter.api
|
||||
testImplementation catalog.junit.jupiter.params
|
||||
testRuntimeOnly catalog.junit.jupiter.engine
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
tasks.named(JavaPlugin.COMPILE_JAVA_TASK_NAME, JavaCompile) {
|
||||
options.release = 17
|
||||
}
|
||||
|
||||
pluginManager.withPlugin(catalog.plugins.kotlin.jvm.get().pluginId) {
|
||||
compileKotlin {
|
||||
kotlinOptions.with {
|
||||
jvmTarget = '17'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
implementation group: "net.woggioni", name: "jwo"
|
||||
implementation group: "org.slf4j", name: "slf4j-api"
|
||||
implementation catalog.jwo
|
||||
implementation catalog.slf4j.api
|
||||
|
||||
testImplementation project('test-utils')
|
||||
testImplementation group: "com.fasterxml.jackson.core", name: "jackson-databind"
|
||||
testImplementation catalog.jackson.databind
|
||||
}
|
||||
|
||||
java {
|
||||
@@ -54,16 +76,9 @@ java {
|
||||
}
|
||||
|
||||
compileJava {
|
||||
options.release = 8
|
||||
options.compilerArgs << '-parameters'
|
||||
}
|
||||
|
||||
wrapper {
|
||||
distributionType = Wrapper.DistributionType.BIN
|
||||
gradleVersion = getProperty("gradle.version")
|
||||
}
|
||||
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
maven {
|
||||
|
0
config/checkstyle/checkstyle.xml
Normal file
0
config/checkstyle/checkstyle.xml
Normal file
@@ -1,14 +1,4 @@
|
||||
gradle.version=7.4.2
|
||||
wson.version = 1.0-SNAPSHOT
|
||||
wson.version = 2023.11.01
|
||||
|
||||
net.woggioni.gradle.lombok.version = 0.1
|
||||
net.woggioni.gradle.envelope.version = 1.0-SNAPSHOT
|
||||
net.woggioni.gradle.multi-release-jar = 0.1
|
||||
|
||||
lys.version = 0.1-SNAPSHOT
|
||||
|
||||
lombok.version = 1.18.22
|
||||
kotlin.version = 1.7.0
|
||||
|
||||
jpms.module.name = net.woggioni.wson
|
||||
lys.version = 2023.10.31
|
||||
|
||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
|
||||
networkTimeout=10000
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
25
gradlew
vendored
25
gradlew
vendored
@@ -55,7 +55,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@@ -80,13 +80,10 @@ do
|
||||
esac
|
||||
done
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@@ -143,12 +140,16 @@ fi
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@@ -193,6 +194,10 @@ if "$cygwin" || "$msys" ; then
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
@@ -205,6 +210,12 @@ set -- \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
|
15
gradlew.bat
vendored
15
gradlew.bat
vendored
@@ -14,7 +14,7 @@
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@@ -25,7 +25,8 @@
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
@@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
@@ -7,22 +7,41 @@ pluginManagement {
|
||||
includeGroup 'net.woggioni.gradle.lombok'
|
||||
includeGroup 'net.woggioni.gradle.envelope'
|
||||
includeGroup 'net.woggioni.gradle.multi-release-jar'
|
||||
includeGroup 'net.woggioni.gradle.graalvm.native-image'
|
||||
includeGroup 'net.woggioni.gradle.graalvm.jlink'
|
||||
includeGroup 'net.woggioni.gradle.sambal'
|
||||
}
|
||||
}
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'net.woggioni.gradle.envelope' version getProperty('net.woggioni.gradle.envelope.version')
|
||||
id 'net.woggioni.gradle.lombok' version getProperty('net.woggioni.gradle.lombok.version')
|
||||
id 'net.woggioni.gradle.multi-release-jar' version getProperty('net.woggioni.gradle.multi-release-jar')
|
||||
id 'org.jetbrains.kotlin.jvm' version getProperty('kotlin.version')
|
||||
dependencyResolutionManagement {
|
||||
repositories {
|
||||
maven {
|
||||
url = 'https://woggioni.net/mvn/'
|
||||
content {
|
||||
includeGroup 'com.lys'
|
||||
}
|
||||
}
|
||||
}
|
||||
versionCatalogs {
|
||||
catalog {
|
||||
from group: 'com.lys', name: 'lys-catalog', version: getProperty('lys.version')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = 'wson'
|
||||
|
||||
def includeDirs = ['antlr', 'benchmark', 'test-utils', 'wson-cli']
|
||||
def includeDirs = [
|
||||
'antlr',
|
||||
'benchmark',
|
||||
'test-utils',
|
||||
'wson-cli',
|
||||
'wson-w3c-json',
|
||||
'wcfg',
|
||||
]
|
||||
|
||||
includeDirs.each {
|
||||
include(it)
|
||||
|
@@ -1,4 +1,5 @@
|
||||
module net.woggioni.wson {
|
||||
requires static lombok;
|
||||
requires net.woggioni.jwo;
|
||||
|
||||
exports net.woggioni.wson.xface;
|
||||
@@ -7,4 +8,5 @@ module net.woggioni.wson {
|
||||
exports net.woggioni.wson.serialization;
|
||||
exports net.woggioni.wson.serialization.json;
|
||||
exports net.woggioni.wson.serialization.binary;
|
||||
exports net.woggioni.wson.traversal;
|
||||
}
|
@@ -4,7 +4,10 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.NonNull;
|
||||
import net.woggioni.wson.xface.Value;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
@EqualsAndHashCode
|
||||
public class ArrayValue implements Value, Iterable<Value> {
|
||||
|
@@ -2,10 +2,18 @@ package net.woggioni.wson.value;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import static net.woggioni.jwo.JWO.newThrowable;
|
||||
import net.woggioni.wson.xface.Value;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import static net.woggioni.jwo.JWO.newThrowable;
|
||||
|
||||
public interface ObjectValue extends Value, Iterable<Map.Entry<String, Value>> {
|
||||
|
||||
|
@@ -4,8 +4,6 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.NonNull;
|
||||
import net.woggioni.wson.xface.Value;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@EqualsAndHashCode
|
||||
public class StringValue implements Value {
|
||||
|
||||
|
@@ -9,7 +9,12 @@ import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
|
229
test-utils/src/main/resources/wordpress.jbon
Normal file
229
test-utils/src/main/resources/wordpress.jbon
Normal file
@@ -0,0 +1,229 @@
|
||||
<EFBFBD>v_linksh
|
||||
about<EFBFBD>_href?https://www.sitepoint.com/wp-json/wp/v2/types/postauthor<6F>`embeddablehref@https://www.sitepoint.com/wp-json/wp/v2/users/72596collection<6F>_href:https://www.sitepoint.com/wp-json/wp/v2/postscuries<65>ahref$https://api.w.org/{rel}namewptemplatedreplies<65>`embeddablehrefIhttps://www.sitepoint.com/wp-json/wp/v2/comments?post=168697self<6C>_hrefAhttps://www.sitepoint.com/wp-json/wp/v2/posts/168697version-history<72>_hrefKhttps://www.sitepoint.com/wp-json/wp/v2/posts/168697/revisionswp:attachment<6E>_hrefHhttps://www.sitepoint.com/wp-json/wp/v2/media?parent=168697 wp:featuredmedia<69>`embeddablehrefAhttps://www.sitepoint.com/wp-json/wp/v2/media/168703wp:term<72>aembeddablehrefKhttps://www.sitepoint.com/wp-json/wp/v2/categories?post=168697taxonomycategoryaembeddablehrefEhttps://www.sitepoint.com/wp-json/wp/v2/tags?post=168697taxonomypost_tagauthor<01><>categories<65><01>[<01>[comment_statusopencontent`protectedrendered]<5D><><p class="wp-special"><em>This article was created in partnership with <a href="https://bawmedia.com/" rel="nofollow">BAWMedia</a>. Thank you for supporting the partners who make SitePoint possible.</em></p>
|
||||
<p>A well-designed website can serve as a powerful marketing tool. These days, creating one for a small business is not distressing or expensive at all. However, it was just a few short years ago.</p>
|
||||
<p>Today, you can take advantage of the features provided by the best WordPress themes. There are special themes for small business-oriented websites. It's not difficult to find a website-building theme that matches a specific business. This can be a startup, a service provider, or some other venture.</p>
|
||||
<p>You undoubtedly want nothing but the best business theme, right? Check out those described below. Each possesses functional designs loaded with amazing features. They will help you create a thoroughly engaging website to promote a business.</p>
|
||||
<h2>1. <a href="http://themes.muffingroup.com/be/splash/?utm_source=sitepoint.com&utm_medium=content&utm_campaign=wpstartups18" rel="nofollow">Be Theme</a></h2>
|
||||
<p><a href="http://themes.muffingroup.com/be/splash/?utm_source=sitepoint.com&utm_medium=content&utm_campaign=wpstartups18" rel="nofollow"><img src="https://www.sitepoint.com/wp-content/uploads/2018/09/15361215231.png" alt="" width="1000" height="481" class="aligncenter size-full wp-image-168702" /></a></p>
|
||||
<p>We’ll start with Be Theme, a responsive, multipurpose WordPress theme that takes every small business need into account with its more than 370 pre-built websites. There's a multiplicity of small business WordPress themes in this pre-built website collection ? each one embedded with the functionality you need to establish an effective online presence, and fully customizable to meet your business and marketing needs.</p>
|
||||
<p>The range of business niches covered is impressive, With more pre-built websites being added every month it's destined to become even more so. Web designers like Be Theme because it allows them to create a website for most small business types in as little as four hours.</p>
|
||||
<p>Clients appreciate the rapid turnaround they receive and the ease in which changes or additions they have in mind can be accommodated.</p>
|
||||
<p>Be Theme, one of the best WordPress themes for small business websites is a ThemeForest top 5 best seller whose core features include easy to work with page-building tools, a multiplicity of design features and options, and great support.</p>
|
||||
<h2>2. <a href="http://bit.ly/2wxuUpA" rel="nofollow">Astra</a></h2>
|
||||
<p><a href="http://bit.ly/2wxuUpA" rel="nofollow"><img src="https://www.sitepoint.com/wp-content/uploads/2018/09/15361215352.jpg" alt="" width="1000" height="491" class="aligncenter size-full wp-image-168703" /></a></p>
|
||||
<p>Astra is fast, fully customizable, and one of the best WordPress themes for business websites as well as for blogs and personal portfolios. Built with SEO in mind, Astra is responsive and WooCommerce ready ? mandatory features in today's online business environment. Its capabilities are easily extendible with premium addons and Astra can be used with most of the popular page builders. This free WP-based theme is definitely worth considering.</p>
|
||||
<h2 id="">3. <a href="http://bit.ly/2oiQkTS" rel="nofollow">The100</a></h2>
|
||||
<p><a href="http://bit.ly/2oiQkTS" rel="nofollow"><img src="https://www.sitepoint.com/wp-content/uploads/2018/09/15361215473.jpg" alt="" width="1000" height="491" class="aligncenter size-full wp-image-168704" /></a></p>
|
||||
<p>A theme selected for WordPress for small businesses can be free or it can be a premium theme requiring an expenditure on your behalf. There are several excellent free themes on the market, and one of them is The100. While it is advertised as having premium-like features, bear in mind that free themes like this one generally can't compete with premium themes. Nevertheless, The100 is an easy-to-use WP theme that features a multiplicity of layouts and plenty of customization options.</p>
|
||||
<h2 id="">4. <a href="https://undsgn.com/uncode/?utm_source=sitepoint.com&utm_medium=content&utm_campaign=wpstartups18" rel="nofollow">Uncode ? Creative Multiuse WordPress Theme</a></h2>
|
||||
<p><a href="https://undsgn.com/uncode/?utm_source=sitepoint.com&utm_medium=content&utm_campaign=wpstartups18" rel="nofollow"><img src="https://www.sitepoint.com/wp-content/uploads/2018/09/15361215604.jpg" alt="" width="1000" height="415" class="aligncenter size-full wp-image-168705" /></a></p>
|
||||
<p>Uncode has proven to be one of the best WordPress themes for business websites. It's a multipurpose theme featuring 30+ homepage concepts designed to get designers and their clients off to a fast start on any small business website. Features include an enhanced version of the popular Visual Composer page builder, and an Adaptive Images System that enables mobile users to see what you want and expect them to see.</p>
|
||||
<h2 id="">5. <a href="http://houzez.co/?utm_source=sitepoint.com&utm_medium=content&utm_campaign=wpstartups18" rel="nofollow">Houzez ? Highly Customizable Real Estate WordPress Theme</a></h2>
|
||||
<p><a href="http://houzez.co/?utm_source=sitepoint.com&utm_medium=content&utm_campaign=wpstartups18" rel="nofollow"><img src="https://www.sitepoint.com/wp-content/uploads/2018/09/15361215715.jpg" alt="" width="1000" height="415" class="aligncenter size-full wp-image-168706" /></a></p>
|
||||
<p>Some WordPress themes are created with a specific purpose in mind. Houzez is a specialty theme offering the features and functionality realtors and real estate agencies look for to promote their businesses and their marketability. Houzez' features include advanced property search filters, IDX systems, property management functionality, and solid customer support.</p>
|
||||
<h2 id="">6. <a href="http://preview.themeforest.net/item/thegem-creative-multipurpose-highperformance-wordpress-theme/full_screen_preview/16061685?sort_priority_group=meta-smallbusiness-startups&utm_source=baw&utm_medium=listing&utm_campaign=smallbusiness" rel="nofollow">TheGem ? Creative Multi-Purpose High-Performance WordPress Theme</a></h2>
|
||||
<p><a href="http://preview.themeforest.net/item/thegem-creative-multipurpose-highperformance-wordpress-theme/full_screen_preview/16061685?sort_priority_group=meta-smallbusiness-startups&utm_source=baw&utm_medium=listing&utm_campaign=smallbusiness" rel="nofollow"><img src="https://www.sitepoint.com/wp-content/uploads/2018/09/15361215846.jpg" alt="" width="1000" height="415" class="aligncenter size-full wp-image-168707" /></a></p>
|
||||
<p>TheGem is without doubt one of the best WordPress business themes on the market. Its users like working with the trendy design concepts the authors have presented based on their analysis of current UX trends. Visual Composer is TheGems' page builder, and a judiciously selected set of plugins gives the web designer the flexibility to satisfy any small business's needs. The package includes a ready-to-go online fashion store.</p>
|
||||
<h2 id="">7. <a href="https://cesis.co/ts/rs.php?theme=cesis&utm_source=bawmedia&utm_medium=article&utm_campaign=bawmedia_cesis_sep2018&utm_content=post" rel="nofollow">Cesis ? Responsive Multi-Purpose WordPress Theme</a></h2>
|
||||
<p><a href="https://cesis.co/ts/rs.php?theme=cesis&utm_source=bawmedia&utm_medium=article&utm_campaign=bawmedia_cesis_sep2018&utm_content=post" rel="nofollow"><img src="https://www.sitepoint.com/wp-content/uploads/2018/09/15361215977.png" alt="" width="1000" height="415" class="aligncenter size-full wp-image-168708" /></a></p>
|
||||
<p>When you're searching among the best WordPress themes for small business websites, Cesis is definitely worth a closer look. Its easy-to-use interface combined with a host of design elements and options allows you to build virtually anything you want. This is an important attribute when working with small businesses and startups, each having their unique business model and branding style.</p>
|
||||
<h2 id="">8. <a href="http://wpdemos.themezaa.com/pofo/?utm_source=sitepoint.com&utm_medium=content&utm_campaign=wpstartups18" rel="nofollow">Pofo – Creative Portfolio and Blog WordPress Theme</a></h2>
|
||||
<p><a href="http://wpdemos.themezaa.com/pofo/?utm_source=sitepoint.com&utm_medium=content&utm_campaign=wpstartups18" rel="nofollow"><img src="https://www.sitepoint.com/wp-content/uploads/2018/09/15361216138.jpg" alt="" width="1000" height="415" class="aligncenter size-full wp-image-168709" /></a></p>
|
||||
<p>Web designers in need of small business WordPress themes include those whose clients represent creative teams and agencies as well as individual artists. Pofo is an ideal choice with its portfolio, eCommerce and blog features, bundled plugins, and more than 150 pre-built design elements. This premium theme's package also includes a nice assortment of home pages and more than 200 demo pages. Pofo is fully responsive, visually stunning, highly flexible, SEO and loading speed optimized.</p>
|
||||
<h2 id="conclusion">Conclusion</h2>
|
||||
<p>Did you like this selection of the best WordPress themes for small business websites? It provides you with a wide range of options and merits close and careful study.</p>
|
||||
<p>You really can't make a bad choice. With a little extra effort, you should be able to walk away with a perfect WordPress theme. It can be ideal for creating a certain small business website you have in mind. Likewise, it can help you create a range of websites for small businesses.</p>
|
||||
date 2018-09-06T09:30:53date_gmt 2018-09-06T16:30:53excerpt`protectedrendered]<5D>N<p class="wp-special"><em>This article was created in partnership with <a href="https://bawmedia.com/" rel="nofollow">BAWMedia</a>. Thank you for supporting the partners who make SitePoint possible.</em></p>
|
||||
<p>A well-designed website can serve as a powerful marketing tool. These days, creating one for a small business is not distressing or expensive at all. However, it was just a few short years ago.</p>
|
||||
<p>Today, you can take advantage of the features provided by the best WordPress themes. There are special themes for small business-oriented websites. It's not difficult to find a website-building theme that matches a specific business. This can be a startup, a service provider, or some other venture.</p>
|
||||
<p>You undoubtedly want nothing but the best business theme, right? Check out those described below. Each possesses functional designs loaded with amazing features. They will help you create a thoroughly engaging website to promote a business.</p>
|
||||
<h2>1. <a href="http://themes.muffingroup.com/be/splash/?utm_source=sitepoint.com&utm_medium=content&utm_campaign=wpstartups18" rel="nofollow">Be Theme</a></h2>
|
||||
<p><a href="http://themes.muffingroup.com/be/splash/?utm_source=sitepoint.com&utm_medium=content&utm_campaign=wpstartups18" rel="nofollow"><img src="https://www.sitepoint.com/wp-content/uploads/2018/09/15361215231.png" alt="" width="1000" height="481" class="aligncenter size-full wp-image-168702" /></a></p>
|
||||
<p>We’ll start with Be Theme, a responsive, multipurpose WordPress theme that takes every small business need into account with its more than 370 pre-built websites. There's a multiplicity of small business WordPress themes in this pre-built website collection ? each one embedded with the functionality you need to establish an effective online presence, and fully customizable to meet your business and marketing needs.</p>
|
||||
<p>The range of business niches covered is impressive, With more pre-built websites being added every month it's destined to become even more so. Web designers like Be Theme because it allows them to create a website for most small business types in as little as four hours.</p>
|
||||
<p>Clients appreciate the rapid turnaround they receive and the ease in which changes or additions they have in mind can be accommodated.</p>
|
||||
<p>Be Theme, one of the best WordPress themes for small business websites is a ThemeForest top 5 best seller whose core features include easy to work with page-building tools, a multiplicity of design features and options, and great support.</p>
|
||||
<h2>2. <a href="http://bit.ly/2wxuUpA" rel="nofollow">Astra</a></h2>
|
||||
<p><a href="http://bit.ly/2wxuUpA" rel="nofollow"><img src="https://www.sitepoint.com/wp-content/uploads/2018/09/15361215352.jpg" alt="" width="1000" height="491" class="aligncenter size-full wp-image-168703" /></a></p>
|
||||
<p>Astra is fast, fully customizable, and one of the best WordPress themes for business websites as well as for blogs and personal portfolios. Built with SEO in mind, Astra is responsive and WooCommerce ready ? mandatory features in today's online business environment. Its capabilities are easily extendible with premium addons and Astra can be used with most of the popular page builders. This free WP-based theme is definitely worth considering.</p>
|
||||
<h2 id="">3. <a href="http://bit.ly/2oiQkTS" rel="nofollow">The100</a></h2>
|
||||
<p><a href="http://bit.ly/2oiQkTS" rel="nofollow"><img src="https://www.sitepoint.com/wp-content/uploads/2018/09/15361215473.jpg" alt="" width="1000" height="491" class="aligncenter size-full wp-image-168704" /></a></p>
|
||||
<p>A theme selected for WordPress for small businesses can be free or it can be a premium theme requiring an expenditure on your behalf. There are several excellent free themes on the market, and one of them is The100. While it is advertised as having premium-like features, bear in mind that free themes like this one generally can't compete with premium themes. Nevertheless, The100 is an easy-to-use WP theme that features a multiplicity of layouts and plenty of customization options.</p>
|
||||
<h2 id="">4. <a href="https://undsgn.com/uncode/?utm_source=sitepoint.com&utm_medium=content&utm_campaign=wpstartups18" rel="nofollow">Uncode ? Creative Multiuse WordPress Theme</a></h2>
|
||||
<p><a href="https://undsgn.com/uncode/?utm_source=sitepoint.com&utm_medium=content&utm_campaign=wpstartups18" rel="nofollow"><img src="https://www.sitepoint.com/wp-content/uploads/2018/09/15361215604.jpg" alt="" width="1000" height="415" class="aligncenter size-full wp-image-168705" /></a></p>
|
||||
<p>Uncode has proven to be one of the best WordPress themes for business websites. It's a multipurpose theme featuring 30+ homepage concepts designed to get designers and their clients off to a fast start on any small business website. Features include an enhanced version of the popular Visual Composer page builder, and an Adaptive Images System that enables mobile users to see what you want and expect them to see.</p>
|
||||
featured_media<01><>formatstandardguid_rendered0https://www.sitepoint.com/?p=168697id<01><>link]<5D>https://www.sitepoint.com/the-8-best-wordpress-themes-for-small-business-websites/meta<74>modified 2018-09-04T21:31:02modified_gmt 2018-09-05T04:31:02ping_statusclosedslugDthe-8-best-wordpress-themes-for-small-business-websitesstatuspublishstickytags<67><01><><01><><01>btemplate
|
||||
title_renderedDThe 8 Best WordPress Themes for Small Business Websitestypepostv_linksh
|
||||
about<EFBFBD>_href?https://www.sitepoint.com/wp-json/wp/v2/types/postauthor<6F>`embeddablehref@https://www.sitepoint.com/wp-json/wp/v2/users/72676collection<6F>_href:https://www.sitepoint.com/wp-json/wp/v2/postscuries<65>ahref$https://api.w.org/{rel}namewptemplatedreplies<65>`embeddablehrefIhttps://www.sitepoint.com/wp-json/wp/v2/comments?post=168397self<6C>_hrefAhttps://www.sitepoint.com/wp-json/wp/v2/posts/168397version-history<72>_hrefKhttps://www.sitepoint.com/wp-json/wp/v2/posts/168397/revisionswp:attachment<6E>_hrefHhttps://www.sitepoint.com/wp-json/wp/v2/media?parent=168397 wp:featuredmedia<69>`embeddablehrefAhttps://www.sitepoint.com/wp-json/wp/v2/media/168458wp:term<72>aembeddablehrefKhttps://www.sitepoint.com/wp-json/wp/v2/categories?post=168397taxonomycategoryaembeddablehrefEhttps://www.sitepoint.com/wp-json/wp/v2/tags?post=168397taxonomypost_tagauthor<01><>categories<65><01>comment_statusopencontent`protectedrendered]<5D><><p class="wp-special"><em>This article was originally published on <a href="https://synd.co/2NCzyKk?" rel="canonical">MongoDB</a>. Thank you for supporting the partners who make SitePoint possible.</em></p>
|
||||
<p>You can build your online, operational workloads atop MongoDB and still respond to events in real time by kicking off <a href="https://aws.amazon.com/kinesis/">Amazon Kinesis</a> stream processing actions, using MongoDB Stitch Triggers.</p>
|
||||
<p>Let?s look at an example scenario in which a stream of data is being generated as a result of actions users take on a website. We?ll durably store the data and simultaneously feed a Kinesis process to do streaming analytics on something like cart abandonment, product recommendations, or even credit card fraud detection.</p>
|
||||
<p>We?ll do this by setting up a Stitch Trigger. When relevant data updates are made in MongoDB, the trigger will use a Stitch Function to call out to AWS Kinesis, as you can see in this architecture diagram:</p>
|
||||
<p><img src="https://www.sitepoint.com/wp-content/uploads/2018/09/1536043981image5-d4wtr8tn5c.png" alt="" width="1525" height="844" class="aligncenter size-full wp-image-168426" /></p>
|
||||
<h4 id="whatyoullneedtofollowalong">What you?ll need to follow along</h4>
|
||||
<ol>
|
||||
<li><strong>An Atlas instance</strong> <br />
|
||||
If you don?t already have an application running on Atlas, you can follow our <a href="https://docs.atlas.mongodb.com/getting-started/">getting started with Atlas guide here</a>. In this example, we?ll be using a database called <em><strong>streamdata</strong></em>, with a collection called <em><strong>clickdata</strong></em> where we?re writing data from our web-based e-commerce application.</li>
|
||||
<li><strong>An AWS account and a Kinesis stream</strong> <br />
|
||||
In this example, we?ll use a Kinesis stream to send data downstream to additional applications such as Kinesis Analytics. This is the stream we want to feed our updates into.</li>
|
||||
<li><strong>A Stitch application</strong> <br />
|
||||
If you don?t already have a Stitch application, <a href="https://cloud.mongodb.com/user#/atlas/login">log into Atlas</a>, and click <strong>Stitch Apps</strong> from the navigation on the left, then click <strong>Create New Application</strong>.</li>
|
||||
</ol>
|
||||
<h3 id="createacollection">Create a Collection</h3>
|
||||
<p>The first step is to create a database and collection from the Stitch application console. Click <strong>Rules</strong> from the left navigation menu and click the <strong>Add Collection</strong> button. Type <strong>streamdata</strong> for the database and <strong>clickdata</strong> for the collection name. Select the template labeled Users can <strong>only read and write their own data</strong> and provide a field name where we?ll specify the user id.</p>
|
||||
<p><img src="https://www.sitepoint.com/wp-content/uploads/2018/09/1536044010image2-51q4gsi7u2.png" alt="Figure 2. Create a collection" width="1999" height="923" class="aligncenter size-full wp-image-168427" /></p>
|
||||
<h3 id="configuringstitchtotalktoaws">Configuring Stitch to Talk to AWS</h3>
|
||||
<p>Stitch lets you configure <em>Services</em> to interact with external <a href="https://docs.mongodb.com/stitch/reference/partner-services/amazon-service/">services such as AWS Kinesis</a>. Choose <strong>Services</strong> from the navigation on the left, and click the <strong>Add a Service</strong> button, select the AWS service and set <strong>AWS <a href="https://aws.amazon.com/blogs/security/wheres-my-secret-access-key/">Access Key ID, and Secret Access Key</a></strong>.</p>
|
||||
<p><img src="https://www.sitepoint.com/wp-content/uploads/2018/09/1536044038image8-2a9rr3qc4k.png" alt="Figure 3. Service Configuration in Stitch" width="1999" height="1041" class="aligncenter size-full wp-image-168428" /></p>
|
||||
<p><em>Services</em> use <em>Rules</em> to specify what aspect of a service Stitch can use, and how. Add a rule which will enable that service to communicate with Kinesis by clicking the button labeled NEW RULE. Name the rule ?kinesis? as we?ll be using this specific rule to enable communication with AWS Kinesis. In the section marked Action, select the API labeled Kinesis and select All Actions.</p>
|
||||
<p><img src="https://www.sitepoint.com/wp-content/uploads/2018/09/1536044067image1-h45v2y48z7.gif" alt="Figure 4. Add a rule to enable integration with Kinesis" width="1424" height="746" class="aligncenter size-full wp-image-168429" /></p>
|
||||
<h3 id="writeafunctionthatstreamsdocumentsintokinesis">Write a Function that Streams Documents into Kinesis</h3>
|
||||
<p>Now that we have a working AWS service, we can use it to put records into a Kinesis stream. The way we do that in Stitch is with Functions. Let?s set up a <em>putKinesisRecord</em> function.</p>
|
||||
<p>Select Functions from the left-hand menu, and click Create New Function. Provide a name for the function and paste the following in the body of the function.</p>
|
||||
<p><img src="https://webassets.mongodb.com/_com_assets/cms/image6-7wtiny60ji.gif" alt="Figure 5. Example Function - putKinesisRecord" /></p>
|
||||
<pre><code class="javascript language-javascript">exports = function(event){
|
||||
const awsService = context.services.get('aws');
|
||||
try{
|
||||
awsService.kinesis().PutRecord({
|
||||
Data: JSON.stringify(event.fullDocument),
|
||||
StreamName: "stitchStream",
|
||||
PartitionKey: "1"
|
||||
}).then(function(response) {
|
||||
return response;
|
||||
});
|
||||
}
|
||||
catch(error){
|
||||
console.log(JSON.parse(error));
|
||||
}
|
||||
};
|
||||
</code></pre>
|
||||
<h3 id="testoutthefunction">Test Out the Function</h3>
|
||||
<p>Let?s make sure everything is working by calling that function manually. From the <strong>Function Editor</strong>, Click <strong>Console</strong> to view the interactive javascript console for Stitch.</p>
|
||||
<p><img src="https://www.sitepoint.com/wp-content/uploads/2018/09/1536044162image9-utm60qwobl.png" alt="" width="1999" height="947" class="aligncenter size-full wp-image-168430" /></p>
|
||||
<p>Functions called from Triggers require an event. To test execution of our function, we?ll need to pass a dummy event to the function. Creating variables from the console in Stitch is simple. Simply set the value of the variable to a JSON document. For our simple example, use the following:</p>
|
||||
<pre><code class="javascript language-javascript">event = {
|
||||
"operationType": "replace",
|
||||
"fullDocument": {
|
||||
"color": "black",
|
||||
"inventory": {
|
||||
"$numberInt": "1"
|
||||
},
|
||||
"overview": "test document",
|
||||
"price": {
|
||||
"$numberDecimal": "123"
|
||||
},
|
||||
"type": "backpack"
|
||||
},
|
||||
"ns": {
|
||||
"db": "streamdata",
|
||||
"coll": "clickdata"
|
||||
}
|
||||
}
|
||||
exports(event);
|
||||
</code></pre>
|
||||
<p>Paste the above into the console and click the button labeled <strong>Run Function As</strong>. Select a user and the function will execute.</p>
|
||||
<p>Ta-da!</p>
|
||||
<h3 id="puttingittogetherwithstitchtriggers">Putting It Together with Stitch Triggers</h3>
|
||||
<p>We?ve got our MongoDB collection living in Atlas, receiving events from our web app. We?ve got our Kinesis stream ready for data. We?ve got a Stitch Function that can put data into a Kinesis stream.</p>
|
||||
<p><a href="https://docs.mongodb.com/stitch/mongodb/triggers/">Configuring Stitch Triggers</a> is so simple it?s almost anticlimactic. Click <strong>Triggers</strong> from the left navigation, name your trigger, provide the database and collection context, and select the database events Stitch will react to with execution of a function.</p>
|
||||
<p>For the database and collection, use the names from step one. Now we?ll set the operations we want to watch with our trigger. (Some triggers might care about all of them ? inserts, updates, deletes, and replacements ? while others can be more efficient because they logically can only matter for some of those.) In our case, we?re going to watch for insert, update and replace operations.</p>
|
||||
<p>Now we specify our <em>putKinesisRecord</em> function as the linked function, and we?re done.</p>
|
||||
<p><img src="https://www.sitepoint.com/wp-content/uploads/2018/09/1536044235image4-mkz3w061j6.gif" alt="Figure 6. Trigger Configuration in Stitch" width="1430" height="928" class="aligncenter size-full wp-image-168431" /></p>
|
||||
<p>As part of trigger execution, Stitch will forward details associated with the trigger event, including the full document involved in the event (i.e. the newly inserted, updated, or deleted document from the collection.) This is where we can evaluate some condition or attribute of the incoming document and decide whether or not to put the record onto a stream.</p>
|
||||
<h3 id="testthetrigger">Test the Trigger!</h3>
|
||||
<p>Amazon provides a dashboard which will enable you to view details associated with the data coming into your stream.</p>
|
||||
<p><img src="https://www.sitepoint.com/wp-content/uploads/2018/09/1536044263image3-fbdbnbh0zx.png" alt="Figure 7. Kinesis Stream Monitoring" width="1878" height="1021" class="aligncenter size-full wp-image-168432" /></p>
|
||||
<p>As you execute the function from within Stitch, you?ll begin to see the data entering the Kinesis stream.</p>
|
||||
<h3 id="buildingmorefunctionality">Building More Functionality</h3>
|
||||
<p>So far our trigger is pretty basic ? it watches a collection and when any updates or inserts happen, it feeds the entire document to our Kinesis stream. From here we can build out some more intelligent functionality. To wrap up this post, let?s look at what we can do with the data once it?s been durably stored in MongoDB and placed into a stream.</p>
|
||||
<p>Once the record is in the Kinesis Stream you can configure additional services downstream to act on the data. A common use case incorporates Amazon Kinesis Data Analytics to perform analytics on the streaming data. <a href="https://aws.amazon.com/kinesis/data-analytics/">Amazon Kinesis Data Analytics</a> offers pre-configured templates to accomplish things like anomaly detection, simple alerts, aggregations, and more.</p>
|
||||
<p>For example, our stream of data will contain orders resulting from purchases. These orders may originate from point-of-sale systems, as well as from our web-based e-commerce application. Kinesis Analytics can be leveraged to create applications that process the incoming stream of data. For our example, we could build a <a href="https://docs.aws.amazon.com/kinesisanalytics/latest/dev/app-anomaly-detection.html">machine learning algorithm</a> to detect anomalies in the data or create a product performance leaderboard from a <a href="https://docs.aws.amazon.com/kinesisanalytics/latest/dev/sliding-window-concepts.html">sliding</a>, or <a href="https://docs.aws.amazon.com/kinesisanalytics/latest/dev/tumbling-window-concepts.html">tumbling</a> window of data from our stream.</p>
|
||||
<p><img src="https://www.sitepoint.com/wp-content/uploads/2018/09/1536044289image7-5c0nd1hscb.png" alt="Figure 8. Amazon Data Analytics - Anomaly Detection Example" width="1203" height="676" class="aligncenter size-full wp-image-168433" /></p>
|
||||
<h3 id="wrappingup">Wrapping Up</h3>
|
||||
<p>Now you can connect MongoDB to Kinesis. From here, you?re able to leverage any one of the many services offered from Amazon Web Services to build on your application. In our next article in the series, we?ll focus on getting the data back from Kinesis into MongoDB. In the meantime, let us know what you?re building with Atlas, Stitch, and Kinesis!</p>
|
||||
<h4 id="resources">Resources</h4>
|
||||
<p><strong>MongoDB Atlas</strong></p>
|
||||
<ul>
|
||||
<li><a href="https://www.mongodb.com/presentations/tutorial-series-getting-started-with-mongodb-atlas">Getting Started</a> – Tutorial Playlist</li>
|
||||
<li><a href="https://www.mongodb.com/cloud/atlas/lp/general">Signup for Free</a></li>
|
||||
<li><a href="https://www.mongodb.com/cloud/atlas/faq">FAQ</a></li>
|
||||
</ul>
|
||||
<p><strong>MongoDB Stitch</strong></p>
|
||||
<ul>
|
||||
<li><a href="https://docs.mongodb.com/stitch/getting-started/">Getting Started Documentation</a></li>
|
||||
<li><a href="https://docs.mongodb.com/stitch/tutorials/">MongoDB Stitch Tutorials</a></li>
|
||||
<li><a href="https://www.mongodb.com/collateral/mongodb-stitch-serverless-platform">MongoDB Stitch White Paper</a></li>
|
||||
<li><a href="https://www.mongodb.com/webinar/mongodb-stitch">Webinar ? 8th August 2018</a></li>
|
||||
</ul>
|
||||
<p><strong>Amazon Kinesis</strong></p>
|
||||
<ul>
|
||||
<li><a href="https://docs.aws.amazon.com/streams/latest/dev/getting-started.html">Getting Started</a></li>
|
||||
<li><a href="https://aws.amazon.com/kinesis/data-streams/">Kinesis Data Streams</a></li>
|
||||
<li><a href="https://aws.amazon.com/kinesis/data-analytics/">Kinesis Data Analytics</a></li>
|
||||
</ul>
|
||||
date 2018-09-06T09:30:31date_gmt 2018-09-06T16:30:31excerpt`protectedrendered]<5D>Y<p class="wp-special"><em>This article was originally published on <a href="https://synd.co/2NCzyKk?" rel="canonical">MongoDB</a>. Thank you for supporting the partners who make SitePoint possible.</em></p>
|
||||
<p>You can build your online, operational workloads atop MongoDB and still respond to events in real time by kicking off <a href="https://aws.amazon.com/kinesis/">Amazon Kinesis</a> stream processing actions, using MongoDB Stitch Triggers.</p>
|
||||
<p>Let?s look at an example scenario in which a stream of data is being generated as a result of actions users take on a website. We?ll durably store the data and simultaneously feed a Kinesis process to do streaming analytics on something like cart abandonment, product recommendations, or even credit card fraud detection.</p>
|
||||
<p>We?ll do this by setting up a Stitch Trigger. When relevant data updates are made in MongoDB, the trigger will use a Stitch Function to call out to AWS Kinesis, as you can see in this architecture diagram:</p>
|
||||
<p><img src="https://www.sitepoint.com/wp-content/uploads/2018/09/1536043981image5-d4wtr8tn5c.png" alt="" width="1525" height="844" class="aligncenter size-full wp-image-168426" /></p>
|
||||
<h4 id="whatyoullneedtofollowalong">What you?ll need to follow along</h4>
|
||||
<ol>
|
||||
<li><strong>An Atlas instance</strong> <br />
|
||||
If you don?t already have an application running on Atlas, you can follow our <a href="https://docs.atlas.mongodb.com/getting-started/">getting started with Atlas guide here</a>. In this example, we?ll be using a database called <em><strong>streamdata</strong></em>, with a collection called <em><strong>clickdata</strong></em> where we?re writing data from our web-based e-commerce application.</li>
|
||||
<li><strong>An AWS account and a Kinesis stream</strong> <br />
|
||||
In this example, we?ll use a Kinesis stream to send data downstream to additional applications such as Kinesis Analytics. This is the stream we want to feed our updates into.</li>
|
||||
<li><strong>A Stitch application</strong> <br />
|
||||
If you don?t already have a Stitch application, <a href="https://cloud.mongodb.com/user#/atlas/login">log into Atlas</a>, and click <strong>Stitch Apps</strong> from the navigation on the left, then click <strong>Create New Application</strong>.</li>
|
||||
</ol>
|
||||
<h3 id="createacollection">Create a Collection</h3>
|
||||
<p>The first step is to create a database and collection from the Stitch application console. Click <strong>Rules</strong> from the left navigation menu and click the <strong>Add Collection</strong> button. Type <strong>streamdata</strong> for the database and <strong>clickdata</strong> for the collection name. Select the template labeled Users can <strong>only read and write their own data</strong> and provide a field name where we?ll specify the user id.</p>
|
||||
<p><img src="https://www.sitepoint.com/wp-content/uploads/2018/09/1536044010image2-51q4gsi7u2.png" alt="Figure 2. Create a collection" width="1999" height="923" class="aligncenter size-full wp-image-168427" /></p>
|
||||
<h3 id="configuringstitchtotalktoaws">Configuring Stitch to Talk to AWS</h3>
|
||||
<p>Stitch lets you configure <em>Services</em> to interact with external <a href="https://docs.mongodb.com/stitch/reference/partner-services/amazon-service/">services such as AWS Kinesis</a>. Choose <strong>Services</strong> from the navigation on the left, and click the <strong>Add a Service</strong> button, select the AWS service and set <strong>AWS <a href="https://aws.amazon.com/blogs/security/wheres-my-secret-access-key/">Access Key ID, and Secret Access Key</a></strong>.</p>
|
||||
<p><img src="https://www.sitepoint.com/wp-content/uploads/2018/09/1536044038image8-2a9rr3qc4k.png" alt="Figure 3. Service Configuration in Stitch" width="1999" height="1041" class="aligncenter size-full wp-image-168428" /></p>
|
||||
<p><em>Services</em> use <em>Rules</em> to specify what aspect of a service Stitch can use, and how. Add a rule which will enable that service to communicate with Kinesis by clicking the button labeled NEW RULE. Name the rule ?kinesis? as we?ll be using this specific rule to enable communication with AWS Kinesis. In the section marked Action, select the API labeled Kinesis and select All Actions.</p>
|
||||
<p><img src="https://www.sitepoint.com/wp-content/uploads/2018/09/1536044067image1-h45v2y48z7.gif" alt="Figure 4. Add a rule to enable integration with Kinesis" width="1424" height="746" class="aligncenter size-full wp-image-168429" /></p>
|
||||
<h3 id="writeafunctionthatstreamsdocumentsintokinesis">Write a Function that Streams Documents into Kinesis</h3>
|
||||
<p>Now that we have a working AWS service, we can use it to put records into a Kinesis stream. The way we do that in Stitch is with Functions. Let?s set up a <em>putKinesisRecord</em> function.</p>
|
||||
<p>Select Functions from the left-hand menu, and click Create New Function. Provide a name for the function and paste the following in the body of the function.</p>
|
||||
<p><img src="https://webassets.mongodb.com/_com_assets/cms/image6-7wtiny60ji.gif" alt="Figure 5. Example Function - putKinesisRecord" /></p>
|
||||
<pre><code class="javascript language-javascript">exports = function(event){
|
||||
const awsService = context.services.get('aws');
|
||||
try{
|
||||
awsService.kinesis().PutRecord({
|
||||
Data: JSON.stringify(event.fullDocument),
|
||||
StreamName: "stitchStream",
|
||||
PartitionKey: "1"
|
||||
}).then(function(response) {
|
||||
return response;
|
||||
});
|
||||
}
|
||||
catch(error){
|
||||
console.log(JSON.parse(error));
|
||||
}
|
||||
};
|
||||
</code></pre>
|
||||
<h3 id="testoutthefunction">Test Out the Function</h3>
|
||||
<p>Let?s make sure everything is working by calling that function manually. From the <strong>Function Editor</strong>, Click <strong>Console</strong> to view the interactive javascript console for Stitch.</p>
|
||||
featured_media<01><>formatstandardguid_rendered0https://www.sitepoint.com/?p=168397id<01><>link]<5D>https://www.sitepoint.com/integrating-mongodb-and-amazon-kinesis-for-intelligent-durable-streams/meta<74>modified 2018-09-04T00:20:42modified_gmt 2018-09-04T07:20:42ping_statusclosedslugSintegrating-mongodb-and-amazon-kinesis-for-intelligent-durable-streamsstatuspublishstickytags<67><01><><01><01>btemplate
|
||||
title_renderedTIntegrating MongoDB and Amazon Kinesis for Intelligent, Durable Streamstypepostv_linksh
|
||||
about<EFBFBD>_href?https://www.sitepoint.com/wp-json/wp/v2/types/postauthor<6F>`embeddablehref@https://www.sitepoint.com/wp-json/wp/v2/users/72596collection<6F>_href:https://www.sitepoint.com/wp-json/wp/v2/postscuries<65>ahref$https://api.w.org/{rel}namewptemplatedreplies<65>`embeddablehrefIhttps://www.sitepoint.com/wp-json/wp/v2/comments?post=167869self<6C>_hrefAhttps://www.sitepoint.com/wp-json/wp/v2/posts/167869version-history<72>_hrefKhttps://www.sitepoint.com/wp-json/wp/v2/posts/167869/revisionswp:attachment<6E>_hrefHhttps://www.sitepoint.com/wp-json/wp/v2/media?parent=167869 wp:featuredmedia<69>`embeddablehrefAhttps://www.sitepoint.com/wp-json/wp/v2/media/167879wp:term<72>aembeddablehrefKhttps://www.sitepoint.com/wp-json/wp/v2/categories?post=167869taxonomycategoryaembeddablehrefEhttps://www.sitepoint.com/wp-json/wp/v2/tags?post=167869taxonomypost_tagauthor<01><>categories<65><01><01>comment_statusopencontent`protectedrendered]<5D><p><iframe width="900" height="506" src="https://www.youtube.com/embed/c9YzHtgsiKE" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe></p>
|
||||
<p class="wp-special"><em>This article was originally published on <a href="https://resource.alibabacloud.com/webinar/live.htm?spm=a2c5p.11425181.0.0.4d913c86X8D869&webinarId=26" rel="canonical">Alibaba Cloud</a>. Thank you for supporting the partners who make SitePoint possible.</em></p>
|
||||
<p class="wp-special"><strong>Think you got a better tip for making the best use of Alibaba Cloud services? Tell us about it and go in for your chance to win a Macbook Pro (plus other cool stuff). <a href="https://www.sitepoint.com/alibaba-competition">Find out more here</a>.</strong></p>
|
||||
<p>Gain an introduction to ApsaraDB for RDS, a cloud-based relational database product provided by Alibaba Cloud. In this webinar you will watch over the shoulder of a Solution Architect and Trainer, as he covers the basic concepts and features of ApsaraDB for RDS including:</p>
|
||||
<ul>
|
||||
<li>HA feature (Master/Slave Architecture, Backup/Recovery, Temporary Instance)</li>
|
||||
<li>Scalability features (Read-only Instance)</li>
|
||||
<li>Security and Monitoring features</li>
|
||||
</ul>
|
||||
<p>This webinar is ideally suited for database engineers and beginners to the Alibaba Cloud product suite.</p>
|
||||
date 2018-09-05T09:30:47date_gmt 2018-09-05T16:30:47excerpt`protectedrendered]<5D><p><iframe width="560" height="315" src="https://www.youtube.com/embed/c9YzHtgsiKE" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe></p>
|
||||
<p class="wp-special"><em>This article was originally published on <a href="https://resource.alibabacloud.com/webinar/live.htm?spm=a2c5p.11425181.0.0.4d913c86X8D869&webinarId=26" rel="canonical">Alibaba Cloud</a>. Thank you for supporting the partners who make SitePoint possible.</em></p>
|
||||
<p>Gain an introduction to ApsaraDB for RDS, a cloud-based relational database product provided by Alibaba Cloud. In this webinar you will watch over the shoulder of a Solution Architect and Trainer, as he covers the basic concepts and features of ApsaraDB for RDS including:</p>
|
||||
<ul>
|
||||
<li>HA feature (Master/Slave Architecture, Backup/Recovery, Temporary Instance)</li>
|
||||
<li>Scalability features (Read-only Instance)</li>
|
||||
<li>Security and Monitoring features</li>
|
||||
</ul>
|
||||
<p>This webinar is ideally suited for database engineers and beginners to the Alibaba Cloud product suite.</p>
|
||||
featured_media<01><>formatstandardguid_rendered0https://www.sitepoint.com/?p=167869id<01><>link]<5D>https://www.sitepoint.com/how-to-set-up-a-secure-relational-database-on-alibaba-cloud/meta<74>modified 2018-09-06T01:21:52modified_gmt 2018-09-06T08:21:52ping_statusclosedslugHhow-to-set-up-a-secure-relational-database-on-alibaba-cloudstatuspublishstickytags<67><01><><01><>template
|
||||
title_renderedHHow to Set up a Secure Relational Database on Alibaba Cloudtypepost
|
22
wcfg/build.gradle
Normal file
22
wcfg/build.gradle
Normal file
@@ -0,0 +1,22 @@
|
||||
plugins {
|
||||
id 'antlr'
|
||||
}
|
||||
|
||||
configurations {
|
||||
api {
|
||||
exclude group: 'org.antlr', module: 'antlr4'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation catalog.jwo
|
||||
implementation rootProject
|
||||
implementation catalog.antlr.runtime
|
||||
|
||||
antlr catalog.antlr
|
||||
}
|
||||
|
||||
generateGrammarSource {
|
||||
maxHeapSize = "64m"
|
||||
arguments += ['-package', 'net.woggioni.wson.wcfg']
|
||||
}
|
106
wcfg/src/main/antlr/net/woggioni/wson/wcfg/WCFG.g4
Normal file
106
wcfg/src/main/antlr/net/woggioni/wson/wcfg/WCFG.g4
Normal file
@@ -0,0 +1,106 @@
|
||||
grammar WCFG;
|
||||
|
||||
wcfg
|
||||
: assignment* export?
|
||||
;
|
||||
|
||||
export
|
||||
: 'export' value ';'
|
||||
;
|
||||
|
||||
assignment
|
||||
: IDENTIFIER ':=' value ';'
|
||||
;
|
||||
|
||||
expression
|
||||
: (obj | IDENTIFIER) ('<<' (obj | IDENTIFIER))+
|
||||
;
|
||||
|
||||
obj
|
||||
: '{' pair (',' pair)* '}'
|
||||
| '{' '}'
|
||||
;
|
||||
|
||||
pair
|
||||
: STRING ':' value
|
||||
;
|
||||
|
||||
array
|
||||
: '[' value (',' value)* ']'
|
||||
| '[' ']'
|
||||
;
|
||||
|
||||
value
|
||||
: NULL
|
||||
| BOOLEAN
|
||||
| NUMBER
|
||||
| STRING
|
||||
| IDENTIFIER
|
||||
| obj
|
||||
| array
|
||||
| expression
|
||||
;
|
||||
|
||||
BOOLEAN
|
||||
: 'true'
|
||||
| 'false'
|
||||
;
|
||||
|
||||
NULL
|
||||
: 'null'
|
||||
;
|
||||
|
||||
NUMBER
|
||||
: '-'? INT ('.' [0-9] +)? EXP?
|
||||
;
|
||||
|
||||
IDENTIFIER: Letter LetterOrDigit*;
|
||||
|
||||
STRING
|
||||
: '"' (ESC | SAFECODEPOINT)* '"'
|
||||
;
|
||||
|
||||
fragment ESC
|
||||
: '\\' (["\\/bfnrt] | UNICODE)
|
||||
;
|
||||
|
||||
|
||||
fragment UNICODE
|
||||
: 'u' HEX HEX HEX HEX
|
||||
;
|
||||
|
||||
|
||||
fragment HEX
|
||||
: [0-9a-fA-F]
|
||||
;
|
||||
|
||||
|
||||
fragment SAFECODEPOINT
|
||||
: ~ ["\\\u0000-\u001F]
|
||||
;
|
||||
|
||||
|
||||
fragment INT
|
||||
: '0' | [1-9] [0-9]*
|
||||
;
|
||||
|
||||
// no leading zeros
|
||||
|
||||
fragment EXP
|
||||
: [Ee] [+\-]? INT
|
||||
;
|
||||
|
||||
fragment LetterOrDigit
|
||||
: Letter
|
||||
| [0-9]
|
||||
;
|
||||
|
||||
fragment Letter
|
||||
: [a-zA-Z$_] // these are the "java letters" below 0x7F
|
||||
| ~[\u0000-\u007F\uD800-\uDBFF] // covers all characters above 0x7F which are not a surrogate
|
||||
| [\uD800-\uDBFF] [\uDC00-\uDFFF] // covers UTF-16 surrogate pairs encodings for U+10000 to U+10FFFF
|
||||
;
|
||||
|
||||
WS
|
||||
: [ \t\n\r] + -> skip
|
||||
;
|
8
wcfg/src/main/java/module-info.java
Normal file
8
wcfg/src/main/java/module-info.java
Normal file
@@ -0,0 +1,8 @@
|
||||
module net.woggioni.wson.wcfg {
|
||||
requires static lombok;
|
||||
requires org.antlr.antlr4.runtime;
|
||||
|
||||
requires net.woggioni.jwo;
|
||||
requires net.woggioni.wson;
|
||||
exports net.woggioni.wson.wcfg;
|
||||
}
|
@@ -0,0 +1,98 @@
|
||||
package net.woggioni.wson.wcfg;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.woggioni.jwo.LazyValue;
|
||||
import net.woggioni.wson.traversal.ValueIdentity;
|
||||
import net.woggioni.wson.value.ObjectValue;
|
||||
import net.woggioni.wson.xface.Value;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class CompositeObjectValue implements ObjectValue {
|
||||
|
||||
private final List<? extends Value> elements;
|
||||
|
||||
private LazyValue<ObjectValue> wrapped;
|
||||
|
||||
public CompositeObjectValue(List<? extends Value> elements, Value.Configuration cfg) {
|
||||
this.elements = elements;
|
||||
this.wrapped = LazyValue.of(() -> {
|
||||
ObjectValue result = ObjectValue.newInstance(cfg);
|
||||
List<ValueIdentity> identities = new ArrayList<>();
|
||||
for (Value element : elements) {
|
||||
if (element instanceof CompositeObjectValue compositeObjectValue) {
|
||||
boolean differenceFound = false;
|
||||
for (int i = 0; i < compositeObjectValue.elements.size(); i++) {
|
||||
ObjectValue objectValue = (ObjectValue) compositeObjectValue.elements.get(i);
|
||||
if (!differenceFound && (i >= identities.size() || !Objects.equals(
|
||||
identities.get(i),
|
||||
new ValueIdentity(compositeObjectValue.elements.get(i))))) {
|
||||
differenceFound = true;
|
||||
}
|
||||
if (differenceFound) {
|
||||
merge(result, objectValue);
|
||||
identities.add(new ValueIdentity(objectValue));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
merge(result, (ObjectValue) element);
|
||||
identities.add(new ValueIdentity(element));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}, LazyValue.ThreadSafetyMode.NONE);
|
||||
}
|
||||
|
||||
private static void merge(ObjectValue v1, ObjectValue v2) {
|
||||
for (Map.Entry<String, Value> entry : v2) {
|
||||
String key = entry.getKey();
|
||||
Value value2put = entry.getValue();
|
||||
Value putResult = v1.getOrPut(key, value2put);
|
||||
if (putResult != value2put) {
|
||||
if (putResult.type() == Value.Type.OBJECT && value2put.type() == Value.Type.OBJECT) {
|
||||
ObjectValue ov = ObjectValue.newInstance();
|
||||
merge(ov, (ObjectValue) putResult);
|
||||
merge(ov, (ObjectValue) value2put);
|
||||
v1.put(key, ov);
|
||||
} else {
|
||||
v1.put(key, value2put);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<String, Value>> iterator() {
|
||||
return wrapped.get().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value get(String key) {
|
||||
return wrapped.get().get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value getOrDefault(String key, Value defaultValue) {
|
||||
return wrapped.get().getOrDefault(key, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(String key) {
|
||||
return wrapped.get().has(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return wrapped.get().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Map.Entry<String, Value>> asObject() {
|
||||
return this::iterator;
|
||||
}
|
||||
}
|
18
wcfg/src/main/java/net/woggioni/wson/wcfg/ErrorHandler.java
Normal file
18
wcfg/src/main/java/net/woggioni/wson/wcfg/ErrorHandler.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package net.woggioni.wson.wcfg;
|
||||
|
||||
import org.antlr.v4.runtime.BaseErrorListener;
|
||||
import org.antlr.v4.runtime.RecognitionException;
|
||||
import org.antlr.v4.runtime.Recognizer;
|
||||
|
||||
class ErrorHandler extends BaseErrorListener {
|
||||
@Override
|
||||
public void syntaxError(Recognizer<?, ?> recognizer,
|
||||
Object offendingSymbol,
|
||||
int line, int charPositionInLine,
|
||||
String msg,
|
||||
RecognitionException e) {
|
||||
throw new SyntaxError(msg, line, charPositionInLine);
|
||||
}
|
||||
|
||||
|
||||
}
|
329
wcfg/src/main/java/net/woggioni/wson/wcfg/ListenerImpl.java
Normal file
329
wcfg/src/main/java/net/woggioni/wson/wcfg/ListenerImpl.java
Normal file
@@ -0,0 +1,329 @@
|
||||
package net.woggioni.wson.wcfg;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import net.woggioni.jwo.JWO;
|
||||
import net.woggioni.wson.value.ArrayValue;
|
||||
import net.woggioni.wson.value.BooleanValue;
|
||||
import net.woggioni.wson.value.FloatValue;
|
||||
import net.woggioni.wson.value.IntegerValue;
|
||||
import net.woggioni.wson.value.NullValue;
|
||||
import net.woggioni.wson.value.ObjectValue;
|
||||
import net.woggioni.wson.value.StringValue;
|
||||
import net.woggioni.wson.xface.Value;
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.antlr.v4.runtime.misc.Interval;
|
||||
import org.antlr.v4.runtime.tree.ErrorNode;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static net.woggioni.jwo.JWO.newThrowable;
|
||||
import static net.woggioni.jwo.JWO.tail;
|
||||
|
||||
class ListenerImpl implements WCFGListener {
|
||||
|
||||
private final Value.Configuration cfg;
|
||||
|
||||
@Getter
|
||||
private Value result;
|
||||
|
||||
private final Map<String, ValueHolder> unresolvedReferences = new TreeMap<>();
|
||||
|
||||
private interface StackLevel {
|
||||
Value getValue();
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
private static class ExportStackLevel implements StackLevel {
|
||||
|
||||
@Setter
|
||||
private Value value;
|
||||
|
||||
@Override
|
||||
public Value getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ArrayStackLevel implements StackLevel {
|
||||
private final ArrayValue value = new ArrayValue();
|
||||
|
||||
@Override
|
||||
public Value getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ObjectStackLevel implements StackLevel {
|
||||
public String currentKey;
|
||||
private final ObjectValue value;
|
||||
|
||||
public ObjectStackLevel(Value.Configuration cfg) {
|
||||
value = ObjectValue.newInstance(cfg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ExpressionStackLevel implements StackLevel {
|
||||
private final Value.Configuration cfg;
|
||||
private ObjectValue value = null;
|
||||
public List<Value> elements = new ArrayList<>();
|
||||
|
||||
public ExpressionStackLevel(Value.Configuration cfg) {
|
||||
this.cfg = cfg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value getValue() {
|
||||
if (value == null) {
|
||||
List<ObjectValue> objects = elements.stream()
|
||||
.map(it -> {
|
||||
if (it instanceof ObjectValue ov)
|
||||
return ov;
|
||||
else if (it instanceof ValueHolder vh) {
|
||||
return (ObjectValue) vh.getDelegate();
|
||||
} else {
|
||||
throw newThrowable(RuntimeException.class, "");
|
||||
}
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
value = new CompositeObjectValue(objects, cfg);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
private final List<StackLevel> stack = new ArrayList<>();
|
||||
|
||||
private void add2Last(Value value) {
|
||||
StackLevel last = tail(stack);
|
||||
if (last instanceof ArrayStackLevel asl) {
|
||||
asl.value.add(value);
|
||||
if (value instanceof ValueHolder holder) {
|
||||
Value arrayValue = asl.getValue();
|
||||
int index = arrayValue.size() - 1;
|
||||
holder.addDeleter(() -> arrayValue.set(index, holder.getDelegate()));
|
||||
}
|
||||
} else if (last instanceof ObjectStackLevel osl) {
|
||||
String key = osl.currentKey;
|
||||
Value objectValue = osl.getValue();
|
||||
objectValue.put(key, value);
|
||||
if (value instanceof ValueHolder holder) {
|
||||
holder.addDeleter(() -> {
|
||||
objectValue.put(key, holder.getDelegate());
|
||||
});
|
||||
}
|
||||
} else if (last instanceof ExpressionStackLevel esl) {
|
||||
List<Value> values = esl.elements;
|
||||
int index = values.size();
|
||||
values.add(value);
|
||||
if (value instanceof ValueHolder holder) {
|
||||
holder.addDeleter(() -> {
|
||||
values.set(index, holder.getDelegate());
|
||||
});
|
||||
}
|
||||
} else if (last instanceof ExportStackLevel esl) {
|
||||
esl.setValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
private static String unquote(String quoted) {
|
||||
return quoted.substring(1, quoted.length() - 1);
|
||||
}
|
||||
|
||||
public ListenerImpl(Value.Configuration cfg) {
|
||||
this.cfg = cfg;
|
||||
StackLevel sl = new ObjectStackLevel(cfg);
|
||||
result = sl.getValue();
|
||||
stack.add(sl);
|
||||
}
|
||||
|
||||
private StackLevel pop() {
|
||||
int size = stack.size() - 1;
|
||||
StackLevel sl = stack.get(size);
|
||||
stack.remove(size);
|
||||
return sl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterWcfg(WCFGParser.WcfgContext ctx) {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void exitWcfg(WCFGParser.WcfgContext ctx) {
|
||||
stack.clear();
|
||||
for (ValueHolder holder : unresolvedReferences.values()) {
|
||||
if (holder.getDelegate() == null) {
|
||||
TerminalNode node = holder.getNode();
|
||||
throw new ParseError(
|
||||
"Undeclared identifier '" + node.getText() + "'",
|
||||
node.getSymbol().getLine(),
|
||||
node.getSymbol().getCharPositionInLine() + 1
|
||||
);
|
||||
}
|
||||
holder.replace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterAssignment(WCFGParser.AssignmentContext ctx) {
|
||||
ObjectStackLevel osl = (ObjectStackLevel) stack.get(0);
|
||||
String key = ctx.IDENTIFIER().getText();
|
||||
osl.currentKey = key;
|
||||
ValueHolder holder = unresolvedReferences.computeIfAbsent(key, it -> new ValueHolder(ctx.IDENTIFIER()));
|
||||
// holder.addDeleter(() -> holder.setDelegate(result.get(key)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitAssignment(WCFGParser.AssignmentContext ctx) {
|
||||
ObjectStackLevel osl = (ObjectStackLevel) stack.get(0);
|
||||
String key = osl.currentKey;
|
||||
unresolvedReferences.get(key).setDelegate(osl.getValue().get(key));
|
||||
osl.currentKey = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterExpression(WCFGParser.ExpressionContext ctx) {
|
||||
ExpressionStackLevel esl = new ExpressionStackLevel(cfg);
|
||||
stack.add(esl);
|
||||
for(TerminalNode node : ctx.IDENTIFIER()) {
|
||||
String key = node.getSymbol().getText();
|
||||
ValueHolder holder = unresolvedReferences
|
||||
.computeIfAbsent(key, k -> new ValueHolder(node));
|
||||
int index = esl.elements.size();
|
||||
esl.elements.add(holder);
|
||||
holder.addDeleter(() -> {
|
||||
esl.elements.set(index, holder.getDelegate());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitExpression(WCFGParser.ExpressionContext ctx) {
|
||||
add2Last(pop().getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterObj(WCFGParser.ObjContext ctx) {
|
||||
ObjectStackLevel osl = new ObjectStackLevel(cfg);
|
||||
stack.add(osl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitObj(WCFGParser.ObjContext ctx) {
|
||||
add2Last(pop().getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterPair(WCFGParser.PairContext ctx) {
|
||||
ObjectStackLevel osl = (ObjectStackLevel) stack.get(stack.size() - 1);
|
||||
osl.currentKey = unquote(ctx.STRING().getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitPair(WCFGParser.PairContext ctx) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterArray(WCFGParser.ArrayContext ctx) {
|
||||
ArrayStackLevel asl = new ArrayStackLevel();
|
||||
stack.add(asl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitArray(WCFGParser.ArrayContext ctx) {
|
||||
add2Last(pop().getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterValue(WCFGParser.ValueContext ctx) {
|
||||
if (ctx.obj() != null) {
|
||||
} else if (ctx.array() != null) {
|
||||
} else if (ctx.STRING() != null) {
|
||||
add2Last(new StringValue(unquote(ctx.STRING().getText())));
|
||||
} else if (ctx.BOOLEAN() != null) {
|
||||
add2Last(new BooleanValue(Boolean.parseBoolean(ctx.BOOLEAN().getText())));
|
||||
} else if (ctx.NULL() != null) {
|
||||
add2Last(new NullValue());
|
||||
} else if (ctx.NUMBER() != null) {
|
||||
String text = ctx.NUMBER().getText();
|
||||
if (text.indexOf('.') < 0) {
|
||||
add2Last(new IntegerValue(Long.parseLong(text)));
|
||||
} else {
|
||||
add2Last(new FloatValue(Double.parseDouble(text)));
|
||||
}
|
||||
} else if (ctx.IDENTIFIER() != null) {
|
||||
String name = ctx.IDENTIFIER().getText();
|
||||
ValueHolder referredValue = unresolvedReferences.computeIfAbsent(name,
|
||||
it -> new ValueHolder(ctx.IDENTIFIER())
|
||||
);
|
||||
// referredValue.addError(() -> new ParseError(
|
||||
// "Undeclared identifier '" + name + "'",
|
||||
// ctx.start.getLine(),
|
||||
// ctx.start.getCharPositionInLine() + 1
|
||||
// )
|
||||
// );
|
||||
// Value referredValue = result.getOrDefault(name, null);
|
||||
// if(referredValue == null) {
|
||||
// throw new ParseError(
|
||||
// "Undeclared identifier '" + name + "'",
|
||||
// ctx.start.getLine(),
|
||||
// ctx.start.getCharPositionInLine() + 1
|
||||
// );
|
||||
// }
|
||||
add2Last(referredValue);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitValue(WCFGParser.ValueContext ctx) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTerminal(TerminalNode node) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitErrorNode(ErrorNode node) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterEveryRule(ParserRuleContext ctx) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitEveryRule(ParserRuleContext ctx) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterExport(WCFGParser.ExportContext ctx) {
|
||||
ExportStackLevel esl = new ExportStackLevel();
|
||||
stack.add(esl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitExport(WCFGParser.ExportContext ctx) {
|
||||
result = pop().getValue();
|
||||
if (result instanceof ValueHolder holder) {
|
||||
holder.addDeleter(() -> {
|
||||
result = holder.getDelegate();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
15
wcfg/src/main/java/net/woggioni/wson/wcfg/ParseError.java
Normal file
15
wcfg/src/main/java/net/woggioni/wson/wcfg/ParseError.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package net.woggioni.wson.wcfg;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class ParseError extends RuntimeException {
|
||||
public ParseError(String message, int line, int column) {
|
||||
super(message + String.format(" at %d:%d", line, column));
|
||||
}
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return super.getMessage();
|
||||
}
|
||||
}
|
15
wcfg/src/main/java/net/woggioni/wson/wcfg/SyntaxError.java
Normal file
15
wcfg/src/main/java/net/woggioni/wson/wcfg/SyntaxError.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package net.woggioni.wson.wcfg;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class SyntaxError extends RuntimeException {
|
||||
public SyntaxError(String message, int line, int column) {
|
||||
super(message + String.format(" at %d:%d", line, column));
|
||||
}
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return super.getMessage();
|
||||
}
|
||||
}
|
135
wcfg/src/main/java/net/woggioni/wson/wcfg/ValueHolder.java
Normal file
135
wcfg/src/main/java/net/woggioni/wson/wcfg/ValueHolder.java
Normal file
@@ -0,0 +1,135 @@
|
||||
package net.woggioni.wson.wcfg;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.SneakyThrows;
|
||||
import net.woggioni.wson.xface.Value;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
class ValueHolder implements Value {
|
||||
|
||||
@Getter
|
||||
private final TerminalNode node;
|
||||
private List<Runnable> deleters = new ArrayList<>();
|
||||
public void addDeleter(Runnable runnable) {
|
||||
deleters.add(runnable);
|
||||
}
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
private Value delegate = null;
|
||||
|
||||
@Override
|
||||
public Type type() {
|
||||
return delegate.type();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNull() {
|
||||
return delegate.isNull();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean asBoolean() {
|
||||
return delegate.asBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long asInteger() {
|
||||
return delegate.asInteger();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double asFloat() {
|
||||
return delegate.asFloat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String asString() {
|
||||
return delegate.asString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Value> asArray() {
|
||||
return delegate.asArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Map.Entry<String, Value>> asObject() {
|
||||
return delegate.asObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return delegate.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(Value value) {
|
||||
delegate.add(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(int index, Value value) {
|
||||
delegate.set(index, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value pop() {
|
||||
return delegate.pop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value head() {
|
||||
return delegate.head();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value tail() {
|
||||
return delegate.tail();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value get(int index) {
|
||||
return delegate.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String key, Value value) {
|
||||
delegate.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value get(String key) {
|
||||
return delegate.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value getOrDefault(String key, Value defaultValue) {
|
||||
return delegate.getOrDefault(key, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value getOrPut(String key, Value value2Put) {
|
||||
return delegate.getOrPut(key, value2Put);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(String key) {
|
||||
return delegate.has(key);
|
||||
}
|
||||
|
||||
public void replace() {
|
||||
for(Runnable run : deleters) {
|
||||
run.run();
|
||||
}
|
||||
}
|
||||
}
|
53
wcfg/src/main/java/net/woggioni/wson/wcfg/WConfig.java
Normal file
53
wcfg/src/main/java/net/woggioni/wson/wcfg/WConfig.java
Normal file
@@ -0,0 +1,53 @@
|
||||
package net.woggioni.wson.wcfg;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import net.woggioni.wson.value.ObjectValue;
|
||||
import net.woggioni.wson.xface.Value;
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.CodePointCharStream;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.antlr.v4.runtime.tree.ParseTreeWalker;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class WConfig {
|
||||
|
||||
private final Value.Configuration cfg;
|
||||
private final Value value;
|
||||
|
||||
@SneakyThrows
|
||||
private WConfig(Reader reader, Value.Configuration cfg) {
|
||||
this.cfg = cfg;
|
||||
CodePointCharStream inputStream = CharStreams.fromReader(reader);
|
||||
WCFGLexer lexer = new WCFGLexer(inputStream);
|
||||
CommonTokenStream commonTokenStream = new CommonTokenStream(lexer);
|
||||
WCFGParser parser = new WCFGParser(commonTokenStream);
|
||||
parser.removeErrorListeners();
|
||||
parser.addErrorListener(new ErrorHandler());
|
||||
ListenerImpl listener = new ListenerImpl(cfg);
|
||||
ParseTreeWalker walker = new ParseTreeWalker();
|
||||
walker.walk(listener, parser.wcfg());
|
||||
value = listener.getResult();
|
||||
}
|
||||
|
||||
public Value get(String key) {
|
||||
return value.get(key);
|
||||
}
|
||||
|
||||
public Value get(String ...overrides) {
|
||||
return new CompositeObjectValue(
|
||||
Arrays.stream(overrides)
|
||||
.map(k -> (ObjectValue) value.get(k))
|
||||
.collect(Collectors.toList()), cfg);
|
||||
}
|
||||
|
||||
public Value whole() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static WConfig parse(Reader reader, Value.Configuration cfg) {
|
||||
return new WConfig(reader, cfg);
|
||||
}
|
||||
}
|
57
wcfg/src/test/java/net/woggioni/wson/wcfg/ParseTest.java
Normal file
57
wcfg/src/test/java/net/woggioni/wson/wcfg/ParseTest.java
Normal file
@@ -0,0 +1,57 @@
|
||||
package net.woggioni.wson.wcfg;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import net.woggioni.wson.serialization.json.JSONDumper;
|
||||
import net.woggioni.wson.value.ObjectValue;
|
||||
import net.woggioni.wson.xface.Value;
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.CodePointCharStream;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.antlr.v4.runtime.tree.ParseTreeWalker;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
|
||||
public class ParseTest {
|
||||
|
||||
@SneakyThrows
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"build.wcfg",
|
||||
"test.wcfg",
|
||||
"recursive.wcfg",
|
||||
"recursive2.wcfg",
|
||||
"recursive3.wcfg",
|
||||
})
|
||||
public void test(String resource) {
|
||||
Value.Configuration cfg = Value.Configuration.builder()
|
||||
.objectValueImplementation(ObjectValue.Implementation.HashMap)
|
||||
.serializeReferences(true)
|
||||
.build();
|
||||
try(Reader reader = new InputStreamReader(getClass().getClassLoader().getResourceAsStream(resource))) {
|
||||
WConfig wcfg = WConfig.parse(reader, cfg);
|
||||
new JSONDumper(cfg).dump(wcfg.whole(), System.out);
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
public void test2() {
|
||||
Value.Configuration cfg = Value.Configuration.builder().serializeReferences(true).build();
|
||||
try (Reader reader = new InputStreamReader(getClass().getResourceAsStream("/build.wcfg"))) {
|
||||
WConfig wcfg = WConfig.parse(reader, cfg);
|
||||
Value result = wcfg.get("release", "dev");
|
||||
try (OutputStream os = new BufferedOutputStream(new FileOutputStream("/tmp/build.json"))) {
|
||||
new JSONDumper(cfg).dump(result, os);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
55
wcfg/src/test/resources/build.wcfg
Normal file
55
wcfg/src/test/resources/build.wcfg
Normal file
@@ -0,0 +1,55 @@
|
||||
default := {
|
||||
"source-directory": ".",
|
||||
"generator": "Unix Makefiles",
|
||||
"options": {
|
||||
"CMAKE_BUILD_TYPE": "Release"
|
||||
}
|
||||
};
|
||||
|
||||
dev := default << {
|
||||
"options": {
|
||||
"CMAKE_BUILD_TYPE": "Debug"
|
||||
}
|
||||
};
|
||||
|
||||
jenkins := {
|
||||
"build-directory" : "build"
|
||||
};
|
||||
|
||||
release := default << {
|
||||
"clean": true,
|
||||
"options": {
|
||||
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
|
||||
}
|
||||
};
|
||||
|
||||
lin64 := default << {
|
||||
"options" : {
|
||||
"STATIC_LIBSTDCXX" : true,
|
||||
"QT5_STATIC": "ON"
|
||||
}
|
||||
};
|
||||
|
||||
win64 := default << {
|
||||
"options": {
|
||||
"CMAKE_TOOLCHAIN_FILE": "/opt/x-tools/x86_64-w64-mingw32/cmake/toolchain-x86_64-w64-mingw32.cmake",
|
||||
"STATIC_BUILD": "ON",
|
||||
"QT5_STATIC": "ON"
|
||||
}
|
||||
};
|
||||
|
||||
win32 := default << {
|
||||
"options": {
|
||||
"CMAKE_TOOLCHAIN_FILE": "/opt/x-tools/i686-w64-mingw32/cmake/toolchain-i686-w64-mingw32.cmake",
|
||||
"STATIC_BUILD": "ON",
|
||||
"QT5_STATIC": "ON"
|
||||
}
|
||||
};
|
||||
|
||||
musl := default << {
|
||||
"options": {
|
||||
"CMAKE_TOOLCHAIN_FILE": "/opt/x-tools/x86_64-woggioni-linux-musl/cmake/toolchain-x86_64-woggioni-linux-musl.cmake",
|
||||
"QT5_STATIC": "ON",
|
||||
"STATIC_BUILD": "ON"
|
||||
}
|
||||
};
|
8
wcfg/src/test/resources/recursive.wcfg
Normal file
8
wcfg/src/test/resources/recursive.wcfg
Normal file
@@ -0,0 +1,8 @@
|
||||
value1 := {
|
||||
"key" : "value",
|
||||
"myself" : value1
|
||||
};
|
||||
|
||||
value2 := [value1];
|
||||
|
||||
value3 := [1, 2, value3, null, false];
|
1
wcfg/src/test/resources/recursive2.wcfg
Normal file
1
wcfg/src/test/resources/recursive2.wcfg
Normal file
@@ -0,0 +1 @@
|
||||
value := [1, 2, value, null, false];
|
5
wcfg/src/test/resources/recursive3.wcfg
Normal file
5
wcfg/src/test/resources/recursive3.wcfg
Normal file
@@ -0,0 +1,5 @@
|
||||
value := [1, 2, value2, null, false];
|
||||
|
||||
value2 := [3, 4, value, null, true, value3];
|
||||
|
||||
value3 := {"key" : "Some string" };
|
25
wcfg/src/test/resources/test.wcfg
Normal file
25
wcfg/src/test/resources/test.wcfg
Normal file
@@ -0,0 +1,25 @@
|
||||
default := {
|
||||
"foo" : 2,
|
||||
"bar" : true
|
||||
};
|
||||
|
||||
overridden := default << {
|
||||
"foo" : 0,
|
||||
"enabled" : false,
|
||||
"window" : null
|
||||
};
|
||||
|
||||
window := {
|
||||
"width" : 640,
|
||||
"height" : 480
|
||||
};
|
||||
|
||||
foobar := {
|
||||
"child" : overridden << {
|
||||
"window" : window
|
||||
} << {
|
||||
"enabled" : true
|
||||
}
|
||||
};
|
||||
|
||||
export default << foobar;
|
@@ -1,25 +1,66 @@
|
||||
plugins {
|
||||
id 'maven-publish'
|
||||
id 'net.woggioni.gradle.envelope'
|
||||
id 'org.jetbrains.kotlin.jvm'
|
||||
alias catalog.plugins.envelope
|
||||
alias catalog.plugins.graalvm.native.image
|
||||
alias catalog.plugins.sambal
|
||||
}
|
||||
import net.woggioni.gradle.graalvm.NativeImageTask
|
||||
import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform
|
||||
|
||||
|
||||
dependencies {
|
||||
implementation group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: getProperty('kotlin.version')
|
||||
implementation group: 'com.beust', name: 'jcommander'
|
||||
implementation group: "org.slf4j", name: "slf4j-simple"
|
||||
implementation catalog.jwo
|
||||
implementation catalog.picocli
|
||||
implementation catalog.slf4j.simple
|
||||
implementation rootProject
|
||||
implementation project(':wcfg')
|
||||
}
|
||||
|
||||
envelopeJar {
|
||||
mainClass = 'net.woggioni.wson.cli.MainKt'
|
||||
application {
|
||||
mainClass = 'net.woggioni.wson.cli.WsonCli'
|
||||
mainModule = 'net.woggioni.wson.cli'
|
||||
}
|
||||
|
||||
compileJava {
|
||||
options.compilerArgs = [
|
||||
"--patch-module",
|
||||
"net.woggioni.wson.cli=${sourceSets.main.output.asPath}"
|
||||
]
|
||||
options.javaModuleMainClass = 'net.woggioni.wson.cli.MainKt'
|
||||
options.javaModuleVersion = project.version
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
kotlinOptions.with {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
configureNativeImage {
|
||||
// args = [
|
||||
// 'wson',
|
||||
// '-f',
|
||||
// '../test-utils/src/main/resources/wordpress.json',
|
||||
// '-t',
|
||||
// 'jbon',
|
||||
// '-o',
|
||||
// '/dev/null'
|
||||
// ]
|
||||
|
||||
args = [
|
||||
'wcfg',
|
||||
'-f',
|
||||
'../wcfg/src/test/resources/build.wcfg',
|
||||
'-t',
|
||||
'jbon',
|
||||
'-o',
|
||||
'/dev/null'
|
||||
]
|
||||
|
||||
mergeConfiguration = true
|
||||
}
|
||||
|
||||
Provider<NativeImageTask> nativeImageTaskProvider = tasks.named("nativeImage") {
|
||||
useMusl = true
|
||||
buildStaticImage = true
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives nativeImageTaskProvider.map(NativeImageTask.&getOutputFile)
|
||||
}
|
||||
|
||||
publishing {
|
||||
@@ -31,6 +72,11 @@ publishing {
|
||||
publications {
|
||||
myDistribution(MavenPublication) {
|
||||
artifact envelopeJar
|
||||
def host = DefaultNativePlatform.host()
|
||||
artifact(
|
||||
source: nativeImageTaskProvider,
|
||||
classifier: host.operatingSystem.name + '-' + host.architecture.name
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
6
wson-cli/native-image/jni-config.json
Normal file
6
wson-cli/native-image/jni-config.json
Normal file
@@ -0,0 +1,6 @@
|
||||
[
|
||||
{
|
||||
"name":"java.lang.Boolean",
|
||||
"methods":[{"name":"getBoolean","parameterTypes":["java.lang.String"] }]
|
||||
}
|
||||
]
|
1
wson-cli/native-image/native-image.properties
Normal file
1
wson-cli/native-image/native-image.properties
Normal file
@@ -0,0 +1 @@
|
||||
Args=-H:Optimize=3 --initialize-at-run-time=net.woggioni.wson.cli.WsonCli
|
8
wson-cli/native-image/predefined-classes-config.json
Normal file
8
wson-cli/native-image/predefined-classes-config.json
Normal file
@@ -0,0 +1,8 @@
|
||||
[
|
||||
{
|
||||
"type":"agent-extracted",
|
||||
"classes":[
|
||||
]
|
||||
}
|
||||
]
|
||||
|
2
wson-cli/native-image/proxy-config.json
Normal file
2
wson-cli/native-image/proxy-config.json
Normal file
@@ -0,0 +1,2 @@
|
||||
[
|
||||
]
|
248
wson-cli/native-image/reflect-config.json
Normal file
248
wson-cli/native-image/reflect-config.json
Normal file
@@ -0,0 +1,248 @@
|
||||
[
|
||||
{
|
||||
"name":"com.sun.crypto.provider.AESCipher$General",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"com.sun.crypto.provider.ARCFOURCipher",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"com.sun.crypto.provider.ChaCha20Cipher$ChaCha20Poly1305",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"com.sun.crypto.provider.DESCipher",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"com.sun.crypto.provider.DESedeCipher",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"com.sun.crypto.provider.GaloisCounterMode$AESGCM",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"groovy.lang.Closure"
|
||||
},
|
||||
{
|
||||
"name":"java.lang.Object",
|
||||
"allDeclaredFields":true,
|
||||
"queryAllDeclaredMethods":true
|
||||
},
|
||||
{
|
||||
"name":"java.nio.file.Path"
|
||||
},
|
||||
{
|
||||
"name":"java.nio.file.Paths",
|
||||
"methods":[{"name":"get","parameterTypes":["java.lang.String","java.lang.String[]"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.security.AlgorithmParametersSpi"
|
||||
},
|
||||
{
|
||||
"name":"java.security.KeyStoreSpi"
|
||||
},
|
||||
{
|
||||
"name":"java.security.SecureRandomParameters"
|
||||
},
|
||||
{
|
||||
"name":"java.sql.Connection"
|
||||
},
|
||||
{
|
||||
"name":"java.sql.Driver"
|
||||
},
|
||||
{
|
||||
"name":"java.sql.DriverManager",
|
||||
"methods":[{"name":"getConnection","parameterTypes":["java.lang.String"] }, {"name":"getDriver","parameterTypes":["java.lang.String"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.sql.Time",
|
||||
"methods":[{"name":"<init>","parameterTypes":["long"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.sql.Timestamp",
|
||||
"methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.time.Duration",
|
||||
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.time.Instant",
|
||||
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.time.LocalDate",
|
||||
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.time.LocalDateTime",
|
||||
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.time.LocalTime",
|
||||
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.time.MonthDay",
|
||||
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.time.OffsetDateTime",
|
||||
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.time.OffsetTime",
|
||||
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.time.Period",
|
||||
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.time.Year",
|
||||
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.time.YearMonth",
|
||||
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.time.ZoneId",
|
||||
"methods":[{"name":"of","parameterTypes":["java.lang.String"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.time.ZoneOffset",
|
||||
"methods":[{"name":"of","parameterTypes":["java.lang.String"] }]
|
||||
},
|
||||
{
|
||||
"name":"java.time.ZonedDateTime",
|
||||
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||
},
|
||||
{
|
||||
"name":"javax.security.auth.x500.X500Principal",
|
||||
"fields":[{"name":"thisX500Name"}],
|
||||
"methods":[{"name":"<init>","parameterTypes":["sun.security.x509.X500Name"] }]
|
||||
},
|
||||
{
|
||||
"name":"net.woggioni.wson.cli.OutputTypeConverter",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"net.woggioni.wson.cli.SerializationTypeConverter",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"net.woggioni.wson.cli.VersionProvider",
|
||||
"allDeclaredFields":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"net.woggioni.wson.cli.WcfgCommand",
|
||||
"allDeclaredFields":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"net.woggioni.wson.cli.WsonCli",
|
||||
"allDeclaredFields":true,
|
||||
"queryAllDeclaredMethods":true
|
||||
},
|
||||
{
|
||||
"name":"net.woggioni.wson.cli.WsonCommand",
|
||||
"allDeclaredFields":true,
|
||||
"queryAllDeclaredMethods":true,
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"sun.security.pkcs12.PKCS12KeyStore",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"sun.security.pkcs12.PKCS12KeyStore$DualFormatPKCS12",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"sun.security.provider.JavaKeyStore$JKS",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"sun.security.provider.NativePRNG",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"<init>","parameterTypes":["java.security.SecureRandomParameters"] }]
|
||||
},
|
||||
{
|
||||
"name":"sun.security.provider.SHA",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"sun.security.provider.X509Factory",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"sun.security.rsa.RSAKeyFactory$Legacy",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"sun.security.ssl.KeyManagerFactoryImpl$SunX509",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"sun.security.ssl.SSLContextImpl$DefaultSSLContext",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"sun.security.ssl.TrustManagerFactoryImpl$PKIXFactory",
|
||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||
},
|
||||
{
|
||||
"name":"sun.security.x509.AuthorityInfoAccessExtension",
|
||||
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
||||
},
|
||||
{
|
||||
"name":"sun.security.x509.AuthorityKeyIdentifierExtension",
|
||||
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
||||
},
|
||||
{
|
||||
"name":"sun.security.x509.BasicConstraintsExtension",
|
||||
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
||||
},
|
||||
{
|
||||
"name":"sun.security.x509.CRLDistributionPointsExtension",
|
||||
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
||||
},
|
||||
{
|
||||
"name":"sun.security.x509.CertificatePoliciesExtension",
|
||||
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
||||
},
|
||||
{
|
||||
"name":"sun.security.x509.ExtendedKeyUsageExtension",
|
||||
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
||||
},
|
||||
{
|
||||
"name":"sun.security.x509.IssuerAlternativeNameExtension",
|
||||
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
||||
},
|
||||
{
|
||||
"name":"sun.security.x509.KeyUsageExtension",
|
||||
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
||||
},
|
||||
{
|
||||
"name":"sun.security.x509.NetscapeCertTypeExtension",
|
||||
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
||||
},
|
||||
{
|
||||
"name":"sun.security.x509.PrivateKeyUsageExtension",
|
||||
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
||||
},
|
||||
{
|
||||
"name":"sun.security.x509.SubjectAlternativeNameExtension",
|
||||
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
||||
},
|
||||
{
|
||||
"name":"sun.security.x509.SubjectKeyIdentifierExtension",
|
||||
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
||||
}
|
||||
]
|
17
wson-cli/native-image/resource-config.json
Normal file
17
wson-cli/native-image/resource-config.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"resources":{
|
||||
"includes":[{
|
||||
"pattern":"\\QMETA-INF/MANIFEST.MF\\E"
|
||||
}, {
|
||||
"pattern":"\\QMETA-INF/services/java.lang.System$LoggerFinder\\E"
|
||||
}, {
|
||||
"pattern":"\\QMETA-INF/services/java.net.spi.URLStreamHandlerProvider\\E"
|
||||
}, {
|
||||
"pattern":"\\QMETA-INF/services/java.time.zone.ZoneRulesProvider\\E"
|
||||
}, {
|
||||
"pattern":"\\QMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider\\E"
|
||||
}, {
|
||||
"pattern":"\\Qsimplelogger.properties\\E"
|
||||
}]},
|
||||
"bundles":[]
|
||||
}
|
8
wson-cli/native-image/serialization-config.json
Normal file
8
wson-cli/native-image/serialization-config.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"types":[
|
||||
],
|
||||
"lambdaCapturingTypes":[
|
||||
],
|
||||
"proxies":[
|
||||
]
|
||||
}
|
12
wson-cli/src/main/java/module-info.java
Normal file
12
wson-cli/src/main/java/module-info.java
Normal file
@@ -0,0 +1,12 @@
|
||||
module net.woggioni.wson.cli {
|
||||
requires static lombok;
|
||||
requires net.woggioni.jwo;
|
||||
requires net.woggioni.wson;
|
||||
requires net.woggioni.wson.wcfg;
|
||||
requires org.slf4j;
|
||||
requires org.slf4j.simple;
|
||||
requires info.picocli;
|
||||
exports net.woggioni.wson.cli;
|
||||
|
||||
opens net.woggioni.wson.cli to info.picocli;
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
package net.woggioni.wson.cli;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import picocli.CommandLine;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Objects;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
|
||||
abstract class AbstractVersionProvider implements CommandLine.IVersionProvider {
|
||||
private String version;
|
||||
private String vcsHash;
|
||||
|
||||
@SneakyThrows
|
||||
protected AbstractVersionProvider(String specificationTitle) {
|
||||
String version = null;
|
||||
String vcsHash = null;
|
||||
Enumeration<URL> it = getClass().getClassLoader().getResources(JarFile.MANIFEST_NAME);
|
||||
while (it.hasMoreElements()) {
|
||||
URL manifestURL = it.nextElement();
|
||||
Manifest mf = new Manifest();
|
||||
try (InputStream inputStream = manifestURL.openStream()) {
|
||||
mf.read(inputStream);
|
||||
}
|
||||
Attributes mainAttributes = mf.getMainAttributes();
|
||||
if (Objects.equals(specificationTitle, mainAttributes.getValue(Attributes.Name.SPECIFICATION_TITLE))) {
|
||||
version = mainAttributes.getValue(Attributes.Name.SPECIFICATION_VERSION);
|
||||
vcsHash = mainAttributes.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
|
||||
}
|
||||
}
|
||||
if (version == null || vcsHash == null) {
|
||||
throw new RuntimeException("Version information not found in manifest");
|
||||
}
|
||||
this.version = version;
|
||||
this.vcsHash = vcsHash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getVersion() {
|
||||
if (version.endsWith("-SNAPSHOT")) {
|
||||
return new String[]{version, vcsHash};
|
||||
} else {
|
||||
return new String[]{version};
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
package net.woggioni.wson.cli;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static net.woggioni.jwo.JWO.newThrowable;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
enum SerializationFormat {
|
||||
JSON("json"), JBON("jbon");
|
||||
|
||||
private final String name;
|
||||
|
||||
|
||||
public static SerializationFormat parse(String value) {
|
||||
return Arrays.stream(SerializationFormat.values())
|
||||
.filter(sf -> Objects.equals(sf.name, value))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> {
|
||||
var availableValues = Stream.of(
|
||||
JSON,
|
||||
JBON
|
||||
).map(SerializationFormat::name).collect(Collectors.joining(", "));
|
||||
throw newThrowable(IllegalArgumentException.class,
|
||||
"Unknown serialization format '%s', possible values are %s",
|
||||
value, availableValues
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
package net.woggioni.wson.cli;
|
||||
|
||||
import picocli.CommandLine;
|
||||
|
||||
class SerializationTypeConverter implements CommandLine.ITypeConverter<SerializationFormat> {
|
||||
@Override
|
||||
public SerializationFormat convert(String value) {
|
||||
return SerializationFormat.parse(value);
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
package net.woggioni.wson.cli;
|
||||
|
||||
class VersionProvider extends AbstractVersionProvider {
|
||||
protected VersionProvider() {
|
||||
super("wson-cli");
|
||||
}
|
||||
}
|
@@ -0,0 +1,92 @@
|
||||
package net.woggioni.wson.cli;
|
||||
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import net.woggioni.jwo.Fun;
|
||||
import net.woggioni.wson.serialization.binary.JBONDumper;
|
||||
import net.woggioni.wson.serialization.json.JSONDumper;
|
||||
import net.woggioni.wson.value.ObjectValue;
|
||||
import net.woggioni.wson.wcfg.WConfig;
|
||||
import net.woggioni.wson.xface.Value;
|
||||
import picocli.CommandLine;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
|
||||
|
||||
@CommandLine.Command(
|
||||
name = "wcfg",
|
||||
versionProvider = VersionProvider.class)
|
||||
public class WcfgCommand implements Runnable {
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {"-f", "--file"},
|
||||
description = {"Name of the input file to parse"}
|
||||
)
|
||||
private Path fileName;
|
||||
|
||||
@CommandLine.Option(names = {"-o", "--output"},
|
||||
description = {"Name of the JSON file to generate"})
|
||||
private Path output;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {"-t", "--type"},
|
||||
description = {"Output type"},
|
||||
converter = {SerializationTypeConverter.class})
|
||||
private SerializationFormat outputType = SerializationFormat.JSON;
|
||||
|
||||
@CommandLine.Option(names = {"-d", "--resolve-refernces"}, description = "Resolve references")
|
||||
private boolean resolveReferences = false;
|
||||
|
||||
@CommandLine.Option(names = {"-h", "--help"}, usageHelp = true)
|
||||
private boolean help = false;
|
||||
|
||||
@SneakyThrows
|
||||
public void run() {
|
||||
Value.Configuration cfg = Value.Configuration.builder()
|
||||
.objectValueImplementation(ObjectValue.Implementation.LinkedHashMap)
|
||||
.serializeReferences(!resolveReferences)
|
||||
.build();
|
||||
InputStream inputStream;
|
||||
if (fileName != null) {
|
||||
inputStream = new BufferedInputStream(Files.newInputStream(fileName));
|
||||
} else {
|
||||
inputStream = System.in;
|
||||
}
|
||||
|
||||
Value result;
|
||||
try(Reader reader = new InputStreamReader(inputStream)) {
|
||||
WConfig wcfg = WConfig.parse(reader, cfg);
|
||||
result = wcfg.whole();
|
||||
}
|
||||
|
||||
|
||||
OutputStream outputStream = Optional.ofNullable(output)
|
||||
.map((Fun<Path, OutputStream>) Files::newOutputStream)
|
||||
.<OutputStream>map(BufferedOutputStream::new)
|
||||
.orElse(System.out);
|
||||
|
||||
switch (outputType) {
|
||||
case JSON:
|
||||
try (Writer writer = new OutputStreamWriter(outputStream)) {
|
||||
new JSONDumper(cfg).dump(result, writer);
|
||||
}
|
||||
break;
|
||||
case JBON:
|
||||
try {
|
||||
new JBONDumper(cfg).dump(result, outputStream);
|
||||
} finally {
|
||||
outputStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
33
wson-cli/src/main/java/net/woggioni/wson/cli/WsonCli.java
Normal file
33
wson-cli/src/main/java/net/woggioni/wson/cli/WsonCli.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package net.woggioni.wson.cli;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import picocli.CommandLine;
|
||||
|
||||
@Slf4j
|
||||
@CommandLine.Command(
|
||||
name = "wcfg",
|
||||
versionProvider = VersionProvider.class,
|
||||
subcommands = {WsonCommand.class, WcfgCommand.class})
|
||||
public class WsonCli implements Runnable {
|
||||
public static void main(String[] args) {
|
||||
final var commandLine = new CommandLine(new WsonCli());
|
||||
commandLine.setExecutionExceptionHandler((ex, cl, parseResult) -> {
|
||||
log.error(ex.getMessage(), ex);
|
||||
return CommandLine.ExitCode.SOFTWARE;
|
||||
});
|
||||
System.exit(commandLine.execute(args));
|
||||
}
|
||||
|
||||
@Getter
|
||||
@CommandLine.Option(names = {"-V", "--version"}, versionHelp = true)
|
||||
private boolean versionHelp;
|
||||
|
||||
@CommandLine.Spec
|
||||
private CommandLine.Model.CommandSpec spec;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
spec.commandLine().usage(System.out);
|
||||
}
|
||||
}
|
104
wson-cli/src/main/java/net/woggioni/wson/cli/WsonCommand.java
Normal file
104
wson-cli/src/main/java/net/woggioni/wson/cli/WsonCommand.java
Normal file
@@ -0,0 +1,104 @@
|
||||
package net.woggioni.wson.cli;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import net.woggioni.jwo.Fun;
|
||||
import net.woggioni.wson.serialization.binary.JBONDumper;
|
||||
import net.woggioni.wson.serialization.binary.JBONParser;
|
||||
import net.woggioni.wson.serialization.json.JSONDumper;
|
||||
import net.woggioni.wson.serialization.json.JSONParser;
|
||||
import net.woggioni.wson.xface.Value;
|
||||
import picocli.CommandLine;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
|
||||
import static net.woggioni.jwo.JWO.newThrowable;
|
||||
|
||||
|
||||
@CommandLine.Command(
|
||||
name = "wson",
|
||||
versionProvider = VersionProvider.class)
|
||||
public class WsonCommand implements Runnable {
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {"-f", "--file"},
|
||||
description = {"Name of the input file to parse"}
|
||||
)
|
||||
private Path fileName;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {"--input-type"},
|
||||
description = {"Input type"},
|
||||
converter = {SerializationTypeConverter.class})
|
||||
private SerializationFormat inputType = SerializationFormat.JSON;
|
||||
|
||||
@CommandLine.Option(names = {"-o", "--output"},
|
||||
description = {"Name of the JSON file to generate"})
|
||||
private Path output;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {"-t", "--type"},
|
||||
description = {"Output type"},
|
||||
converter = {SerializationTypeConverter.class})
|
||||
private SerializationFormat outputType = SerializationFormat.JSON;
|
||||
|
||||
@CommandLine.Option(names = {"-h", "--help"}, usageHelp = true)
|
||||
private boolean help = false;
|
||||
|
||||
@SneakyThrows
|
||||
public void run() {
|
||||
Value.Configuration cfg = Value.Configuration.builder().serializeReferences(true).build();
|
||||
InputStream inputStream;
|
||||
if (fileName != null) {
|
||||
inputStream = new BufferedInputStream(Files.newInputStream(fileName));
|
||||
} else {
|
||||
inputStream = System.in;
|
||||
}
|
||||
|
||||
Value result;
|
||||
switch (inputType) {
|
||||
case JSON:
|
||||
try (Reader reader = new InputStreamReader(inputStream)) {
|
||||
result = new JSONParser(cfg).parse(reader);
|
||||
}
|
||||
break;
|
||||
case JBON:
|
||||
try {
|
||||
result = new JBONParser(cfg).parse(inputStream);
|
||||
} finally {
|
||||
inputStream.close();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw newThrowable(RuntimeException.class, "This sohuld never happen");
|
||||
}
|
||||
|
||||
OutputStream outputStream = Optional.ofNullable(output)
|
||||
.map((Fun<Path, OutputStream>) Files::newOutputStream)
|
||||
.<OutputStream>map(BufferedOutputStream::new)
|
||||
.orElse(System.out);
|
||||
|
||||
switch (outputType) {
|
||||
case JSON:
|
||||
try (Writer writer = new OutputStreamWriter(outputStream)) {
|
||||
new JSONDumper(cfg).dump(result, writer);
|
||||
}
|
||||
break;
|
||||
case JBON:
|
||||
try {
|
||||
new JBONDumper(cfg).dump(result, outputStream);
|
||||
} finally {
|
||||
outputStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
package net.woggioni.wson.cli
|
||||
|
||||
import java.net.URL
|
||||
import java.util.Enumeration
|
||||
import java.util.jar.Attributes
|
||||
import java.util.jar.JarFile
|
||||
import java.util.jar.Manifest
|
||||
import lombok.SneakyThrows
|
||||
import picocli.CommandLine
|
||||
|
||||
|
||||
abstract class AbstractVersionProvider @SneakyThrows protected constructor(specificationTitle: String?) :
|
||||
CommandLine.IVersionProvider {
|
||||
private val version: String
|
||||
private val vcsHash: String
|
||||
|
||||
init {
|
||||
var version: String? = null
|
||||
var vcsHash: String? = null
|
||||
val it: Enumeration<URL> = javaClass.classLoader.getResources(JarFile.MANIFEST_NAME)
|
||||
while (it.hasMoreElements()) {
|
||||
val manifestURL: URL = it.nextElement()
|
||||
val mf = Manifest()
|
||||
manifestURL.openStream().use(mf::read)
|
||||
val mainAttributes: Attributes = mf.mainAttributes
|
||||
if (specificationTitle == mainAttributes.getValue(Attributes.Name.SPECIFICATION_TITLE)) {
|
||||
version = mainAttributes.getValue(Attributes.Name.SPECIFICATION_VERSION)
|
||||
vcsHash = mainAttributes.getValue(Attributes.Name.IMPLEMENTATION_VERSION)
|
||||
}
|
||||
}
|
||||
if (version == null || vcsHash == null) {
|
||||
throw RuntimeException("Version information not found in manifest")
|
||||
}
|
||||
this.version = version
|
||||
this.vcsHash = vcsHash
|
||||
}
|
||||
|
||||
override fun getVersion(): Array<String?> {
|
||||
return if (version.endsWith("-SNAPSHOT")) {
|
||||
arrayOf(version, vcsHash)
|
||||
} else {
|
||||
arrayOf(version)
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,22 +1,19 @@
|
||||
package net.woggioni.wson.cli
|
||||
|
||||
import com.beust.jcommander.IStringConverter
|
||||
import com.beust.jcommander.JCommander
|
||||
import com.beust.jcommander.Parameter
|
||||
import com.beust.jcommander.ParameterException
|
||||
import com.beust.jcommander.converters.PathConverter
|
||||
import kotlin.system.exitProcess
|
||||
import net.woggioni.wson.serialization.binary.JBONDumper
|
||||
import net.woggioni.wson.serialization.binary.JBONParser
|
||||
import net.woggioni.wson.serialization.json.JSONDumper
|
||||
import net.woggioni.wson.serialization.json.JSONParser
|
||||
import net.woggioni.wson.xface.Value
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.InputStreamReader
|
||||
import java.io.OutputStreamWriter
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import kotlin.system.exitProcess
|
||||
import net.woggioni.wson.serialization.binary.JBONDumper
|
||||
import net.woggioni.wson.serialization.binary.JBONParser
|
||||
import net.woggioni.wson.serialization.json.JSONDumper
|
||||
import net.woggioni.wson.serialization.json.JSONParser
|
||||
import net.woggioni.wson.xface.Value
|
||||
import org.slf4j.LoggerFactory
|
||||
import picocli.CommandLine
|
||||
|
||||
|
||||
sealed class SerializationFormat(val name: String) {
|
||||
@@ -41,88 +38,99 @@ sealed class SerializationFormat(val name: String) {
|
||||
}
|
||||
}
|
||||
|
||||
private class OutputTypeConverter : IStringConverter<SerializationFormat> {
|
||||
class OutputTypeConverter : CommandLine.ITypeConverter<SerializationFormat> {
|
||||
override fun convert(value: String): SerializationFormat = SerializationFormat.parse(value)
|
||||
}
|
||||
|
||||
private class CliArg {
|
||||
|
||||
@Parameter(names = ["-f", "--file"], description = "Name of the input file to parse", converter = PathConverter::class)
|
||||
class VersionProvider internal constructor() : AbstractVersionProvider("wson-cli")
|
||||
|
||||
@CommandLine.Command(
|
||||
name = "wson-cli",
|
||||
versionProvider = VersionProvider::class)
|
||||
private class WsonCli : Runnable {
|
||||
|
||||
@CommandLine.Option(
|
||||
names = ["-f", "--file"],
|
||||
description = ["Name of the input file to parse"],
|
||||
)
|
||||
var fileName: Path? = null
|
||||
|
||||
@Parameter(names = ["--input-type"], description = "Input type", converter = OutputTypeConverter::class)
|
||||
@CommandLine.Option(
|
||||
names = ["--input-type"],
|
||||
description = ["Input type"],
|
||||
converter = [OutputTypeConverter::class])
|
||||
var inputType: SerializationFormat = SerializationFormat.JSON
|
||||
|
||||
@Parameter(names = ["-o", "--output"], description = "Name of the JSON file to generate", converter = PathConverter::class)
|
||||
@CommandLine.Option(names = ["-o", "--output"],
|
||||
description = ["Name of the JSON file to generate"])
|
||||
var output: Path? = null
|
||||
|
||||
@Parameter(names = ["-t", "--type"], description = "Output type", converter = OutputTypeConverter::class)
|
||||
@CommandLine.Option(
|
||||
names = ["-t", "--type"],
|
||||
description = ["Output type"],
|
||||
converter = [OutputTypeConverter::class])
|
||||
var outputType: SerializationFormat = SerializationFormat.JSON
|
||||
|
||||
@Parameter(names = ["-h", "--help"], help = true)
|
||||
@CommandLine.Option(names = ["-h", "--help"], usageHelp = true)
|
||||
var help: Boolean = false
|
||||
|
||||
override fun run() {
|
||||
val cfg = Value.Configuration.builder().serializeReferences(true).build()
|
||||
val inputStream = if (fileName != null) {
|
||||
BufferedInputStream(Files.newInputStream(fileName))
|
||||
} else {
|
||||
System.`in`
|
||||
}
|
||||
|
||||
val result = when(inputType) {
|
||||
SerializationFormat.JSON -> {
|
||||
val reader = InputStreamReader(inputStream)
|
||||
try {
|
||||
JSONParser(cfg).parse(reader)
|
||||
} finally {
|
||||
reader.close()
|
||||
}
|
||||
}
|
||||
SerializationFormat.JBON -> {
|
||||
try {
|
||||
JBONParser(cfg).parse(inputStream)
|
||||
} finally {
|
||||
inputStream.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val outputStream = output?.let {
|
||||
BufferedOutputStream(Files.newOutputStream(it))
|
||||
} ?: System.out
|
||||
when(outputType) {
|
||||
SerializationFormat.JSON -> {
|
||||
val writer = OutputStreamWriter(outputStream)
|
||||
try {
|
||||
JSONDumper(cfg).dump(result, writer)
|
||||
} finally {
|
||||
writer.close()
|
||||
}
|
||||
}
|
||||
SerializationFormat.JBON -> {
|
||||
try {
|
||||
JBONDumper(cfg).dump(result, outputStream)
|
||||
} finally {
|
||||
outputStream.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun main(vararg args: String) {
|
||||
val cliArg = CliArg()
|
||||
val cliArgumentParser = JCommander.newBuilder()
|
||||
.addObject(cliArg)
|
||||
.build()
|
||||
try {
|
||||
cliArgumentParser.parse(*args)
|
||||
} catch (pe: ParameterException) {
|
||||
cliArgumentParser.usage()
|
||||
exitProcess(-1)
|
||||
}
|
||||
if (cliArg.help) {
|
||||
cliArgumentParser.usage()
|
||||
exitProcess(0)
|
||||
}
|
||||
val cfg = Value.Configuration.builder().serializeReferences(true).build()
|
||||
val inputStream = if (cliArg.fileName != null) {
|
||||
BufferedInputStream(Files.newInputStream(cliArg.fileName))
|
||||
} else {
|
||||
System.`in`
|
||||
}
|
||||
|
||||
val result = when(cliArg.inputType) {
|
||||
SerializationFormat.JSON -> {
|
||||
val reader = InputStreamReader(inputStream)
|
||||
try {
|
||||
JSONParser(cfg).parse(reader)
|
||||
} finally {
|
||||
reader.close()
|
||||
}
|
||||
}
|
||||
SerializationFormat.JBON -> {
|
||||
try {
|
||||
JBONParser(cfg).parse(inputStream)
|
||||
} finally {
|
||||
inputStream.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val outputStream = if (cliArg.output != null) {
|
||||
BufferedOutputStream(Files.newOutputStream(cliArg.output))
|
||||
} else {
|
||||
System.out
|
||||
}
|
||||
when(cliArg.outputType) {
|
||||
SerializationFormat.JSON -> {
|
||||
val writer = OutputStreamWriter(outputStream)
|
||||
try {
|
||||
JSONDumper(cfg).dump(result, writer)
|
||||
} finally {
|
||||
writer.close()
|
||||
}
|
||||
}
|
||||
SerializationFormat.JBON -> {
|
||||
try {
|
||||
JBONDumper(cfg).dump(result, outputStream)
|
||||
} finally {
|
||||
outputStream.close()
|
||||
}
|
||||
}
|
||||
val log = LoggerFactory.getLogger("wson-cli")
|
||||
val commandLine = CommandLine(WsonCli())
|
||||
commandLine.setExecutionExceptionHandler { ex, cl, parseResult ->
|
||||
log.error(ex.message, ex)
|
||||
CommandLine.ExitCode.SOFTWARE
|
||||
}
|
||||
exitProcess(commandLine.execute(*args))
|
||||
}
|
9
wson-w3c-json/build.gradle
Normal file
9
wson-w3c-json/build.gradle
Normal file
@@ -0,0 +1,9 @@
|
||||
plugins {
|
||||
id 'java-library'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation rootProject
|
||||
implementation catalog.jakarta.json.api
|
||||
implementation catalog.jwo
|
||||
}
|
@@ -0,0 +1,91 @@
|
||||
package net.woggioni.wson.jakarta;
|
||||
|
||||
import jakarta.json.JsonNumber;
|
||||
import net.woggioni.wson.value.FloatValue;
|
||||
import net.woggioni.wson.value.IntegerValue;
|
||||
import net.woggioni.wson.xface.Value;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
|
||||
|
||||
public class WorthJsonNumber implements JsonNumber {
|
||||
private final Value value;
|
||||
|
||||
public WorthJsonNumber(IntegerValue value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public WorthJsonNumber(FloatValue value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIntegral() {
|
||||
return value.type() == Value.Type.INTEGER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int intValue() {
|
||||
return (int) value.asInteger();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int intValueExact() {
|
||||
if(isIntegral()) return intValue();
|
||||
else {
|
||||
double res = value.asFloat();
|
||||
double res2 = Math.floor(res);
|
||||
if(res != res2) throw new ArithmeticException();
|
||||
else return (int) res;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long longValue() {
|
||||
return value.asInteger();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long longValueExact() {
|
||||
if(isIntegral()) return intValue();
|
||||
else {
|
||||
double res = value.asFloat();
|
||||
double res2 = Math.floor(res);
|
||||
if(res != res2) throw new ArithmeticException();
|
||||
else return (long) res;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger bigIntegerValue() {
|
||||
return bigDecimalValue().toBigInteger();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger bigIntegerValueExact() {
|
||||
if(isIntegral()) return bigDecimalValue().toBigInteger();
|
||||
else {
|
||||
double res = value.asFloat();
|
||||
double res2 = Math.floor(res);
|
||||
if(res != res2) throw new ArithmeticException();
|
||||
else return BigInteger.valueOf((long) res);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double doubleValue() {
|
||||
return value.asFloat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal bigDecimalValue() {
|
||||
if(isIntegral()) return BigDecimal.valueOf(value.asInteger());
|
||||
else return BigDecimal.valueOf(value.asFloat());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueType getValueType() {
|
||||
return ValueType.NUMBER;
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
package net.woggioni.wson.jakarta;
|
||||
|
||||
import jakarta.json.JsonString;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.woggioni.wson.value.StringValue;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class WorthJsonString implements JsonString {
|
||||
private final StringValue value;
|
||||
|
||||
@Override
|
||||
public String getString() {
|
||||
return value.asString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getChars() {
|
||||
return value.asString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueType getValueType() {
|
||||
return ValueType.STRING;
|
||||
}
|
||||
}
|
@@ -0,0 +1,75 @@
|
||||
package net.woggioni.wson.jakarta;
|
||||
|
||||
import jakarta.json.JsonArray;
|
||||
import jakarta.json.JsonArrayBuilder;
|
||||
import jakarta.json.JsonObjectBuilder;
|
||||
import jakarta.json.JsonValue;
|
||||
import net.woggioni.wson.value.ArrayValue;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
|
||||
public class WsonArrayBuilder implements JsonArrayBuilder {
|
||||
|
||||
private final ArrayValue arrayValue = new ArrayValue();
|
||||
|
||||
@Override
|
||||
public JsonArrayBuilder add(JsonValue value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonArrayBuilder add(String value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonArrayBuilder add(BigDecimal value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonArrayBuilder add(BigInteger value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonArrayBuilder add(int value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonArrayBuilder add(long value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonArrayBuilder add(double value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonArrayBuilder add(boolean value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonArrayBuilder addNull() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonArrayBuilder add(JsonObjectBuilder builder) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonArrayBuilder add(JsonArrayBuilder builder) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonArray build() {
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
package net.woggioni.wson.jakarta;
|
||||
|
||||
import jakarta.json.stream.JsonLocation;
|
||||
import jakarta.json.stream.JsonParser;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
class WsonJsonParser implements JsonParser {
|
||||
private final Reader reader;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Event next() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIntegralNumber() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getBigDecimal() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLocation getLocation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,98 @@
|
||||
package net.woggioni.wson.jakarta;
|
||||
|
||||
import jakarta.json.JsonArrayBuilder;
|
||||
import jakarta.json.JsonBuilderFactory;
|
||||
import jakarta.json.JsonObjectBuilder;
|
||||
import jakarta.json.JsonReader;
|
||||
import jakarta.json.JsonReaderFactory;
|
||||
import jakarta.json.JsonWriter;
|
||||
import jakarta.json.JsonWriterFactory;
|
||||
import jakarta.json.spi.JsonProvider;
|
||||
import jakarta.json.stream.JsonGenerator;
|
||||
import jakarta.json.stream.JsonGeneratorFactory;
|
||||
import jakarta.json.stream.JsonParser;
|
||||
import jakarta.json.stream.JsonParserFactory;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.util.Map;
|
||||
|
||||
public class WsonJsonProvider extends JsonProvider {
|
||||
@Override
|
||||
public JsonParser createParser(Reader reader) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonParser createParser(InputStream in) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonParserFactory createParserFactory(Map<String, ?> config) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonGenerator createGenerator(Writer writer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonGenerator createGenerator(OutputStream out) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonGeneratorFactory createGeneratorFactory(Map<String, ?> config) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonReader createReader(Reader reader) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonReader createReader(InputStream in) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonWriter createWriter(Writer writer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonWriter createWriter(OutputStream out) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonWriterFactory createWriterFactory(Map<String, ?> config) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonReaderFactory createReaderFactory(Map<String, ?> config) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObjectBuilder createObjectBuilder() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonArrayBuilder createArrayBuilder() {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonBuilderFactory createBuilderFactory(Map<String, ?> config) {
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -0,0 +1,361 @@
|
||||
package net.woggioni.wson.jakarta;
|
||||
|
||||
import jakarta.json.JsonValue;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.woggioni.wson.xface.Value;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class WsonJsonValue implements JsonValue {
|
||||
private final Value value;
|
||||
|
||||
@Override
|
||||
public ValueType getValueType() {
|
||||
ValueType result;
|
||||
switch (value.type()) {
|
||||
case NULL:
|
||||
result = ValueType.NULL;
|
||||
break;
|
||||
case OBJECT:
|
||||
result = ValueType.OBJECT;
|
||||
break;
|
||||
case ARRAY:
|
||||
result = ValueType.ARRAY;
|
||||
break;
|
||||
case STRING:
|
||||
result = ValueType.STRING;
|
||||
break;
|
||||
case BOOLEAN:
|
||||
result = value.asBoolean() ? ValueType.TRUE : ValueType.FALSE;
|
||||
break;
|
||||
case DOUBLE:
|
||||
case INTEGER:
|
||||
result = ValueType.NUMBER;
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException("This should never happen");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
//
|
||||
// @Override
|
||||
// public JsonObject getJsonObject(int index) {
|
||||
// return new WsonJsonValue(value.get(index));
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public JsonArray getJsonArray(int index) {
|
||||
// return new WsonJsonValue(value.get(index));
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public JsonNumber getJsonNumber(int index) {
|
||||
// return new WsonJsonValue(value.get(index));
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public JsonString getJsonString(int index) {
|
||||
// return new WsonJsonValue(value.get(index));
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public <T extends JsonValue> List<T> getValuesAs(Class<T> clazz) {
|
||||
// return (List<T>) JWO.iterable2Stream(value.asArray()).map(WsonJsonValue::new)
|
||||
// .collect(CollectionUtils.toUnmodifiableList());
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public String getString(int index) {
|
||||
// return value.get(index).asString();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public String getString(int index, String defaultValue) {
|
||||
// if(index < value.size()) return value.get(index).asString();
|
||||
// else return defaultValue;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public int getInt(int index) {
|
||||
// return (int) value.get(index).asInteger();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public int getInt(int index, int defaultValue) {
|
||||
// if(index < value.size()) return (int) value.get(index).asInteger();
|
||||
// else return defaultValue;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean getBoolean(int index) {
|
||||
// return value.get(index).asBoolean();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean getBoolean(int index, boolean defaultValue) {
|
||||
// if(index < value.size()) return value.get(index).asBoolean();
|
||||
// else return defaultValue;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean isNull(int index) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public JsonArray getJsonArray(String name) {
|
||||
// return new WsonJsonValue(value.get(name));
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public JsonObject getJsonObject(String name) {
|
||||
// return new WsonJsonValue(value.get(name));
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public JsonNumber getJsonNumber(String name) {
|
||||
// return new WsonJsonValue(value.get(name));
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public JsonString getJsonString(String name) {
|
||||
// return new WsonJsonValue(value.get(name));
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public String getString(String name) {
|
||||
// return value.get(name).asString();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public String getString(String name, String defaultValue) {
|
||||
// Value result = value.getOrDefault(name, Value.Null);
|
||||
// if(result.isNull()) return defaultValue;
|
||||
// else return result.asString();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public int getInt(String name) {
|
||||
// return (int) value.get(name).asInteger();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public int getInt(String name, int defaultValue) {
|
||||
// Value result = value.getOrDefault(name, Value.Null);
|
||||
// if(result.isNull()) return defaultValue;
|
||||
// else return (int) result.asInteger();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean getBoolean(String name) {
|
||||
// return value.get(name).asBoolean();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean getBoolean(String name, boolean defaultValue) {
|
||||
// Value result = value.getOrDefault(name, Value.Null);
|
||||
// if(result.isNull()) return defaultValue;
|
||||
// else return result.asBoolean();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean isNull(String name) {
|
||||
// return value.isNull();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public int size() {
|
||||
// return value.size();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean isEmpty() {
|
||||
// return value.size() == 0;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean contains(Object o) {
|
||||
// boolean result = false;
|
||||
// switch (value.type()) {
|
||||
// case ARRAY:
|
||||
// for(Value v : value.asArray()) {
|
||||
// if(v == o) {
|
||||
// result = true;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// break;
|
||||
// case OBJECT:
|
||||
// for(Map.Entry<String, Value> entry : value.asObject()) {
|
||||
// if(entry.getValue() == o) {
|
||||
// result = true;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// break;
|
||||
// default:
|
||||
// throw newThrowable(ClassCastException.class, "This value is not of type %s", ValueType.ARRAY);
|
||||
// }
|
||||
// return result;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Iterator<JsonValue> iterator() {
|
||||
// return new Iterator<>() {
|
||||
// private final Iterator<Value> it = value.asArray().iterator();
|
||||
// @Override
|
||||
// public boolean hasNext() {
|
||||
// return it.hasNext();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public JsonValue next() {
|
||||
// return new WsonJsonValue(it.next());
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Object[] toArray() {
|
||||
// return new Object[0];
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public <T> T[] toArray(T[] a) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean add(JsonValue jsonValue) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean remove(Object o) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean containsAll(Collection<?> c) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean addAll(Collection<? extends JsonValue> c) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean addAll(int index, Collection<? extends JsonValue> c) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean removeAll(Collection<?> c) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean retainAll(Collection<?> c) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void clear() {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public JsonValue get(int index) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public JsonValue set(int index, JsonValue element) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void add(int index, JsonValue element) {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public JsonValue remove(int index) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public int indexOf(Object o) {
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public int lastIndexOf(Object o) {
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public ListIterator<JsonValue> listIterator() {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public ListIterator<JsonValue> listIterator(int index) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public List<JsonValue> subList(int fromIndex, int toIndex) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean containsKey(Object key) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean containsValue(Object value) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public JsonValue get(Object key) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public JsonValue put(String key, JsonValue value) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void putAll(Map<? extends String, ? extends JsonValue> m) {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Set<String> keySet() {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Collection<JsonValue> values() {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Set<Entry<String, JsonValue>> entrySet() {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public JsonObject asJsonObject() {
|
||||
// return this;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public JsonArray asJsonArray() {
|
||||
// return this;
|
||||
// }
|
||||
}
|
Reference in New Issue
Block a user