Skip to content

Commit

Permalink
[json-node] Add copy() and unmodifible() for deep copy options (#302)
Browse files Browse the repository at this point in the history
Ability to create a mutable deep copy and an immutable deep copy
  • Loading branch information
rbygrave authored Dec 12, 2024
1 parent 2e2255d commit ac31283
Show file tree
Hide file tree
Showing 11 changed files with 168 additions and 7 deletions.
18 changes: 18 additions & 0 deletions json-node/src/main/java/io/avaje/json/node/JsonArray.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,24 @@ public String toString() {
return text();
}

@Override
public JsonArray unmodifiable() {
final var newList = new ArrayList<JsonNode>(children.size());
for (JsonNode child : children) {
newList.add(child.unmodifiable());
}
return of(newList);
}

@Override
public JsonArray copy() {
final var newList = new ArrayList<JsonNode>(children.size());
for (JsonNode child : children) {
newList.add(child.copy());
}
return new JsonArray(newList);
}

@Override
public Type type() {
return Type.ARRAY;
Expand Down
12 changes: 11 additions & 1 deletion json-node/src/main/java/io/avaje/json/node/JsonBoolean.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.avaje.json.node;

public final class JsonBoolean implements JsonNode {
public final /*value*/ class JsonBoolean implements JsonNode {

private final boolean value;

Expand All @@ -17,6 +17,16 @@ public String toString() {
return text();
}

@Override
public JsonBoolean unmodifiable() {
return this;
}

@Override
public JsonBoolean copy() {
return this;
}

@Override
public Type type() {
return Type.BOOLEAN;
Expand Down
12 changes: 11 additions & 1 deletion json-node/src/main/java/io/avaje/json/node/JsonDecimal.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import java.math.BigDecimal;

public final class JsonDecimal implements JsonNumber {
public final /*value*/ class JsonDecimal implements JsonNumber {

private final BigDecimal value;

Expand All @@ -21,6 +21,16 @@ public String toString() {
return text();
}

@Override
public JsonDecimal unmodifiable() {
return this;
}

@Override
public JsonDecimal copy() {
return this;
}

@Override
public Type type() {
return Type.NUMBER;
Expand Down
12 changes: 11 additions & 1 deletion json-node/src/main/java/io/avaje/json/node/JsonDouble.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import java.math.BigDecimal;

public final class JsonDouble implements JsonNumber {
public final /*value*/ class JsonDouble implements JsonNumber {

private final double value;

Expand All @@ -21,6 +21,16 @@ public String toString() {
return text();
}

@Override
public JsonDouble unmodifiable() {
return this;
}

@Override
public JsonDouble copy() {
return this;
}

@Override
public Type type() {
return Type.NUMBER;
Expand Down
12 changes: 11 additions & 1 deletion json-node/src/main/java/io/avaje/json/node/JsonInteger.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import java.math.BigDecimal;

public final class JsonInteger implements JsonNumber {
public final /*value*/ class JsonInteger implements JsonNumber {

private final int value;

Expand All @@ -21,6 +21,16 @@ public String toString() {
return text();
}

@Override
public JsonInteger unmodifiable() {
return this;
}

@Override
public JsonInteger copy() {
return this;
}

@Override
public Type type() {
return Type.NUMBER;
Expand Down
12 changes: 11 additions & 1 deletion json-node/src/main/java/io/avaje/json/node/JsonLong.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import java.math.BigDecimal;

public final class JsonLong implements JsonNumber {
public final /*value*/ class JsonLong implements JsonNumber {

private final long value;

Expand All @@ -21,6 +21,16 @@ public String toString() {
return text();
}

@Override
public JsonLong unmodifiable() {
return this;
}

@Override
public JsonLong copy() {
return this;
}

@Override
public Type type() {
return Type.NUMBER;
Expand Down
10 changes: 10 additions & 0 deletions json-node/src/main/java/io/avaje/json/node/JsonNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ public boolean isObject() {
*/
String text();

/**
* Return an unmodifiable deep copy of the JsonNode.
*/
JsonNode unmodifiable();

/**
* Return a mutable deep copy of the JsonNode.
*/
JsonNode copy();

/**
* Find a node given a path using dot notation.
* @param path The path in dot notation
Expand Down
18 changes: 18 additions & 0 deletions json-node/src/main/java/io/avaje/json/node/JsonObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,24 @@ public String text() {
return children.toString();
}

@Override
public JsonObject unmodifiable() {
final var mapCopy = new LinkedHashMap<String,JsonNode>();
for (Map.Entry<String, JsonNode> entry : children.entrySet()) {
mapCopy.put(entry.getKey(), entry.getValue().unmodifiable());
}
return JsonObject.of(mapCopy);
}

@Override
public JsonObject copy() {
final var mapCopy = new LinkedHashMap<String,JsonNode>();
for (Map.Entry<String, JsonNode> entry : children.entrySet()) {
mapCopy.put(entry.getKey(), entry.getValue().copy());
}
return new JsonObject(mapCopy);
}

/**
* Return true if the json object contains no elements.
*/
Expand Down
12 changes: 11 additions & 1 deletion json-node/src/main/java/io/avaje/json/node/JsonString.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.avaje.json.node;

public final class JsonString implements JsonNode {
public final /*value*/ class JsonString implements JsonNode {

private final String value;

Expand All @@ -17,6 +17,16 @@ public String toString() {
return text();
}

@Override
public JsonString unmodifiable() {
return this;
}

@Override
public JsonString copy() {
return this;
}

@Override
public Type type() {
return Type.STRING;
Expand Down
28 changes: 28 additions & 0 deletions json-node/src/test/java/io/avaje/json/node/JsonArrayTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.stream.Collectors;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

class JsonArrayTest {

Expand Down Expand Up @@ -87,4 +88,31 @@ void add() {
assertThat(elements.get(4)).isInstanceOf(JsonObject.class);
}

@Test
void copy() {
final JsonArray source = JsonArray.create()
.add("foo")
.add(JsonObject.create().add("b", 42));

JsonArray copy = source.copy();
assertThat(copy.toString()).isEqualTo(source.toString());

copy.add("canMutate");
assertThat(copy.size()).isEqualTo(3);
assertThat(source.size()).isEqualTo(2);
}

@Test
void unmodifiable() {
final JsonArray source = JsonArray.create()
.add("foo")
.add(JsonObject.create().add("b", 42));

JsonArray copy = source.unmodifiable();
assertThat(copy.toString()).isEqualTo(source.toString());

assertThatThrownBy(() -> copy.add("canMutate"))
.isInstanceOf(UnsupportedOperationException.class);
}

}
29 changes: 28 additions & 1 deletion json-node/src/test/java/io/avaje/json/node/JsonObjectTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
import java.math.BigDecimal;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

class JsonObjectTest {

Expand Down Expand Up @@ -116,4 +116,31 @@ void findNested() {

assertThat(node.extract("address.other.deep")).isEqualTo("one");
}

@Test
void copy() {
final JsonObject source = JsonObject.create()
.add("name", "foo")
.add("other", JsonObject.create().add("b", 42));

JsonObject copy = source.copy();
assertThat(copy.toString()).isEqualTo(source.toString());

copy.add("canMutate", true);
assertThat(copy.containsKey("canMutate")).isTrue();
assertThat(source.containsKey("canMutate")).isFalse();
}

@Test
void unmodifiable() {
final JsonObject source = JsonObject.create()
.add("name", "foo")
.add("other", JsonObject.create().add("b", 42));

JsonObject copy = source.unmodifiable();
assertThat(copy.toString()).isEqualTo(source.toString());

assertThatThrownBy(() -> copy.add("canMutate", true))
.isInstanceOf(UnsupportedOperationException.class);
}
}

0 comments on commit ac31283

Please sign in to comment.