From 45e013098fcaf9169c53c130430db14e3cebf881 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Thu, 16 Nov 2023 23:17:38 +0300 Subject: [PATCH] Make node values type-safe --- .../chronosx88/JGUN/NodeArrayBuilder.java | 88 ------------------- .../JGUN/{ => api}/NodeChangeListener.java | 4 +- .../JGUN/api/graph/ArrayBuilder.java | 87 ++++++++++++++++++ .../graph/NodeBuilder.java} | 59 ++++++------- .../chronosx88/JGUN/models/NodeLink.java | 14 --- .../chronosx88/JGUN/models/acks/GetAck.java | 3 +- .../JGUN/models/{ => graph}/DeferredNode.java | 6 +- .../JGUN/models/{ => graph}/MemoryGraph.java | 7 +- .../JGUN/models/{ => graph}/Node.java | 18 ++-- .../JGUN/models/{ => graph}/NodeMetadata.java | 2 +- .../JGUN/models/graph/NodeValue.java | 19 ++++ .../models/graph/NodeValueDeserializer.java | 55 ++++++++++++ .../JGUN/models/graph/values/ArrayValue.java | 31 +++++++ .../models/graph/values/BooleanValue.java | 22 +++++ .../models/graph/values/DecimalValue.java | 40 +++++++++ .../JGUN/models/graph/values/IntValue.java | 44 ++++++++++ .../models/graph/values/NodeLinkValue.java | 22 +++++ .../JGUN/models/graph/values/StringValue.java | 23 +++++ .../JGUN/storage/MemoryStorage.java | 4 +- .../chronosx88/JGUN/storage/Storage.java | 15 ++-- .../chronosx88/JGUN/GraphNodeBuilderTest.java | 59 ++++++++----- 21 files changed, 433 insertions(+), 189 deletions(-) delete mode 100644 src/main/java/io/github/chronosx88/JGUN/NodeArrayBuilder.java rename src/main/java/io/github/chronosx88/JGUN/{ => api}/NodeChangeListener.java (65%) create mode 100644 src/main/java/io/github/chronosx88/JGUN/api/graph/ArrayBuilder.java rename src/main/java/io/github/chronosx88/JGUN/{GraphNodeBuilder.java => api/graph/NodeBuilder.java} (50%) delete mode 100644 src/main/java/io/github/chronosx88/JGUN/models/NodeLink.java rename src/main/java/io/github/chronosx88/JGUN/models/{ => graph}/DeferredNode.java (84%) rename src/main/java/io/github/chronosx88/JGUN/models/{ => graph}/MemoryGraph.java (78%) rename src/main/java/io/github/chronosx88/JGUN/models/{ => graph}/Node.java (52%) rename src/main/java/io/github/chronosx88/JGUN/models/{ => graph}/NodeMetadata.java (90%) create mode 100644 src/main/java/io/github/chronosx88/JGUN/models/graph/NodeValue.java create mode 100644 src/main/java/io/github/chronosx88/JGUN/models/graph/NodeValueDeserializer.java create mode 100644 src/main/java/io/github/chronosx88/JGUN/models/graph/values/ArrayValue.java create mode 100644 src/main/java/io/github/chronosx88/JGUN/models/graph/values/BooleanValue.java create mode 100644 src/main/java/io/github/chronosx88/JGUN/models/graph/values/DecimalValue.java create mode 100644 src/main/java/io/github/chronosx88/JGUN/models/graph/values/IntValue.java create mode 100644 src/main/java/io/github/chronosx88/JGUN/models/graph/values/NodeLinkValue.java create mode 100644 src/main/java/io/github/chronosx88/JGUN/models/graph/values/StringValue.java diff --git a/src/main/java/io/github/chronosx88/JGUN/NodeArrayBuilder.java b/src/main/java/io/github/chronosx88/JGUN/NodeArrayBuilder.java deleted file mode 100644 index 6e46ca0..0000000 --- a/src/main/java/io/github/chronosx88/JGUN/NodeArrayBuilder.java +++ /dev/null @@ -1,88 +0,0 @@ -package io.github.chronosx88.JGUN; - -import io.github.chronosx88.JGUN.models.MemoryGraph; -import io.github.chronosx88.JGUN.models.Node; -import io.github.chronosx88.JGUN.models.NodeLink; -import io.github.chronosx88.JGUN.models.NodeMetadata; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.*; - -public class NodeArrayBuilder { - private final MemoryGraph graph; - private final Node rootNode; - private final List innerArray; - protected static final String ARRAY_FIELD = "__ARRAY__"; - - public NodeArrayBuilder() { - this.graph = MemoryGraph.builder().build(); - this.innerArray = new ArrayList<>(); - this.rootNode = Node.builder() - .metadata(NodeMetadata.builder() - .nodeID(null) - .states(new HashMap<>(Map.of(ARRAY_FIELD, System.currentTimeMillis()))) - .build()) - .values(Map.of(ARRAY_FIELD, innerArray)) - .build(); - graph.nodes.put(GraphNodeBuilder.ROOT_NODE, this.rootNode); - } - - private NodeArrayBuilder addScalar(Object value) { - this.innerArray.add(value); - this.rootNode.getMetadata().getStates().put(ARRAY_FIELD, System.currentTimeMillis()); - return this; - } - - public NodeArrayBuilder add(String value) { - return addScalar(value); - } - - public NodeArrayBuilder add(BigInteger value) { - return addScalar(value); - } - - public NodeArrayBuilder add(BigDecimal value) { - return addScalar(value); - } - - public NodeArrayBuilder add(int value) { - return addScalar(value); - } - - public NodeArrayBuilder add(long value) { - return addScalar(value); - } - - public NodeArrayBuilder add(double value) { - return addScalar(value); - } - - public NodeArrayBuilder add(boolean value) { - return addScalar(value); - - } - - public NodeArrayBuilder addNull(String name) { - return addScalar(null); - } - - public NodeArrayBuilder add(GraphNodeBuilder builder) { - String newNodeID = UUID.randomUUID().toString(); - //noinspection unchecked - List innerArray = (List) rootNode.values.get(ARRAY_FIELD); - innerArray.add(NodeLink.builder() - .link(newNodeID) - .build()); - MemoryGraph innerGraph = builder.build(); - innerGraph.nodes.get(GraphNodeBuilder.ROOT_NODE).getMetadata().setNodeID(newNodeID); - innerGraph.nodes.put(newNodeID, innerGraph.nodes.get(GraphNodeBuilder.ROOT_NODE)); - innerGraph.nodes.remove(GraphNodeBuilder.ROOT_NODE); - this.graph.nodes.putAll(innerGraph.nodes); - return this; - } - - public MemoryGraph build() { - return this.graph; - } -} diff --git a/src/main/java/io/github/chronosx88/JGUN/NodeChangeListener.java b/src/main/java/io/github/chronosx88/JGUN/api/NodeChangeListener.java similarity index 65% rename from src/main/java/io/github/chronosx88/JGUN/NodeChangeListener.java rename to src/main/java/io/github/chronosx88/JGUN/api/NodeChangeListener.java index 4d3d58e..92ba625 100644 --- a/src/main/java/io/github/chronosx88/JGUN/NodeChangeListener.java +++ b/src/main/java/io/github/chronosx88/JGUN/api/NodeChangeListener.java @@ -1,6 +1,6 @@ -package io.github.chronosx88.JGUN; +package io.github.chronosx88.JGUN.api; -import io.github.chronosx88.JGUN.models.Node; +import io.github.chronosx88.JGUN.models.graph.Node; @FunctionalInterface public interface NodeChangeListener { diff --git a/src/main/java/io/github/chronosx88/JGUN/api/graph/ArrayBuilder.java b/src/main/java/io/github/chronosx88/JGUN/api/graph/ArrayBuilder.java new file mode 100644 index 0000000..f4445b9 --- /dev/null +++ b/src/main/java/io/github/chronosx88/JGUN/api/graph/ArrayBuilder.java @@ -0,0 +1,87 @@ +package io.github.chronosx88.JGUN.api.graph; + +import io.github.chronosx88.JGUN.models.graph.MemoryGraph; +import io.github.chronosx88.JGUN.models.graph.Node; +import io.github.chronosx88.JGUN.models.graph.NodeValue; +import io.github.chronosx88.JGUN.models.graph.values.*; +import io.github.chronosx88.JGUN.models.graph.NodeMetadata; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.*; + +public class ArrayBuilder { + private final MemoryGraph graph; + private final Node rootNode; + private final List innerArray; + protected static final String ARRAY_FIELD = "__ARRAY__"; + + public ArrayBuilder() { + this.graph = new MemoryGraph(); + this.innerArray = new ArrayList<>(); + this.rootNode = Node.builder() + .metadata(NodeMetadata.builder() + .nodeID(null) + .states(new HashMap<>(Map.of(ARRAY_FIELD, System.currentTimeMillis()))) + .build()) + .values(Map.of(ARRAY_FIELD, new ArrayValue(this.innerArray))) + .build(); + graph.nodes.put(NodeBuilder.ROOT_NODE, this.rootNode); + } + + private ArrayBuilder addScalar(NodeValue value) { + this.innerArray.add(value); + this.rootNode.getMetadata().getStates().put(ARRAY_FIELD, System.currentTimeMillis()); + return this; + } + + public ArrayBuilder add(String value) { + return addScalar(new StringValue(value)); + } + + public ArrayBuilder add(BigInteger value) { + return addScalar(new IntValue(value)); + } + + public ArrayBuilder add(BigDecimal value) { + return addScalar(new DecimalValue(value)); + } + + public ArrayBuilder add(int value) { + return addScalar(new IntValue(value)); + } + + public ArrayBuilder add(long value) { + return addScalar(new IntValue(value)); + } + + public ArrayBuilder add(double value) { + return addScalar(new DecimalValue(value)); + } + + public ArrayBuilder add(boolean value) { + return addScalar(new BooleanValue(value)); + } + + public ArrayBuilder addNull(String name) { + return addScalar(null); + } + + public ArrayBuilder add(NodeBuilder builder) { + String newNodeID = UUID.randomUUID().toString(); + List innerArray = ((ArrayValue) rootNode.values.get(ARRAY_FIELD)).getValue(); + innerArray.add(NodeLinkValue.builder() + .link(newNodeID) + .build()); + MemoryGraph innerGraph = builder.build(); + innerGraph.nodes.get(NodeBuilder.ROOT_NODE).getMetadata().setNodeID(newNodeID); + innerGraph.nodes.put(newNodeID, innerGraph.nodes.get(NodeBuilder.ROOT_NODE)); + innerGraph.nodes.remove(NodeBuilder.ROOT_NODE); + this.graph.nodes.putAll(innerGraph.nodes); + return this; + } + + public MemoryGraph build() { + return this.graph; + } +} diff --git a/src/main/java/io/github/chronosx88/JGUN/GraphNodeBuilder.java b/src/main/java/io/github/chronosx88/JGUN/api/graph/NodeBuilder.java similarity index 50% rename from src/main/java/io/github/chronosx88/JGUN/GraphNodeBuilder.java rename to src/main/java/io/github/chronosx88/JGUN/api/graph/NodeBuilder.java index 2234646..a4be063 100644 --- a/src/main/java/io/github/chronosx88/JGUN/GraphNodeBuilder.java +++ b/src/main/java/io/github/chronosx88/JGUN/api/graph/NodeBuilder.java @@ -1,22 +1,21 @@ -package io.github.chronosx88.JGUN; +package io.github.chronosx88.JGUN.api.graph; -import io.github.chronosx88.JGUN.models.MemoryGraph; -import io.github.chronosx88.JGUN.models.Node; -import io.github.chronosx88.JGUN.models.NodeLink; -import io.github.chronosx88.JGUN.models.NodeMetadata; +import io.github.chronosx88.JGUN.models.graph.*; +import io.github.chronosx88.JGUN.models.graph.values.*; +import io.github.chronosx88.JGUN.models.graph.NodeValue; import java.math.BigDecimal; import java.math.BigInteger; import java.util.List; import java.util.UUID; -public class GraphNodeBuilder { +public class NodeBuilder { private final MemoryGraph graph; private final Node rootNode; protected static final String ROOT_NODE = "__ROOT__"; - public GraphNodeBuilder() { - this.graph = MemoryGraph.builder().build(); + public NodeBuilder() { + this.graph = new MemoryGraph(); this.rootNode = Node.builder() .metadata(NodeMetadata.builder() .nodeID(null) @@ -25,48 +24,47 @@ public class GraphNodeBuilder { graph.nodes.put(ROOT_NODE, this.rootNode); } - private GraphNodeBuilder addScalar(String name, Object value) { + private NodeBuilder addScalar(String name, NodeValue value) { rootNode.values.put(name, value); rootNode.getMetadata().getStates().put(name, System.currentTimeMillis()); return this; } - public GraphNodeBuilder add(String name, String value) { - return addScalar(name, value); + public NodeBuilder add(String name, String value) { + return addScalar(name, new StringValue(value)); } - public GraphNodeBuilder add(String name, BigInteger value) { - return addScalar(name, value); + public NodeBuilder add(String name, BigInteger value) { + return addScalar(name, new IntValue(value)); } - public GraphNodeBuilder add(String name, BigDecimal value) { - return addScalar(name, value); + public NodeBuilder add(String name, BigDecimal value) { + return addScalar(name, new DecimalValue(value)); } - public GraphNodeBuilder add(String name, int value) { - return addScalar(name, value); + public NodeBuilder add(String name, int value) { + return addScalar(name, new IntValue(value)); } - public GraphNodeBuilder add(String name, long value) { - return addScalar(name, value); + public NodeBuilder add(String name, long value) { + return addScalar(name, new IntValue(value)); } - public GraphNodeBuilder add(String name, double value) { - return addScalar(name, value); + public NodeBuilder add(String name, double value) { + return addScalar(name, new DecimalValue(value)); } - public GraphNodeBuilder add(String name, boolean value) { - return addScalar(name, value); - + public NodeBuilder add(String name, boolean value) { + return addScalar(name, new BooleanValue(value)); } - public GraphNodeBuilder addNull(String name) { + public NodeBuilder addNull(String name) { return addScalar(name, null); } - public GraphNodeBuilder add(String name, GraphNodeBuilder builder) { + public NodeBuilder add(String name, NodeBuilder builder) { String newNodeID = UUID.randomUUID().toString(); - rootNode.values.put(name, NodeLink.builder() + rootNode.values.put(name, NodeLinkValue.builder() .link(newNodeID) .build()); MemoryGraph innerGraph = builder.build(); @@ -77,17 +75,16 @@ public class GraphNodeBuilder { return this; } - public GraphNodeBuilder add(String name, NodeArrayBuilder builder) { + public NodeBuilder add(String name, ArrayBuilder builder) { MemoryGraph innerGraph = builder.build(); - //noinspection unchecked - var innerArray = (List) innerGraph.nodes.get(ROOT_NODE).values.get(NodeArrayBuilder.ARRAY_FIELD); + var innerArray = (NodeValue) innerGraph.nodes.get(ROOT_NODE).values.get(ArrayBuilder.ARRAY_FIELD); rootNode.values.put(name, innerArray); rootNode.getMetadata().getStates().put(name, innerGraph .nodes .get(ROOT_NODE) .getMetadata() .getStates() - .get(NodeArrayBuilder.ARRAY_FIELD)); + .get(ArrayBuilder.ARRAY_FIELD)); innerGraph.nodes.remove(ROOT_NODE); graph.nodes.putAll(innerGraph.nodes); return this; diff --git a/src/main/java/io/github/chronosx88/JGUN/models/NodeLink.java b/src/main/java/io/github/chronosx88/JGUN/models/NodeLink.java deleted file mode 100644 index 7772c6a..0000000 --- a/src/main/java/io/github/chronosx88/JGUN/models/NodeLink.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.github.chronosx88.JGUN.models; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.Builder; -import lombok.Data; -import lombok.extern.jackson.Jacksonized; - -@Data -@Builder -@Jacksonized -public class NodeLink { - @JsonProperty("#") - String link; -} diff --git a/src/main/java/io/github/chronosx88/JGUN/models/acks/GetAck.java b/src/main/java/io/github/chronosx88/JGUN/models/acks/GetAck.java index 3fccfe8..89d4d1c 100644 --- a/src/main/java/io/github/chronosx88/JGUN/models/acks/GetAck.java +++ b/src/main/java/io/github/chronosx88/JGUN/models/acks/GetAck.java @@ -1,8 +1,7 @@ package io.github.chronosx88.JGUN.models.acks; import com.fasterxml.jackson.annotation.JsonProperty; -import io.github.chronosx88.JGUN.models.MemoryGraph; -import lombok.Builder; +import io.github.chronosx88.JGUN.models.graph.MemoryGraph; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.SuperBuilder; diff --git a/src/main/java/io/github/chronosx88/JGUN/models/DeferredNode.java b/src/main/java/io/github/chronosx88/JGUN/models/graph/DeferredNode.java similarity index 84% rename from src/main/java/io/github/chronosx88/JGUN/models/DeferredNode.java rename to src/main/java/io/github/chronosx88/JGUN/models/graph/DeferredNode.java index 2fbceea..1af4ef7 100644 --- a/src/main/java/io/github/chronosx88/JGUN/models/DeferredNode.java +++ b/src/main/java/io/github/chronosx88/JGUN/models/graph/DeferredNode.java @@ -1,6 +1,4 @@ -package io.github.chronosx88.JGUN.models; - -import lombok.Getter; +package io.github.chronosx88.JGUN.models.graph; import java.util.Map; import java.util.concurrent.Delayed; @@ -9,7 +7,7 @@ import java.util.concurrent.TimeUnit; public class DeferredNode extends Node implements Delayed { private long deferredUntil = 0; - DeferredNode(NodeMetadata metadata, Map values) { + DeferredNode(NodeMetadata metadata, Map values) { super(metadata, values); } diff --git a/src/main/java/io/github/chronosx88/JGUN/models/MemoryGraph.java b/src/main/java/io/github/chronosx88/JGUN/models/graph/MemoryGraph.java similarity index 78% rename from src/main/java/io/github/chronosx88/JGUN/models/MemoryGraph.java rename to src/main/java/io/github/chronosx88/JGUN/models/graph/MemoryGraph.java index ea0505f..cef1ec4 100644 --- a/src/main/java/io/github/chronosx88/JGUN/models/MemoryGraph.java +++ b/src/main/java/io/github/chronosx88/JGUN/models/graph/MemoryGraph.java @@ -1,4 +1,4 @@ -package io.github.chronosx88.JGUN.models; +package io.github.chronosx88.JGUN.models.graph; import com.fasterxml.jackson.annotation.JsonAnyGetter; import com.fasterxml.jackson.annotation.JsonAnySetter; @@ -11,12 +11,9 @@ import java.util.LinkedHashMap; import java.util.Map; @Data -@Builder -@Jacksonized public class MemoryGraph { @JsonIgnore - @Builder.Default - public final Map nodes = new LinkedHashMap<>(); + public Map nodes = new LinkedHashMap<>(); @JsonAnyGetter public Map nodes() { diff --git a/src/main/java/io/github/chronosx88/JGUN/models/Node.java b/src/main/java/io/github/chronosx88/JGUN/models/graph/Node.java similarity index 52% rename from src/main/java/io/github/chronosx88/JGUN/models/Node.java rename to src/main/java/io/github/chronosx88/JGUN/models/graph/Node.java index 49d429d..90511a2 100644 --- a/src/main/java/io/github/chronosx88/JGUN/models/Node.java +++ b/src/main/java/io/github/chronosx88/JGUN/models/graph/Node.java @@ -1,34 +1,38 @@ -package io.github.chronosx88.JGUN.models; +package io.github.chronosx88.JGUN.models.graph; import com.fasterxml.jackson.annotation.JsonAnyGetter; import com.fasterxml.jackson.annotation.JsonAnySetter; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; -import lombok.extern.jackson.Jacksonized; +import lombok.NoArgsConstructor; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; @Data -@Jacksonized @Builder +@NoArgsConstructor +@AllArgsConstructor public class Node { @JsonProperty("_") - private NodeMetadata metadata; + @Builder.Default + private final NodeMetadata metadata = new NodeMetadata(new HashMap<>(), null); @JsonIgnore @Builder.Default - public Map values = new LinkedHashMap<>(); // Data + public final Map values = new LinkedHashMap<>(); // Data @JsonAnyGetter - public Map getValues() { + public Map getValues() { return values; } @JsonAnySetter - public void allSetter(String key, String value) { + public void allSetter(String key, NodeValue value) { values.put(key, value); } } \ No newline at end of file diff --git a/src/main/java/io/github/chronosx88/JGUN/models/NodeMetadata.java b/src/main/java/io/github/chronosx88/JGUN/models/graph/NodeMetadata.java similarity index 90% rename from src/main/java/io/github/chronosx88/JGUN/models/NodeMetadata.java rename to src/main/java/io/github/chronosx88/JGUN/models/graph/NodeMetadata.java index ed79122..81f004e 100644 --- a/src/main/java/io/github/chronosx88/JGUN/models/NodeMetadata.java +++ b/src/main/java/io/github/chronosx88/JGUN/models/graph/NodeMetadata.java @@ -1,4 +1,4 @@ -package io.github.chronosx88.JGUN.models; +package io.github.chronosx88.JGUN.models.graph; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Builder; diff --git a/src/main/java/io/github/chronosx88/JGUN/models/graph/NodeValue.java b/src/main/java/io/github/chronosx88/JGUN/models/graph/NodeValue.java new file mode 100644 index 0000000..bcf0357 --- /dev/null +++ b/src/main/java/io/github/chronosx88/JGUN/models/graph/NodeValue.java @@ -0,0 +1,19 @@ +package io.github.chronosx88.JGUN.models.graph; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + +//@JsonTypeInfo(use = JsonTypeInfo.Id.NONE) +@JsonDeserialize(using = NodeValueDeserializer.class) +public interface NodeValue { + enum ValueType { + ARRAY, + STRING, + INTEGER, + DECIMAL, + BOOLEAN, + NULL, + LINK + } + + ValueType getValueType(); +} \ No newline at end of file diff --git a/src/main/java/io/github/chronosx88/JGUN/models/graph/NodeValueDeserializer.java b/src/main/java/io/github/chronosx88/JGUN/models/graph/NodeValueDeserializer.java new file mode 100644 index 0000000..0d980ef --- /dev/null +++ b/src/main/java/io/github/chronosx88/JGUN/models/graph/NodeValueDeserializer.java @@ -0,0 +1,55 @@ +package io.github.chronosx88.JGUN.models.graph; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import io.github.chronosx88.JGUN.models.graph.values.*; + +import java.io.IOException; +import java.util.ArrayList; + +public class NodeValueDeserializer extends JsonDeserializer { + @Override + public NodeValue deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException { + JsonNode node = p.readValueAsTree(); + return parseValue(node); + } + + private NodeValue parseValue(JsonNode node) throws JsonProcessingException { + if (node.isBoolean()) { + return new BooleanValue(node.booleanValue()); + } else if (node.isBigDecimal()) { + return new DecimalValue(node.decimalValue()); + } else if (node.isFloat()) { + return new DecimalValue(node.floatValue()); + } else if (node.isDouble()) { + return new DecimalValue(node.doubleValue()); + } else if (node.isTextual()) { + return new StringValue(node.textValue()); + } else if (node.isInt()) { + return new IntValue(node.intValue()); + } else if (node.isBigInteger()) { + return new IntValue(node.bigIntegerValue()); + } else if (node.isLong()) { + return new IntValue(node.longValue()); + } else if (node.isObject()) { + if (node.has("#")) { + return NodeLinkValue.builder() + .link(node.get("#").textValue()) + .build(); + } else { + throw new IllegalArgumentException("node can have only links, not actual objects"); + } + } else if (node.isArray()) { + ArrayValue value = new ArrayValue(); + for (JsonNode arrayItem : node) { + value.getValue().add(parseValue(arrayItem)); + } + return value; + } + throw new IllegalArgumentException("unsupported node value"); + } +} diff --git a/src/main/java/io/github/chronosx88/JGUN/models/graph/values/ArrayValue.java b/src/main/java/io/github/chronosx88/JGUN/models/graph/values/ArrayValue.java new file mode 100644 index 0000000..3ba2f9e --- /dev/null +++ b/src/main/java/io/github/chronosx88/JGUN/models/graph/values/ArrayValue.java @@ -0,0 +1,31 @@ +package io.github.chronosx88.JGUN.models.graph.values; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import io.github.chronosx88.JGUN.models.graph.NodeValue; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.List; + +@Getter +@NoArgsConstructor +public class ArrayValue implements NodeValue { + @JsonValue + private List value = new ArrayList<>(); + + @JsonCreator + public ArrayValue(NodeValue[] value) { + this.value = new ArrayList<>(List.of(value)); + } + + public ArrayValue(List value) { + this.value = value; + } + + @Override + public ValueType getValueType() { + return ValueType.ARRAY; + } +} diff --git a/src/main/java/io/github/chronosx88/JGUN/models/graph/values/BooleanValue.java b/src/main/java/io/github/chronosx88/JGUN/models/graph/values/BooleanValue.java new file mode 100644 index 0000000..5b4c026 --- /dev/null +++ b/src/main/java/io/github/chronosx88/JGUN/models/graph/values/BooleanValue.java @@ -0,0 +1,22 @@ +package io.github.chronosx88.JGUN.models.graph.values; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import io.github.chronosx88.JGUN.models.graph.NodeValue; +import lombok.Getter; + +@Getter +public class BooleanValue implements NodeValue { + @JsonValue + private boolean value; + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public BooleanValue(boolean value) { + this.value = value; + } + + @Override + public NodeValue.ValueType getValueType() { + return NodeValue.ValueType.BOOLEAN; + } +} diff --git a/src/main/java/io/github/chronosx88/JGUN/models/graph/values/DecimalValue.java b/src/main/java/io/github/chronosx88/JGUN/models/graph/values/DecimalValue.java new file mode 100644 index 0000000..ceacfea --- /dev/null +++ b/src/main/java/io/github/chronosx88/JGUN/models/graph/values/DecimalValue.java @@ -0,0 +1,40 @@ +package io.github.chronosx88.JGUN.models.graph.values; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import io.github.chronosx88.JGUN.models.graph.NodeValue; + +import java.math.BigDecimal; + +public class DecimalValue implements NodeValue { + @JsonValue + private BigDecimal value; + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public DecimalValue(double num) { + this.value = BigDecimal.valueOf(num); + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public DecimalValue(float num) { + this.value = BigDecimal.valueOf(num); + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public DecimalValue(BigDecimal value) { + this.value = value; + } + + @Override + public ValueType getValueType() { + return ValueType.DECIMAL; + } + + public double getDouble() { + return value.floatValue(); + } + + public float getFloat() { + return value.floatValue(); + } +} diff --git a/src/main/java/io/github/chronosx88/JGUN/models/graph/values/IntValue.java b/src/main/java/io/github/chronosx88/JGUN/models/graph/values/IntValue.java new file mode 100644 index 0000000..bc152bf --- /dev/null +++ b/src/main/java/io/github/chronosx88/JGUN/models/graph/values/IntValue.java @@ -0,0 +1,44 @@ +package io.github.chronosx88.JGUN.models.graph.values; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import io.github.chronosx88.JGUN.models.graph.NodeValue; + +import java.math.BigInteger; + +public class IntValue implements NodeValue { + @JsonValue + public BigInteger value; + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public IntValue(int value) { + this.value = BigInteger.valueOf(value); + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public IntValue(long value) { + this.value = BigInteger.valueOf(value); + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public IntValue(BigInteger value) { + this.value = value; + } + + public int getInt() { + return value.intValue(); + } + + public BigInteger getBig() { + return value; + } + + public long getLong() { + return value.longValue(); + } + + @Override + public ValueType getValueType() { + return ValueType.INTEGER; + } +} diff --git a/src/main/java/io/github/chronosx88/JGUN/models/graph/values/NodeLinkValue.java b/src/main/java/io/github/chronosx88/JGUN/models/graph/values/NodeLinkValue.java new file mode 100644 index 0000000..a539480 --- /dev/null +++ b/src/main/java/io/github/chronosx88/JGUN/models/graph/values/NodeLinkValue.java @@ -0,0 +1,22 @@ +package io.github.chronosx88.JGUN.models.graph.values; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.github.chronosx88.JGUN.models.graph.NodeValue; +import lombok.Builder; +import lombok.Data; +import lombok.extern.jackson.Jacksonized; + +@Data +@Builder +@Jacksonized +public class NodeLinkValue implements NodeValue { + @JsonProperty("#") + private String link; + + @Override + @JsonIgnore + public ValueType getValueType() { + return ValueType.LINK; + } +} diff --git a/src/main/java/io/github/chronosx88/JGUN/models/graph/values/StringValue.java b/src/main/java/io/github/chronosx88/JGUN/models/graph/values/StringValue.java new file mode 100644 index 0000000..f6d8e3a --- /dev/null +++ b/src/main/java/io/github/chronosx88/JGUN/models/graph/values/StringValue.java @@ -0,0 +1,23 @@ +package io.github.chronosx88.JGUN.models.graph.values; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import io.github.chronosx88.JGUN.models.graph.NodeValue; +import lombok.Getter; + +@Getter +public class StringValue implements NodeValue { + @JsonValue + private String value; + + @JsonCreator + public StringValue(String value) { + this.value = value; + } + + @Override + public ValueType getValueType() { + return ValueType.STRING; + } + +} diff --git a/src/main/java/io/github/chronosx88/JGUN/storage/MemoryStorage.java b/src/main/java/io/github/chronosx88/JGUN/storage/MemoryStorage.java index 055669b..feaa9c7 100644 --- a/src/main/java/io/github/chronosx88/JGUN/storage/MemoryStorage.java +++ b/src/main/java/io/github/chronosx88/JGUN/storage/MemoryStorage.java @@ -3,8 +3,8 @@ package io.github.chronosx88.JGUN.storage; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.Expiry; -import io.github.chronosx88.JGUN.models.DeferredNode; -import io.github.chronosx88.JGUN.models.Node; +import io.github.chronosx88.JGUN.models.graph.DeferredNode; +import io.github.chronosx88.JGUN.models.graph.Node; import org.checkerframework.checker.index.qual.NonNegative; import java.util.Collection; diff --git a/src/main/java/io/github/chronosx88/JGUN/storage/Storage.java b/src/main/java/io/github/chronosx88/JGUN/storage/Storage.java index d81a195..200be29 100644 --- a/src/main/java/io/github/chronosx88/JGUN/storage/Storage.java +++ b/src/main/java/io/github/chronosx88/JGUN/storage/Storage.java @@ -1,11 +1,8 @@ package io.github.chronosx88.JGUN.storage; -import io.github.chronosx88.JGUN.HAM; -import io.github.chronosx88.JGUN.NodeChangeListener; -import io.github.chronosx88.JGUN.models.DeferredNode; -import io.github.chronosx88.JGUN.models.MemoryGraph; -import io.github.chronosx88.JGUN.models.Node; -import io.github.chronosx88.JGUN.models.NodeMetadata; +import io.github.chronosx88.JGUN.api.NodeChangeListener; +import io.github.chronosx88.JGUN.models.graph.*; +import io.github.chronosx88.JGUN.models.graph.NodeValue; import java.util.*; @@ -36,7 +33,7 @@ public abstract class Storage { */ public void mergeUpdate(MemoryGraph update) { long machine = System.currentTimeMillis(); - MemoryGraph diff = MemoryGraph.builder().build(); + MemoryGraph diff = new MemoryGraph(); for (Map.Entry entry : update.getNodes().entrySet()) { Node node = entry.getValue(); Node diffNode = this.mergeNode(node, machine); @@ -57,7 +54,7 @@ public abstract class Storage { changeListeners.get(diffEntry.getKey()).forEach((e) -> e.onChange(diffEntry.getValue())); } if (mapChangeListeners.containsKey(diffEntry.getKey())) { - for (Map.Entry nodeEntry : changedNode.getValues().entrySet()) { + for (Map.Entry nodeEntry : changedNode.getValues().entrySet()) { mapChangeListeners.get(nodeEntry.getKey()).forEach((e) -> e.onChange(nodeEntry.getKey(), nodeEntry.getValue())); } } @@ -75,7 +72,7 @@ public abstract class Storage { public Node mergeNode(Node incomingNode, long machineState) { Node changedNode = null; for (String key : incomingNode.getValues().keySet()) { - Object value = incomingNode.getValues().get(key); + NodeValue value = incomingNode.getValues().get(key); long state = incomingNode.getMetadata().getStates().get(key); long previousState = -1; Object currentValue = null; diff --git a/src/test/java/io/github/chronosx88/JGUN/GraphNodeBuilderTest.java b/src/test/java/io/github/chronosx88/JGUN/GraphNodeBuilderTest.java index 1a76a0e..3ed3e43 100644 --- a/src/test/java/io/github/chronosx88/JGUN/GraphNodeBuilderTest.java +++ b/src/test/java/io/github/chronosx88/JGUN/GraphNodeBuilderTest.java @@ -3,61 +3,72 @@ package io.github.chronosx88.JGUN; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import io.github.chronosx88.JGUN.api.graph.NodeBuilder; +import io.github.chronosx88.JGUN.api.graph.ArrayBuilder; +import io.github.chronosx88.JGUN.models.graph.MemoryGraph; +import io.github.chronosx88.JGUN.models.graph.values.IntValue; +import io.github.chronosx88.JGUN.models.graph.values.NodeLinkValue; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + class GraphNodeBuilderTest { @Test - void Test_sampleGraph1() { + void Test_sampleGraph1() throws JsonProcessingException { var objectMapper = new ObjectMapper(); objectMapper.registerModule(new Jdk8Module()); - var graph = new GraphNodeBuilder() + var graph = new NodeBuilder() .add("firstName", "John") .add("lastName", "Smith") .add("age", 25) - .add("address", new GraphNodeBuilder() + .add("address", new NodeBuilder() .add("streetAddress", "21 2nd Street") .add("city", "New York") .add("state", "NY") .add("postalCode", "10021")) - .add("phoneNumber", new NodeArrayBuilder() - .add(new GraphNodeBuilder() + .add("phoneNumber", new ArrayBuilder() + .add(new NodeBuilder() .add("type", "home") .add("number", "212 555-1234")) - .add(new GraphNodeBuilder() + .add(new NodeBuilder() .add("type", "fax") .add("number", "646 555-4567"))) + .addNull("heh") .build(); - String graphJSON = null; - try { - graphJSON = objectMapper.writeValueAsString(graph); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - System.out.println(graphJSON); + String graphJSON1 = objectMapper.writeValueAsString(graph); + System.out.println(graphJSON1); + + graph = objectMapper.readValue(graphJSON1, MemoryGraph.class); + String graphJSON2 = objectMapper.writeValueAsString(graph); + System.out.println(graphJSON2); + + assertEquals(graphJSON1, graphJSON2); + assertEquals(((IntValue) graph.nodes.get("__ROOT__").getValues().get("age")).getInt(), 25); + assertTrue(graph.nodes.get("__ROOT__").getValues().get("address") instanceof NodeLinkValue); } @Test - void Test_sampleGraph2() { + void Test_sampleGraph2() throws JsonProcessingException { var objectMapper = new ObjectMapper(); objectMapper.registerModule(new Jdk8Module()); - var graph = new GraphNodeBuilder() - .add("a", new NodeArrayBuilder() - .add(new GraphNodeBuilder() - .add("b", new GraphNodeBuilder() + var graph = new NodeBuilder() + .add("a", new ArrayBuilder() + .add(new NodeBuilder() + .add("b", new NodeBuilder() .add("c", true))) .add(0)) .build(); - String graphJSON = null; - try { - graphJSON = objectMapper.writeValueAsString(graph); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } + String graphJSON = objectMapper.writeValueAsString(graph); + System.out.println(graphJSON); + + graph = objectMapper.readValue(graphJSON, MemoryGraph.class); + graphJSON = objectMapper.writeValueAsString(graph); System.out.println(graphJSON); } } \ No newline at end of file