[java-support] 02/08: Put back notion of "null" object, and add unit tests.
Scott Cantor
cantor.2 at osu.edu
Mon Sep 13 23:37:41 UTC 2021
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch dev/JSPT-111
in repository java-support.
View the commit online:
http://git.shibboleth.net/view/?p=java-support.git;a=commit;h=c67ebc58c49680f185cad7c96987a4304b029642
commit c67ebc58c49680f185cad7c96987a4304b029642
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Mon May 17 10:13:47 2021 -0400
Put back notion of "null" object, and add unit tests.
---
.../shibboleth/utilities/java/support/ddf/DDF.java | 107 +++++++++++++--------
.../utilities/java/support/ddf/DDFTest.java | 72 +++++++++++++-
2 files changed, 137 insertions(+), 42 deletions(-)
diff --git a/src/main/java/net/shibboleth/utilities/java/support/ddf/DDF.java b/src/main/java/net/shibboleth/utilities/java/support/ddf/DDF.java
index 8e75e06..8ee2374 100644
--- a/src/main/java/net/shibboleth/utilities/java/support/ddf/DDF.java
+++ b/src/main/java/net/shibboleth/utilities/java/support/ddf/DDF.java
@@ -19,11 +19,10 @@ package net.shibboleth.utilities.java.support.ddf;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.function.Function;
-import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -54,7 +53,7 @@ import net.shibboleth.utilities.java.support.logic.Constraint;
* the other version(s) of the same API.</p>
*/
@NotThreadSafe
-public class DDF {
+public class DDF implements Iterable<DDF> {
/** Name of node. */
@Nullable private String name;
@@ -65,6 +64,9 @@ public class DDF {
/** Type enum. */
public enum DDFType {
+ /** A null node. */
+ DDF_NULL(-1),
+
/** An empty node with no value. */
DDF_EMPTY(0),
@@ -120,7 +122,7 @@ public class DDF {
/** Constructor. */
public DDF() {
- type = DDFType.DDF_EMPTY;
+ type = DDFType.DDF_NULL;
}
/**
@@ -131,7 +133,7 @@ public class DDF {
* @param n node name
*/
public DDF(@Nullable @NotEmpty final String n) {
- this();
+ type = DDFType.DDF_EMPTY;
name(n);
}
@@ -197,7 +199,7 @@ public class DDF {
*/
@Nonnull public DDF destroy() {
remove().empty().name(null);
- type = null;
+ type = DDFType.DDF_NULL;
return this;
}
@@ -211,6 +213,10 @@ public class DDF {
final DDF dup = new DDF(name);
switch (type) {
+ case DDF_NULL:
+ dup.destroy();
+ break;
+
case DDF_EMPTY:
break;
@@ -271,7 +277,7 @@ public class DDF {
* @return this object
*/
@Nonnull public DDF name(@Nullable @NotEmpty final String n) {
- if (parent == null || !parent.isstruct()) {
+ if (!isnull() && (parent == null || !parent.isstruct())) {
if (n != null) {
name = Constraint.isNotEmpty(n.substring(0,Integer.min(n.length(), 255)), "Name cannot be empty");
} else {
@@ -281,6 +287,15 @@ public class DDF {
return this;
}
+ /**
+ * Returns true iff the node is null.
+ *
+ * @return true iff the node is null
+ */
+ public boolean isnull() {
+ return type == DDFType.DDF_NULL;
+ }
+
/**
* Returns true iff the node is empty.
*
@@ -614,7 +629,7 @@ public class DDF {
*/
@SuppressWarnings("unchecked")
@Nonnull public DDF add(@Nonnull final DDF child) {
- if ((!isstruct() && !islist()) || this == child.parent) {
+ if ((!isstruct() && !islist()) || child.isnull() || this == child.parent) {
return child;
}
@@ -647,7 +662,7 @@ public class DDF {
* @return the child
*/
@Nonnull public DDF addbefore(@Nonnull final DDF child, @Nonnull final DDF before) {
- if (!islist() || before.parent != this) {
+ if (!islist() || child.isnull() || before.parent != this) {
return child;
}
@@ -672,7 +687,7 @@ public class DDF {
* @return the child
*/
@Nonnull public DDF addafter(@Nonnull final DDF child, @Nonnull final DDF after) {
- if (!islist() || after.parent != this) {
+ if (!islist() || child.isnull() || after.parent != this) {
return child;
}
@@ -721,36 +736,33 @@ public class DDF {
}
/**
- * Expose an immutable map representing a structure or list node.
+ * Expose an immutable map representing a structure node.
*
- * @return immutable map, or null
+ * @return immutable map, or null if the node is not a structure
*/
@SuppressWarnings("unchecked")
- @Nonnull @NonnullElements @Unmodifiable @NotLive public Map<String,DDF> asMap() {
+ @Nullable @NonnullElements @Unmodifiable @NotLive public Map<String,DDF> asMap() {
if (isstruct()) {
return Map.copyOf((Map<String,DDF>) value);
- } else if (islist()) {
- return ((List<DDF>) value).stream().collect(
- Collectors.toUnmodifiableMap(DDF::name, Function.identity()));
}
- return Collections.emptyMap();
+ return null;
}
/**
* Expose an immutable list representing a structure or list node.
*
- * @return immutable list, or null
+ * @return immutable list, or null if the node is not a structure or list
*/
@SuppressWarnings("unchecked")
- @Nonnull @NonnullElements @Unmodifiable @NotLive public List<DDF> asList() {
+ @Nullable @NonnullElements @Unmodifiable @NotLive public List<DDF> asList() {
if (isstruct()) {
return List.copyOf(((Map<String,DDF>) value).values());
} else if (islist()) {
return List.copyOf((List<DDF>) value);
}
- return Collections.emptyList();
+ return null;
}
/**
@@ -764,45 +776,49 @@ public class DDF {
*
* @param path dotted path to use
*
- * @return the last node added to the nested tree
+ * @return the last node added to the nested tree, or a null node if unable to do so
*/
@Nonnull public DDF addmember(@Nonnull @NotEmpty final String path) {
final String[] tokens = Constraint.isNotEmpty(path, "Path cannot be null").split("\\.");
Constraint.isNotEmpty(tokens, "Path did not produce an array of path segments");
- DDF base = this;
- for (final String segment : tokens) {
- if (!base.isstruct()) {
- base.structure();
- }
-
- DDF node = base.getmember(segment);
- if (node == null) {
- node = base.add(new DDF(segment));
+ if (!isnull()) {
+ DDF base = this;
+ for (final String segment : tokens) {
+ if (!base.isstruct()) {
+ base.structure();
+ }
+
+ DDF node = base.getmember(segment);
+ if (node.isnull()) {
+ node = base.add(new DDF(segment));
+ }
+
+ base = node;
}
- base = node;
+ return base;
}
- return base;
+ return new DDF();
}
/**
* Access a (possibly nested) structure member via dotted path notation, also allowing access to
* list elements via "[n]" array notation.
*
- * <p>Failure to navigate the tree at any point will cause a null to be returned.
+ * <p>Failure to navigate the tree at any point will cause a null node to be returned.</p>
*
* @param path dotted path to use
*
- * @return the matching node, or null
+ * @return the matching node, or a null node
*/
// Checkstyle: CyclomaticComplexity OFF
@SuppressWarnings("unchecked")
- @Nullable public DDF getmember(@Nonnull @NotEmpty final String path) {
+ @Nonnull public DDF getmember(@Nonnull @NotEmpty final String path) {
final String[] tokens = path.split("\\.");
- if (tokens == null || tokens.length == 0) {
- return null;
+ if (tokens == null || tokens.length == 0 || isnull()) {
+ return new DDF();
}
DDF current = this;
@@ -819,30 +835,39 @@ public class DDF {
if (islist() && index < ((List<DDF>) current.value).size()) {
current = ((List<DDF>) current.value).get(index);
} else {
- return null;
+ return new DDF();
}
i++;
} else if (current.isstruct()) {
// Access the named element and advance the path.
current = ((Map<String,DDF>) current.value).get(tokens[i]);
if (current == null) {
- return null;
+ return new DDF();
}
i++;
} else if (current.islist()) {
// Access first element of list, don't advance the path.
current = ((List<DDF>) current.value).get(0);
if (current == null) {
- return null;
+ return new DDF();
}
} else {
- return null;
+ return new DDF();
}
}
return current;
}
+ /** {@inheritDoc} */
+ @Nonnull public Iterator<DDF> iterator() {
+ final List<DDF> list = asList();
+ if (list != null) {
+ return list.iterator();
+ }
+ return Collections.emptyListIterator();
+ }
+
/** {@inheritDoc} */
@Override
public boolean equals(final Object obj) {
diff --git a/src/test/java/net/shibboleth/utilities/java/support/ddf/DDFTest.java b/src/test/java/net/shibboleth/utilities/java/support/ddf/DDFTest.java
index 1989a9a..5270b08 100644
--- a/src/test/java/net/shibboleth/utilities/java/support/ddf/DDFTest.java
+++ b/src/test/java/net/shibboleth/utilities/java/support/ddf/DDFTest.java
@@ -21,6 +21,8 @@ import static org.testng.Assert.*;
import org.testng.annotations.Test;
+import net.shibboleth.utilities.java.support.collection.Pair;
+
/**
* DDF unit tests.
*/
@@ -31,7 +33,7 @@ public class DDFTest {
public void testConstructors() {
DDF obj = new DDF();
assertNull(obj.name());
- assertTrue(obj.isempty());
+ assertTrue(obj.isnull());
obj = new DDF("foo");
assertEquals(obj.name(), "foo");
@@ -89,5 +91,73 @@ public class DDFTest {
assertEquals(obj.integer(), Integer.valueOf(42));
assertEquals(obj.floating(), Double.valueOf(42.42));
}
+
+ @Test
+ public void testLists() {
+ final DDF obj = new DDF().list();
+ assertTrue(obj.islist());
+ assertEquals(obj.integer(), Integer.valueOf(0));
+
+ obj.add(new DDF("foo", "bar"));
+ obj.add(new DDF("foo2", 42));
+ obj.add(new DDF("foo3").pointer(new Pair<>()));
+ assertEquals(obj.integer(), Integer.valueOf(3));
+
+ for (final DDF el : obj) {
+ switch (el.name()) {
+ case "foo":
+ assertEquals(el.string(), "bar");
+ break;
+
+ case "foo2":
+ assertEquals(el.integer(), Integer.valueOf(42));
+ break;
+
+ case "foo3":
+ assertEquals(el.pointer(), new Pair<>());
+ break;
+
+ default:
+ fail("Node unrecognized");
+ }
+ }
+
+ assertEquals(obj.getmember("[0]"), new DDF("foo", "bar"));
+ assertEquals(obj.getmember("[1]"), new DDF("foo2", 42));
+ assertTrue(obj.getmember("[3]").isnull());
+
+ obj.addafter(new DDF(null), obj.getmember("[0]"));
+ assertEquals(obj.integer(), Integer.valueOf(4));
+ assertTrue(obj.getmember("[1]").isempty());
+ obj.addbefore(new DDF("foo4"), obj.getmember("[2]"));
+ assertEquals(obj.integer(), Integer.valueOf(5));
+ assertTrue(obj.getmember("[2]").name().equals("foo4"));
+
+ assertTrue(obj.asList().get(4).remove().ispointer());
+ assertEquals(obj.integer(), Integer.valueOf(4));
+ }
+
+ @Test
+ public void testStructures() {
+ final DDF obj = new DDF().structure();
+ assertTrue(obj.isstruct());
+ assertEquals(obj.integer(), Integer.valueOf(0));
+
+ obj.add(new DDF("foo", "bar"));
+ assertEquals(obj.integer(), Integer.valueOf(1));
+ assertTrue(obj.getmember("foo").name().equals("foo"));
+ assertTrue(obj.getmember("foo").string().equals("bar"));
+
+ obj.addmember("foo2").integer(42);
+ assertEquals(obj.integer(), Integer.valueOf(2));
+
+ obj.addmember("foo2.foo3").string("bar3");
+ assertEquals(obj.integer(), Integer.valueOf(2));
+ assertTrue(obj.getmember("foo2").isstruct());
+ assertEquals(obj.getmember("foo2").integer(), Integer.valueOf(1));
+ assertTrue(obj.getmember("foo2").getmember("foo3").string().equals("bar3"));
+ assertTrue(obj.getmember("foo2.foo3").string().equals("bar3"));
+ }
+
}
\ No newline at end of file
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list