feat: introduce muscript-gson to support json parsing/writing from muScript and of muScript data from java
ci/woodpecker/push/woodpecker Pipeline was successful
Details
ci/woodpecker/push/woodpecker Pipeline was successful
Details
This commit is contained in:
parent
0ca5b4f323
commit
e927d1f82c
|
@ -1,11 +1,8 @@
|
|||
package io.gitlab.jfronny.commons.serialize.gson.api.v1;
|
||||
|
||||
import io.gitlab.jfronny.commons.ComparableVersion;
|
||||
import io.gitlab.jfronny.commons.serialize.gson.impl.ComparableVersionAdapter;
|
||||
import io.gitlab.jfronny.commons.serialize.gson.impl.GsonIgnoreExclusionStrategy;
|
||||
import io.gitlab.jfronny.commons.serialize.gson.api.v2.GsonTransformer;
|
||||
import io.gitlab.jfronny.gson.*;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
|
@ -13,21 +10,14 @@ import java.util.function.Consumer;
|
|||
* Holds a common instance of the Gson object.
|
||||
* Supports registering type adapters/etc. as needed
|
||||
*/
|
||||
@Deprecated
|
||||
public class GsonHolder {
|
||||
private final GsonBuilder builder = new GsonBuilder()
|
||||
.registerTypeAdapter(ComparableVersion.class, new ComparableVersionAdapter())
|
||||
.excludeFieldsWithModifiers(Modifier.TRANSIENT, Modifier.PRIVATE)
|
||||
.setExclusionStrategies(new GsonIgnoreExclusionStrategy())
|
||||
.setLenient();
|
||||
|
||||
private boolean clean = false;
|
||||
private Gson gson;
|
||||
|
||||
private final io.gitlab.jfronny.commons.serialize.gson.api.v2.GsonHolder impl;
|
||||
public GsonHolder() {
|
||||
synchronized (GsonHolders.KNOWN_INSTANCES) {
|
||||
GsonHolders.KNOWN_INSTANCES.add(this);
|
||||
for (Consumer<GsonBuilder> modification : GsonHolders.KNOWN_MODIFICATIONS) modification.accept(builder);
|
||||
}
|
||||
this(new io.gitlab.jfronny.commons.serialize.gson.api.v2.GsonHolder());
|
||||
}
|
||||
public GsonHolder(io.gitlab.jfronny.commons.serialize.gson.api.v2.GsonHolder impl) {
|
||||
this.impl = impl;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -36,11 +26,7 @@ public class GsonHolder {
|
|||
* @return The Gson instance
|
||||
*/
|
||||
public Gson getGson() {
|
||||
if (!clean) {
|
||||
gson = builder.create();
|
||||
clean = true;
|
||||
}
|
||||
return gson;
|
||||
return impl.getGson();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,8 +36,7 @@ public class GsonHolder {
|
|||
* @param typeAdapter The adapter type
|
||||
*/
|
||||
public GsonHolder registerTypeAdapter(Type type, Object typeAdapter) {
|
||||
builder.registerTypeAdapter(type, typeAdapter);
|
||||
clean = false;
|
||||
impl.registerTypeAdapter(type, typeAdapter);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -61,8 +46,7 @@ public class GsonHolder {
|
|||
* @param factory The factory to register
|
||||
*/
|
||||
public GsonHolder registerTypeAdapterFactory(TypeAdapterFactory factory) {
|
||||
builder.registerTypeAdapterFactory(factory);
|
||||
clean = false;
|
||||
impl.registerTypeAdapterFactory(factory);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -72,8 +56,7 @@ public class GsonHolder {
|
|||
* @param func The function to run
|
||||
*/
|
||||
public GsonHolder modifyBuilder(Consumer<GsonBuilder> func) {
|
||||
func.accept(builder);
|
||||
clean = false;
|
||||
impl.apply(GsonTransformer.byConsumer(func));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,47 +1,34 @@
|
|||
package io.gitlab.jfronny.commons.serialize.gson.api.v1;
|
||||
|
||||
import io.gitlab.jfronny.commons.ref.WeakSet;
|
||||
import io.gitlab.jfronny.commons.serialize.Serializer;
|
||||
import io.gitlab.jfronny.commons.serialize.gson.impl.GsonHolderSerializer;
|
||||
import io.gitlab.jfronny.commons.serialize.gson.api.v2.GsonTransformer;
|
||||
import io.gitlab.jfronny.gson.GsonBuilder;
|
||||
import io.gitlab.jfronny.gson.TypeAdapterFactory;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Deprecated
|
||||
public class GsonHolders {
|
||||
@ApiStatus.Internal
|
||||
static final Set<Consumer<GsonBuilder>> KNOWN_MODIFICATIONS = new LinkedHashSet<>();
|
||||
@ApiStatus.Internal
|
||||
static final WeakSet<GsonHolder> KNOWN_INSTANCES = new WeakSet<>();
|
||||
|
||||
public static final GsonHolder API = new GsonHolder();
|
||||
public static final GsonHolder CONFIG = new GsonHolder().modifyBuilder(b -> b
|
||||
.setOmitQuotes()
|
||||
.serializeSpecialFloatingPointValues()
|
||||
.serializeNulls()
|
||||
.setPrettyPrinting()
|
||||
);
|
||||
public static final GsonHolder API = new GsonHolder(io.gitlab.jfronny.commons.serialize.gson.api.v2.GsonHolders.API);
|
||||
public static final GsonHolder CONFIG = new GsonHolder(io.gitlab.jfronny.commons.serialize.gson.api.v2.GsonHolders.CONFIG);
|
||||
|
||||
public static void registerTypeAdapter(Type type, Object typeAdapter) {
|
||||
modifyBuilder(b -> b.registerTypeAdapter(type, typeAdapter));
|
||||
modifyBuilder(GsonTransformer.byTypeAdapter(type, typeAdapter));
|
||||
}
|
||||
|
||||
public static void registerTypeAdapterFactory(TypeAdapterFactory factory) {
|
||||
modifyBuilder(b -> b.registerTypeAdapterFactory(factory));
|
||||
modifyBuilder(GsonTransformer.byTypeAdapterFactory(factory));
|
||||
}
|
||||
|
||||
public static void modifyBuilder(Consumer<GsonBuilder> func) {
|
||||
synchronized (KNOWN_INSTANCES) {
|
||||
KNOWN_MODIFICATIONS.add(func);
|
||||
for (GsonHolder holder : KNOWN_INSTANCES) holder.modifyBuilder(func);
|
||||
}
|
||||
modifyBuilder(GsonTransformer.byConsumer(func));
|
||||
}
|
||||
|
||||
private static void modifyBuilder(GsonTransformer transformer) {
|
||||
io.gitlab.jfronny.commons.serialize.gson.api.v2.GsonHolders.applyTransform(transformer);
|
||||
}
|
||||
|
||||
public static void registerSerializer() {
|
||||
Serializer.setInstance(new GsonHolderSerializer(API));
|
||||
io.gitlab.jfronny.commons.serialize.gson.api.v2.GsonHolders.registerSerializer();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import java.lang.annotation.*;
|
|||
* Mark a class/field to be ignored by Gson.
|
||||
* May be used for metadata in serialized classes
|
||||
*/
|
||||
@Deprecated
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.TYPE})
|
||||
public @interface Ignore {
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
package io.gitlab.jfronny.commons.serialize.gson.api.v2;
|
||||
|
||||
import io.gitlab.jfronny.gson.*;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* Holds a common instance of the Gson object.
|
||||
* Supports registering type adapters/etc. as needed
|
||||
*/
|
||||
public class GsonHolder {
|
||||
private final GsonBuilder builder = new GsonBuilder();
|
||||
|
||||
private boolean clean = false;
|
||||
private Gson gson;
|
||||
|
||||
public GsonHolder() {
|
||||
synchronized (GsonHolders.KNOWN_INSTANCES) {
|
||||
GsonHolders.KNOWN_INSTANCES.add(this);
|
||||
GsonHolders.PAST_MODIFICATIONS.apply(builder);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current gson instance or build it if needed
|
||||
*
|
||||
* @return The Gson instance
|
||||
*/
|
||||
public Gson getGson() {
|
||||
if (!clean) {
|
||||
gson = builder.create();
|
||||
clean = true;
|
||||
}
|
||||
return gson;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a type adapter and mark the gson instance as unclean
|
||||
*
|
||||
* @param type The type for which to register the adapter
|
||||
* @param typeAdapter The adapter type
|
||||
*/
|
||||
public GsonHolder registerTypeAdapter(Type type, Object typeAdapter) {
|
||||
builder.registerTypeAdapter(type, typeAdapter);
|
||||
clean = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a type adapter factory and mark the gson instance as unclean
|
||||
*
|
||||
* @param factory The factory to register
|
||||
*/
|
||||
public GsonHolder registerTypeAdapterFactory(TypeAdapterFactory factory) {
|
||||
builder.registerTypeAdapterFactory(factory);
|
||||
clean = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a function on the builder for modifying it and mark the gson instance as unclean
|
||||
*
|
||||
* @param transformer The transformer to apply
|
||||
*/
|
||||
public GsonHolder apply(GsonTransformer transformer) {
|
||||
transformer.apply(builder);
|
||||
clean = false;
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package io.gitlab.jfronny.commons.serialize.gson.api.v2;
|
||||
|
||||
import io.gitlab.jfronny.commons.ref.WeakSet;
|
||||
import io.gitlab.jfronny.commons.serialize.Serializer;
|
||||
import io.gitlab.jfronny.commons.serialize.gson.impl.GsonHolderSerializer;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class GsonHolders {
|
||||
@ApiStatus.Internal
|
||||
static GsonTransformer PAST_MODIFICATIONS = GsonTransformer.IDENTITY;
|
||||
@ApiStatus.Internal
|
||||
static final WeakSet<GsonHolder> KNOWN_INSTANCES = new WeakSet<>();
|
||||
|
||||
public static final GsonHolder API = new GsonHolder();
|
||||
public static final GsonHolder CONFIG = new GsonHolder().apply(b -> b
|
||||
.setOmitQuotes()
|
||||
.serializeSpecialFloatingPointValues()
|
||||
.serializeNulls()
|
||||
.setPrettyPrinting()
|
||||
);
|
||||
|
||||
public static void applyTransform(GsonTransformer transformer) {
|
||||
synchronized (KNOWN_INSTANCES) {
|
||||
PAST_MODIFICATIONS = GsonTransformer.concat(List.of(PAST_MODIFICATIONS, transformer));
|
||||
for (GsonHolder holder : KNOWN_INSTANCES) holder.apply(transformer);
|
||||
}
|
||||
}
|
||||
|
||||
public static void applyTransforms(List<GsonTransformer> transformers) {
|
||||
synchronized (KNOWN_INSTANCES) {
|
||||
PAST_MODIFICATIONS = GsonTransformer.concat(Stream.concat(
|
||||
Stream.of(PAST_MODIFICATIONS),
|
||||
transformers.stream()
|
||||
).toList());
|
||||
for (GsonHolder holder : KNOWN_INSTANCES)
|
||||
for (GsonTransformer transformer : transformers)
|
||||
holder.apply(transformer);
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
applyTransforms(ServiceLoader.load(GsonTransformer.class)
|
||||
.stream()
|
||||
.map(ServiceLoader.Provider::get)
|
||||
.toList());
|
||||
}
|
||||
|
||||
public static void registerSerializer() {
|
||||
Serializer.setInstance(new GsonHolderSerializer(API));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package io.gitlab.jfronny.commons.serialize.gson.api.v2;
|
||||
|
||||
import io.gitlab.jfronny.commons.ref.R;
|
||||
import io.gitlab.jfronny.commons.serialize.gson.impl.MultipleTransformer;
|
||||
import io.gitlab.jfronny.gson.GsonBuilder;
|
||||
import io.gitlab.jfronny.gson.TypeAdapterFactory;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Represents a transformation for {@code GsonBuilder} objects handled by {@code GsonHolder}.
|
||||
* Transformers may be applied to individual holders, manually to all holders via {@code GsonHolders.applyTransforms}
|
||||
* or automatically through the service loader mechanism.
|
||||
*/
|
||||
public interface GsonTransformer {
|
||||
GsonTransformer IDENTITY = R::nop;
|
||||
|
||||
static GsonTransformer byConsumer(Consumer<GsonBuilder> source) {
|
||||
return source::accept;
|
||||
}
|
||||
|
||||
static GsonTransformer byTypeAdapter(Type type, Object typeAdapter) {
|
||||
return b -> b.registerTypeAdapter(type, typeAdapter);
|
||||
}
|
||||
|
||||
static GsonTransformer byTypeAdapterFactory(TypeAdapterFactory factory) {
|
||||
return b -> b.registerTypeAdapterFactory(factory);
|
||||
}
|
||||
|
||||
static GsonTransformer concat(List<GsonTransformer> transformers) {
|
||||
transformers = transformers.stream()
|
||||
.flatMap(t -> t instanceof MultipleTransformer mt
|
||||
? mt.transformers().stream()
|
||||
: Stream.of(t))
|
||||
.filter(s -> s != IDENTITY)
|
||||
.toList();
|
||||
return switch (transformers.size()) {
|
||||
case 0 -> IDENTITY;
|
||||
case 1 -> transformers.get(0);
|
||||
default -> new MultipleTransformer(transformers);
|
||||
};
|
||||
}
|
||||
|
||||
void apply(GsonBuilder builder);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package io.gitlab.jfronny.commons.serialize.gson.api.v2;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Mark a class/field to be ignored by Gson.
|
||||
* May be used for metadata in serialized classes
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.TYPE})
|
||||
public @interface Ignore {
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package io.gitlab.jfronny.commons.serialize.gson.impl;
|
||||
|
||||
import io.gitlab.jfronny.commons.ComparableVersion;
|
||||
import io.gitlab.jfronny.commons.serialize.gson.api.v2.GsonTransformer;
|
||||
import io.gitlab.jfronny.gson.GsonBuilder;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
public class DefaultTransformer implements GsonTransformer {
|
||||
@Override
|
||||
public void apply(GsonBuilder builder) {
|
||||
builder.registerTypeAdapter(ComparableVersion.class, new ComparableVersionAdapter())
|
||||
.excludeFieldsWithModifiers(Modifier.TRANSIENT, Modifier.PRIVATE)
|
||||
.setExclusionStrategies(new GsonIgnoreExclusionStrategy())
|
||||
.setLenient();
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
package io.gitlab.jfronny.commons.serialize.gson.impl;
|
||||
|
||||
import io.gitlab.jfronny.commons.serialize.Serializer;
|
||||
import io.gitlab.jfronny.commons.serialize.gson.api.v1.GsonHolder;
|
||||
import io.gitlab.jfronny.commons.serialize.gson.api.v2.GsonHolder;
|
||||
import io.gitlab.jfronny.gson.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
|
|
@ -9,12 +9,14 @@ import io.gitlab.jfronny.gson.*;
|
|||
public class GsonIgnoreExclusionStrategy implements ExclusionStrategy {
|
||||
@Override
|
||||
public boolean shouldSkipClass(Class<?> clazz) {
|
||||
return clazz.isAnnotationPresent(Ignore.class);
|
||||
return clazz.isAnnotationPresent(Ignore.class)
|
||||
|| clazz.isAnnotationPresent(io.gitlab.jfronny.commons.serialize.gson.api.v2.Ignore.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldSkipField(FieldAttributes f) {
|
||||
return f.getAnnotation(Ignore.class) != null
|
||||
|| f.getDeclaringClass().isAnnotationPresent(Ignore.class);
|
||||
|| f.getAnnotation(io.gitlab.jfronny.commons.serialize.gson.api.v2.Ignore.class) != null
|
||||
|| shouldSkipClass(f.getDeclaringClass());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
package io.gitlab.jfronny.commons.serialize.gson.impl;
|
||||
|
||||
import io.gitlab.jfronny.commons.serialize.gson.api.v2.GsonTransformer;
|
||||
import io.gitlab.jfronny.gson.GsonBuilder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record MultipleTransformer(List<GsonTransformer> transformers) implements GsonTransformer {
|
||||
@Override
|
||||
public void apply(GsonBuilder builder) {
|
||||
for (GsonTransformer transformer : transformers) {
|
||||
transformer.apply(builder);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
module io.gitlab.jfronny.commons.gson {
|
||||
uses io.gitlab.jfronny.commons.serialize.gson.api.v2.GsonTransformer;
|
||||
requires io.gitlab.jfronny.commons;
|
||||
requires transitive io.gitlab.jfronny.gson;
|
||||
requires static org.jetbrains.annotations;
|
||||
exports io.gitlab.jfronny.commons.serialize.gson.api.v1;
|
||||
exports io.gitlab.jfronny.commons.serialize.gson.api.v2;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
io.gitlab.jfronny.commons.serialize.gson.impl.DefaultTransformer
|
|
@ -0,0 +1,30 @@
|
|||
import io.gitlab.jfronny.scripts.*
|
||||
|
||||
plugins {
|
||||
id("commons.library")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":muscript"))
|
||||
implementation(project(":commons-gson"))
|
||||
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.3")
|
||||
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.3")
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
create<MavenPublication>("maven") {
|
||||
groupId = "io.gitlab.jfronny"
|
||||
artifactId = "muscript-gson"
|
||||
|
||||
from(components["java"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.javadoc {
|
||||
linksOffline("https://maven.frohnmeyer-wds.de/javadoc/artifacts/io/gitlab/jfronny/commons/$version/raw", project(":"))
|
||||
linksOffline("https://maven.frohnmeyer-wds.de/javadoc/artifacts/io/gitlab/jfronny/commons-gson/$version/raw", project(":commons-gson"))
|
||||
linksOffline("https://maven.frohnmeyer-wds.de/javadoc/artifacts/io/gitlab/jfronny/muscript-gson/$version/raw", project(":muscript"))
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package io.gitlab.jfronny.muscript.gson;
|
||||
|
||||
import io.gitlab.jfronny.commons.serialize.gson.api.v2.GsonHolders;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal;
|
||||
|
||||
public class GsonLib {
|
||||
public static Scope addTo(Scope scope) {
|
||||
return scope
|
||||
.set("toJson", GsonLib::toJson)
|
||||
.set("fromJson", GsonLib::fromJson);
|
||||
}
|
||||
|
||||
public static DString toJson(DList args) {
|
||||
if (args.isEmpty() || args.size() > 2) throw new IllegalArgumentException("Invalid number of arguments for toJson: expected 1 or 2 but got " + args.size());
|
||||
Dynamic source = args.get(0);
|
||||
boolean lenient = args.size() > 1 && args.get(1).asBool().getValue();
|
||||
return DFinal.of((lenient ? GsonHolders.CONFIG : GsonHolders.API).getGson().toJson(source));
|
||||
}
|
||||
|
||||
public static Dynamic fromJson(DList args) {
|
||||
if (args.isEmpty() || args.size() > 2) throw new IllegalArgumentException("Invalid number of arguments for fromJson: expected 1 or 2 but got " + args.size());
|
||||
String source = args.get(0).asString().getValue();
|
||||
boolean lenient = args.size() > 1 && args.get(1).asBool().getValue();
|
||||
return (lenient ? GsonHolders.CONFIG : GsonHolders.API).getGson().fromJson(source, Dynamic.class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package io.gitlab.jfronny.muscript.gson.impl;
|
||||
|
||||
import io.gitlab.jfronny.commons.serialize.gson.api.v2.GsonTransformer;
|
||||
import io.gitlab.jfronny.gson.GsonBuilder;
|
||||
|
||||
public class DynamicGsonTransformer implements GsonTransformer {
|
||||
@Override
|
||||
public void apply(GsonBuilder builder) {
|
||||
new DynamicSerializer().registerTo(builder);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package io.gitlab.jfronny.muscript.gson.impl;
|
||||
|
||||
import io.gitlab.jfronny.gson.GsonBuilder;
|
||||
import io.gitlab.jfronny.gson.TypeAdapter;
|
||||
import io.gitlab.jfronny.gson.stream.JsonReader;
|
||||
import io.gitlab.jfronny.gson.stream.JsonWriter;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.*;
|
||||
import io.gitlab.jfronny.muscript.gson.impl.typed.*;
|
||||
import io.gitlab.jfronny.muscript.gson.impl.typed.additional.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class DynamicSerializer extends TypeAdapter<Dynamic> {
|
||||
public final DObjectSerializer dObject = new DObjectSerializer(this);
|
||||
public final DListSerializer dList = new DListSerializer(this);
|
||||
public final DNullSerializer dNull = new DNullSerializer();
|
||||
public final DBoolSerializer dBool = new DBoolSerializer();
|
||||
public final DNumberSerializer dNumber = new DNumberSerializer();
|
||||
public final DStringSerializer dString = new DStringSerializer(this);
|
||||
public final DCallableSerializer dCallable = new DCallableSerializer();
|
||||
public final DynamicBaseSerializer dynamicBase = new DynamicBaseSerializer();
|
||||
public final DDateSerializer dDate = new DDateSerializer();
|
||||
public final DTimeSerializer dTime = new DTimeSerializer();
|
||||
public final DEnumSerializer dEnum = new DEnumSerializer();
|
||||
|
||||
public void registerTo(GsonBuilder builder) {
|
||||
builder.registerTypeAdapter(Dynamic.class, this);
|
||||
builder.registerTypeHierarchyAdapter(DObject.class, dObject);
|
||||
builder.registerTypeHierarchyAdapter(DList.class, dList);
|
||||
builder.registerTypeAdapter(DNull.class, dNull);
|
||||
builder.registerTypeHierarchyAdapter(DBool.class, dBool);
|
||||
builder.registerTypeHierarchyAdapter(DNumber.class, dNumber);
|
||||
builder.registerTypeHierarchyAdapter(DString.class, dString);
|
||||
builder.registerTypeHierarchyAdapter(DCallable.class, dCallable);
|
||||
builder.registerTypeHierarchyAdapter(DynamicBase.class, dynamicBase); // Custom dynamic implementations need custom support
|
||||
builder.registerTypeAdapter(DDate.class, dDate);
|
||||
builder.registerTypeAdapter(DTime.class, dTime);
|
||||
builder.registerTypeAdapter(DEnum.class, dEnum);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dynamic read(JsonReader r) throws IOException {
|
||||
return switch (r.peek()) {
|
||||
case BEGIN_OBJECT -> dObject.read(r);
|
||||
case BEGIN_ARRAY -> dList.read(r);
|
||||
case NULL -> dNull.read(r);
|
||||
case BOOLEAN -> dBool.read(r);
|
||||
case NUMBER -> dNumber.read(r);
|
||||
case STRING -> dString.read(r);
|
||||
default -> throw new IllegalStateException("Unsupported token for beginning of Dynamic: " + r.peek());
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(JsonWriter w, Dynamic dynamic) throws IOException {
|
||||
if (dynamic == null) w.nullValue();
|
||||
else if (dynamic instanceof DDate date) dDate.write(w, date);
|
||||
else if (dynamic instanceof DTime time) dTime.write(w, time);
|
||||
else if (dynamic instanceof DEnum enm) dEnum.write(w, enm);
|
||||
else if (dynamic.isObject()) dObject.write(w, dynamic.asObject());
|
||||
else if (dynamic.isList()) dList.write(w, dynamic.asList());
|
||||
else if (dynamic instanceof DNull n) dNull.write(w, n);
|
||||
else if (dynamic.isBool()) dBool.write(w, dynamic.asBool());
|
||||
else if (dynamic.isNumber()) dNumber.write(w, dynamic.asNumber());
|
||||
else if (dynamic.isString()) dString.write(w, dynamic.asString());
|
||||
else if (dynamic.isCallable()) dCallable.write(w, dynamic.asCallable());
|
||||
else throw new IllegalArgumentException("Unexpected dynamic type for: " + dynamic);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package io.gitlab.jfronny.muscript.gson.impl.typed;
|
||||
|
||||
import io.gitlab.jfronny.gson.TypeAdapter;
|
||||
import io.gitlab.jfronny.gson.stream.JsonReader;
|
||||
import io.gitlab.jfronny.gson.stream.JsonWriter;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.DBool;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class DBoolSerializer extends TypeAdapter<DBool> {
|
||||
@Override
|
||||
public void write(JsonWriter w, DBool dynamic) throws IOException {
|
||||
w.value(dynamic.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBool read(JsonReader r) throws IOException {
|
||||
return DFinal.of(r.nextBoolean());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package io.gitlab.jfronny.muscript.gson.impl.typed;
|
||||
|
||||
import io.gitlab.jfronny.gson.TypeAdapter;
|
||||
import io.gitlab.jfronny.gson.stream.JsonReader;
|
||||
import io.gitlab.jfronny.gson.stream.JsonWriter;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.DCallable;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class DCallableSerializer extends TypeAdapter<DCallable> {
|
||||
@Override
|
||||
public void write(JsonWriter w, DCallable dynamic) throws IOException {
|
||||
if (dynamic.getName().equals(DCallable.DEFAULT_NAME)) throw new IllegalArgumentException("Unnamed callables cannot be serialized to json");
|
||||
else w.value(dynamic.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DCallable read(JsonReader r) throws IOException {
|
||||
throw new UnsupportedOperationException("DCallables cannot be deserialized from json");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package io.gitlab.jfronny.muscript.gson.impl.typed;
|
||||
|
||||
import io.gitlab.jfronny.gson.TypeAdapter;
|
||||
import io.gitlab.jfronny.gson.stream.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.DList;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal;
|
||||
import io.gitlab.jfronny.muscript.gson.impl.DynamicSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class DListSerializer extends TypeAdapter<DList> {
|
||||
private final DynamicSerializer base;
|
||||
|
||||
public DListSerializer(DynamicSerializer base) {
|
||||
this.base = base;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(JsonWriter w, DList dynamic) throws IOException {
|
||||
w.beginArray();
|
||||
for (Dynamic value : dynamic.getValue()) {
|
||||
base.write(w, value);
|
||||
}
|
||||
w.endArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DList read(JsonReader r) throws IOException {
|
||||
List<Dynamic> elements = new LinkedList<>();
|
||||
r.beginArray();
|
||||
while (r.peek() != JsonToken.END_ARRAY) {
|
||||
elements.add(base.read(r));
|
||||
}
|
||||
r.endArray();
|
||||
return DFinal.of(elements);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package io.gitlab.jfronny.muscript.gson.impl.typed;
|
||||
|
||||
import io.gitlab.jfronny.gson.TypeAdapter;
|
||||
import io.gitlab.jfronny.gson.stream.JsonReader;
|
||||
import io.gitlab.jfronny.gson.stream.JsonWriter;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.DNull;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class DNullSerializer extends TypeAdapter<DNull> {
|
||||
@Override
|
||||
public void write(JsonWriter w, DNull dynamic) throws IOException {
|
||||
w.nullValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DNull read(JsonReader r) throws IOException {
|
||||
r.nextNull();
|
||||
return new DNull();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package io.gitlab.jfronny.muscript.gson.impl.typed;
|
||||
|
||||
import io.gitlab.jfronny.gson.TypeAdapter;
|
||||
import io.gitlab.jfronny.gson.stream.JsonReader;
|
||||
import io.gitlab.jfronny.gson.stream.JsonWriter;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.DNumber;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class DNumberSerializer extends TypeAdapter<DNumber> {
|
||||
@Override
|
||||
public void write(JsonWriter w, DNumber dynamic) throws IOException {
|
||||
double d = dynamic.getValue();
|
||||
if (d % 1.0 != 0 || d > Long.MAX_VALUE) w.value(d);
|
||||
else w.value((long) d);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DNumber read(JsonReader r) throws IOException {
|
||||
return DFinal.of(r.nextDouble());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package io.gitlab.jfronny.muscript.gson.impl.typed;
|
||||
|
||||
import io.gitlab.jfronny.gson.TypeAdapter;
|
||||
import io.gitlab.jfronny.gson.stream.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.DObject;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal;
|
||||
import io.gitlab.jfronny.muscript.gson.impl.DynamicSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class DObjectSerializer extends TypeAdapter<DObject> {
|
||||
private final DynamicSerializer base;
|
||||
|
||||
public DObjectSerializer(DynamicSerializer base) {
|
||||
this.base = base;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(JsonWriter w, DObject dynamic) throws IOException {
|
||||
w.beginObject();
|
||||
for (Map.Entry<String, ? extends Dynamic> entry : dynamic.getValue().entrySet()) {
|
||||
w.name(entry.getKey());
|
||||
base.write(w, entry.getValue());
|
||||
}
|
||||
w.endObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DObject read(JsonReader r) throws IOException {
|
||||
Map<String, Dynamic> elements = new LinkedHashMap<>();
|
||||
r.beginObject();
|
||||
while (r.peek() != JsonToken.END_OBJECT) {
|
||||
elements.put(r.nextName(), base.read(r));
|
||||
}
|
||||
r.endObject();
|
||||
return DFinal.of(elements);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package io.gitlab.jfronny.muscript.gson.impl.typed;
|
||||
|
||||
import io.gitlab.jfronny.gson.TypeAdapter;
|
||||
import io.gitlab.jfronny.gson.stream.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.DString;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal;
|
||||
import io.gitlab.jfronny.muscript.gson.impl.DynamicSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class DStringSerializer extends TypeAdapter<DString> {
|
||||
private final DynamicSerializer base;
|
||||
|
||||
public DStringSerializer(DynamicSerializer base) {
|
||||
this.base = base;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(JsonWriter w, DString dynamic) throws IOException {
|
||||
w.value(dynamic.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DString read(JsonReader r) throws IOException {
|
||||
if (r.peek() == JsonToken.STRING) return DFinal.of(r.nextString());
|
||||
else return base.read(r).asString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package io.gitlab.jfronny.muscript.gson.impl.typed;
|
||||
|
||||
import io.gitlab.jfronny.gson.TypeAdapter;
|
||||
import io.gitlab.jfronny.gson.stream.JsonReader;
|
||||
import io.gitlab.jfronny.gson.stream.JsonWriter;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.DynamicBase;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class DynamicBaseSerializer extends TypeAdapter<DynamicBase> {
|
||||
@Override
|
||||
public void write(JsonWriter w, DynamicBase dynamic) throws IOException {
|
||||
throw new UnsupportedOperationException("Tried to write unsupported custom dynamic: " + dynamic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicBase read(JsonReader r) throws IOException {
|
||||
throw new UnsupportedOperationException("Tried to read unsupported custom dynamic");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package io.gitlab.jfronny.muscript.gson.impl.typed.additional;
|
||||
|
||||
import io.gitlab.jfronny.gson.TypeAdapter;
|
||||
import io.gitlab.jfronny.gson.stream.JsonReader;
|
||||
import io.gitlab.jfronny.gson.stream.JsonWriter;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.DDate;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
|
||||
public class DDateSerializer extends TypeAdapter<DDate> {
|
||||
@Override
|
||||
public void write(JsonWriter w, DDate dynamic) throws IOException {
|
||||
w.value(dynamic.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DDate read(JsonReader r) throws IOException {
|
||||
String s = r.nextString();
|
||||
return new DDate(() -> LocalDate.parse(s));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package io.gitlab.jfronny.muscript.gson.impl.typed.additional;
|
||||
|
||||
import io.gitlab.jfronny.gson.TypeAdapter;
|
||||
import io.gitlab.jfronny.gson.stream.JsonReader;
|
||||
import io.gitlab.jfronny.gson.stream.JsonWriter;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.DEnum;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class DEnumSerializer extends TypeAdapter<DEnum> {
|
||||
@Override
|
||||
public void write(JsonWriter w, DEnum dynamic) throws IOException {
|
||||
if (dynamic.value() == null) {
|
||||
w.beginArray();
|
||||
for (String s : dynamic.values().keySet()) {
|
||||
w.value(s);
|
||||
}
|
||||
w.endArray();
|
||||
} else w.value(dynamic.value().value());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DEnum read(JsonReader r) throws IOException {
|
||||
throw new UnsupportedOperationException("Deserializing DEnum is unsupported");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package io.gitlab.jfronny.muscript.gson.impl.typed.additional;
|
||||
|
||||
import io.gitlab.jfronny.gson.TypeAdapter;
|
||||
import io.gitlab.jfronny.gson.stream.JsonReader;
|
||||
import io.gitlab.jfronny.gson.stream.JsonWriter;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.DTime;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalTime;
|
||||
|
||||
public class DTimeSerializer extends TypeAdapter<DTime> {
|
||||
@Override
|
||||
public void write(JsonWriter w, DTime dynamic) throws IOException {
|
||||
w.value(dynamic.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DTime read(JsonReader r) throws IOException {
|
||||
String s = r.nextString();
|
||||
return new DTime(() -> LocalTime.parse(s));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
module io.gitlab.jfronny.commons.muscript.gson {
|
||||
requires io.gitlab.jfronny.gson;
|
||||
requires io.gitlab.jfronny.commons.muscript;
|
||||
requires io.gitlab.jfronny.commons.gson;
|
||||
exports io.gitlab.jfronny.muscript.gson;
|
||||
// provides io.gitlab.jfronny.commons.serialize.gson.api.v2.GsonTransformer with io.gitlab.jfronny.muscript.gson.DynamicGsonTransformer;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
io.gitlab.jfronny.muscript.gson.impl.DynamicGsonTransformer
|
|
@ -0,0 +1,58 @@
|
|||
package io.gitlab.jfronny.muscript.gson.test;
|
||||
|
||||
import io.gitlab.jfronny.commons.serialize.gson.api.v2.GsonHolders;
|
||||
import io.gitlab.jfronny.muscript.StandardLib;
|
||||
import io.gitlab.jfronny.muscript.compiler.Parser;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal;
|
||||
import io.gitlab.jfronny.muscript.gson.GsonLib;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
import static io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal.of;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class JsonTest {
|
||||
@Test
|
||||
void simpleSerialize() {
|
||||
assertEquals("\"Yes\"", serialize(of("Yes")));
|
||||
assertEquals("{\"key\":3}", serialize(of(Map.of("key", of(3)))));
|
||||
assertEquals("[true,12,\"7\"]", serialize(of(of(true), of(12), of(7).asString())));
|
||||
}
|
||||
|
||||
@Test
|
||||
void simpleDeserialize() {
|
||||
assertEquals(of("Yes"), deserialize("\"Yes\"", Dynamic.class));
|
||||
assertEquals(of(Map.of("key", of(3))), deserialize("{\"key\":3}", Dynamic.class));
|
||||
assertEquals(of(of(true), of(12), of(7).asString()), deserialize("[true,12,\"7\"]", Dynamic.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void muscriptDSL() {
|
||||
assertEquals("{\"key\":\"One\",\"key2\":3}", execute("{key = 'One', key2 = 3}::toJson()").asString().getValue());
|
||||
assertEquals(of(Map.of("key", of(3), "value", of(false))), execute("args[0]::fromJson()", "{\"key\": 3, \"value\": false}"));
|
||||
}
|
||||
|
||||
private Scope createScope() {
|
||||
return GsonLib.addTo(StandardLib.createScope());
|
||||
}
|
||||
|
||||
private Dynamic execute(String source, String... args) {
|
||||
return Parser.parse(source)
|
||||
.asDynamicExpr()
|
||||
.get(createScope()
|
||||
.set("args", DFinal.of(Arrays.stream(args).map(DFinal::of).toList()))
|
||||
);
|
||||
}
|
||||
|
||||
private String serialize(Object object) {
|
||||
return GsonHolders.API.getGson().toJson(object);
|
||||
}
|
||||
|
||||
private <T> T deserialize(String source, Class<T> klazz) {
|
||||
return GsonHolders.API.getGson().fromJson(source, klazz);
|
||||
}
|
||||
}
|
|
@ -113,7 +113,7 @@ public class StandardLib {
|
|||
}
|
||||
|
||||
public static DNumber random(DList args) {
|
||||
if (args.size() == 0) return of(rnd.nextDouble());
|
||||
if (args.isEmpty()) return of(rnd.nextDouble());
|
||||
else if (args.size() == 2) {
|
||||
double min = args.get(0).asNumber().getValue();
|
||||
double max = args.get(1).asNumber().getValue();
|
||||
|
@ -257,7 +257,7 @@ public class StandardLib {
|
|||
}
|
||||
|
||||
public static DObject try_(DList args) {
|
||||
if (args.size() == 0) throw new IllegalArgumentException("Invalid number of arguments for try: expected 1 or more but got " + args.size());
|
||||
if (args.isEmpty()) throw new IllegalArgumentException("Invalid number of arguments for try: expected 1 or more but got " + args.size());
|
||||
var callable = args.get(0).asCallable();
|
||||
var l = args.getValue();
|
||||
var innerArgs = of(l.subList(1, l.size()));
|
||||
|
|
|
@ -28,7 +28,7 @@ public class StringCoerce extends StringExpr {
|
|||
if (inner instanceof NullLiteral) return literal(location, "null");
|
||||
if (inner instanceof BoolLiteral expr) return literal(location, StringFormatter.toString(expr.value));
|
||||
if (inner instanceof DynamicLiteral<?> expr) return literal(location, StringFormatter.toString(expr.value));
|
||||
if (inner instanceof NumberLiteral expr) return literal(location, StringFormatter.toString(expr.value));
|
||||
if (inner instanceof NumberLiteral expr) return literal(location, StringFormatter.toStringPrecise(expr.value));
|
||||
if (inner instanceof StringExpr expr) return expr;
|
||||
return new StringCoerce(location, inner);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ import io.gitlab.jfronny.muscript.data.dynamic.additional.NamedDCallable;
|
|||
import java.util.function.Function;
|
||||
|
||||
public non-sealed interface DCallable extends Dynamic {
|
||||
String DEFAULT_NAME = "<unnamed>";
|
||||
|
||||
default Dynamic call(DList args) {
|
||||
return getValue().apply(args);
|
||||
}
|
||||
|
@ -15,7 +17,7 @@ public non-sealed interface DCallable extends Dynamic {
|
|||
}
|
||||
|
||||
default String getName() {
|
||||
return "<unnamed>";
|
||||
return DEFAULT_NAME;
|
||||
}
|
||||
|
||||
default DCallable named(String name) {
|
||||
|
|
|
@ -3,6 +3,7 @@ package io.gitlab.jfronny.muscript.data.dynamic;
|
|||
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.ObjectLiteral;
|
||||
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
|
@ -24,4 +25,13 @@ public non-sealed interface DObject extends Dynamic {
|
|||
|
||||
@Override
|
||||
Map<String, ? extends Dynamic> getValue();
|
||||
|
||||
@Override
|
||||
default DString asString() {
|
||||
return DFinal.of(getValue()
|
||||
.entrySet()
|
||||
.stream()
|
||||
.map(e -> e.getKey() + " = " + e.getValue().asString().getValue())
|
||||
.collect(Collectors.joining(", ", "{", "}")));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,12 +5,10 @@ import io.gitlab.jfronny.muscript.ast.dynamic.Call;
|
|||
import io.gitlab.jfronny.muscript.ast.dynamic.Variable;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
|
||||
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.lens.DNumberLens;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.lens.DStringLens;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.additional;
|
||||
|
||||
import io.gitlab.jfronny.commons.StringFormatter;
|
||||
import io.gitlab.jfronny.commons.data.ImmCollection;
|
||||
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.Call;
|
||||
import io.gitlab.jfronny.muscript.ast.dynamic.Variable;
|
||||
|
@ -83,7 +84,7 @@ public record DEnum(Map<String, ? extends Dynamic> values, @Nullable DEnumEntry
|
|||
for (int i = 0; i < values.size(); i++) {
|
||||
result.put(values.get(i), new DEnumEntry(values.get(i), i, values.get(i).equals(value)));
|
||||
}
|
||||
return Map.copyOf(result);
|
||||
return ImmCollection.of(result);
|
||||
}
|
||||
|
||||
public record DEnumEntry(String value, int index, boolean selected) implements DString {
|
||||
|
|
|
@ -28,7 +28,7 @@ public class DFinal {
|
|||
}
|
||||
|
||||
public static DObject of(Map<String, ? extends Dynamic> b) {
|
||||
return new FObject(Map.copyOf(b));
|
||||
return new FObject(ImmCollection.copyOf((Map<String, Dynamic>) b));
|
||||
}
|
||||
|
||||
public static DList of(Dynamic... b) {
|
||||
|
@ -36,7 +36,7 @@ public class DFinal {
|
|||
}
|
||||
|
||||
public static DList of(List<? extends Dynamic> b) {
|
||||
return new FList(ImmCollection.of((List<Dynamic>) b));
|
||||
return new FList(ImmCollection.copyOf((List<Dynamic>) b));
|
||||
}
|
||||
|
||||
public static DCallable of(Function<DList, ? extends Dynamic> b, Supplier<String> serialized) {
|
||||
|
@ -81,7 +81,7 @@ public class DFinal {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return StringFormatter.toString(value);
|
||||
return StringFormatter.toStringPrecise(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,3 +6,4 @@ include("commons-jlhttp")
|
|||
include("commons-manifold")
|
||||
include("commons-slf4j")
|
||||
include("muscript")
|
||||
include("muscript-gson")
|
||||
|
|
|
@ -6,25 +6,33 @@ import java.util.function.Function;
|
|||
public class StringFormatter {
|
||||
public static String toString(Object o) {
|
||||
if (o == null) return "null";
|
||||
else if (o instanceof Double d) return toString(d);
|
||||
else if (o instanceof Float f) return toString(f);
|
||||
else if (o instanceof Double d) return toString((double) d);
|
||||
else if (o instanceof Float f) return toString((float) f);
|
||||
else if (o instanceof Throwable t) return toString(t, Objects::toString);
|
||||
else return o.toString();
|
||||
}
|
||||
|
||||
public static String toString(double d) {
|
||||
double abs = Math.abs(d);
|
||||
if (abs % 1.0 == 0) return String.format(Locale.US, "%.0f", d);
|
||||
else if (abs >= 1000) return String.format(Locale.US, "%.0f", d);
|
||||
if (abs >= 1000 || abs % 1.0 == 0) return String.format(Locale.US, "%.0f", d);
|
||||
else if (abs >= 0.1) return String.format(Locale.US, "%.4f", d);
|
||||
else return toStringPrecise(d);
|
||||
}
|
||||
|
||||
public static String toStringPrecise(double d) {
|
||||
if (d % 1.0 == 0) return String.format(Locale.US, "%.0f", d);
|
||||
else return String.format(Locale.US, "%s", d);
|
||||
}
|
||||
|
||||
public static String toString(float f) {
|
||||
float abs = Math.abs(f);
|
||||
if (abs % 1.0f == 0) return String.format(Locale.US, "%.0f", f);
|
||||
else if (abs >= 1000) return String.format(Locale.US, "%.0f", f);
|
||||
if (abs >= 1000 || abs % 1.0 == 0) return String.format(Locale.US, "%.0f", f);
|
||||
else if (abs >= 0.1f) return String.format(Locale.US, "%.4f", f);
|
||||
else return toStringPrecise(f);
|
||||
}
|
||||
|
||||
public static String toStringPrecise(float f) {
|
||||
if (f % 1.0f == 0) return String.format(Locale.US, "%.0f", f);
|
||||
else return String.format(Locale.US, "%s", f);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,14 +13,26 @@ public class ImmCollection {
|
|||
return new ImmutableList<>(list);
|
||||
}
|
||||
|
||||
public static <T> List<T> copyOf(List<T> list) {
|
||||
return of(new ArrayList<>(list));
|
||||
}
|
||||
|
||||
public static <T> Set<T> of(Set<T> set) {
|
||||
return new ImmutableSet<>(set);
|
||||
}
|
||||
|
||||
public static <T> Set<T> copyOf(Set<T> set) {
|
||||
return of(new LinkedHashSet<>(set));
|
||||
}
|
||||
|
||||
public static <K, V> Map<K, V> of(Map<K, V> map) {
|
||||
return new ImmutableMap<>(map);
|
||||
}
|
||||
|
||||
public static <K, V> Map<K, V> copyOf(Map<K, V> map) {
|
||||
return of(new LinkedHashMap<>(map));
|
||||
}
|
||||
|
||||
public static <T> Iterable<T> of(Iterable<T> iterable) {
|
||||
return new ImmutableIterable<>(iterable);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue