[java-support] 01/01: Work in progress.

Scott Cantor cantor.2 at osu.edu
Fri May 14 19:33:21 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=43342147a08d251d315f731db1792ec2a71f32f3

commit 43342147a08d251d315f731db1792ec2a71f32f3
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Fri May 14 15:33:02 2021 -0400

    Work in progress.
---
 .../shibboleth/utilities/java/support/ddf/DDF.java | 898 +++++++++++++++++++++
 .../utilities/java/support/ddf/package-info.java   |  28 +
 .../utilities/java/support/ddf/DDFTest.java        |  93 +++
 3 files changed, 1019 insertions(+)

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
new file mode 100644
index 0000000..8e75e06
--- /dev/null
+++ b/src/main/java/net/shibboleth/utilities/java/support/ddf/DDF.java
@@ -0,0 +1,898 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.utilities.java.support.ddf;
+
+import java.util.ArrayList;
+import java.util.Collections;
+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;
+import javax.annotation.concurrent.NotThreadSafe;
+
+import net.shibboleth.utilities.java.support.annotation.constraint.NonNegative;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotLive;
+import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/**
+ * The core object in the DDF mode, this is a node in a tree of objects that
+ * make up the entire data structure.
+ * 
+ * <p>Each node contains references to its parant and children, if any,
+ * as well as data type and possibly a value if a leaf node.</p>
+ * 
+ * <p>Most of the types are self-explanatory, but strings may be "safe" or "unsafe".
+ * Safe strings are understood to be Unicode that can be safely converted between UTF-8
+ * and UTF-16. Unsafe strings are represented as Java String objects but have an unknown
+ * character encoding so the individual code points above 127 are essentially undefined
+ * and cannot be assumed to represent the "correct" value. They may only be compared
+ * with other values that are understood to represent the same range of values.</p> 
+ * 
+ * <p>The method names do not align to normal Java conventions for compatibility with
+ * the other version(s) of the same API.</p>
+ */
+ at NotThreadSafe
+public class DDF {
+
+    /** Name of node. */
+    @Nullable private String name;
+    
+    /** Parent node. */
+    @Nullable private DDF parent;
+
+    /** Type enum. */
+    public enum DDFType {
+
+        /** An empty node with no value. */
+        DDF_EMPTY(0),
+        
+        /** A string value. */
+        DDF_STRING(1),
+        
+        /** An integral value. */
+        DDF_INT(2),
+        
+        /** A floating point value. */
+        DDF_FLOAT(3),
+        
+        /** A structure with named children. */
+        DDF_STRUCT(4),
+        
+        /** An ordered list. */
+        DDF_LIST(5),
+        
+        /** A reference to any object. */
+        DDF_POINTER(6),
+        
+        /** A string that cannot be assumed to be UTF-8 (see above docs). */
+        DDF_STRING_UNSAFE(7);
+        
+        /** Type value. */
+        private final int value;
+        
+        /**
+         * Constructor.
+         * 
+         * @param val value of the type enum
+         */
+        private DDFType(@NonNegative final int val) {
+            value = val;
+        }
+        
+        /**
+         * Get the type value.
+         * 
+         * @return type value
+         */
+        public int getValue() {
+            return value;
+        }
+        
+    };
+    
+    /** Node type. */
+    @Nonnull private DDFType type;
+    
+    /** Reference to the value, which depends on the type. */
+    @Nullable private Object value;
+    
+    /** Constructor. */
+    public DDF() {
+        type = DDFType.DDF_EMPTY;
+    }
+
+    /**
+     * Constructor.
+     *
+     * <p>For compatibility, the name is constrained to <= 255 characters.</p>
+     *
+     * @param n node name
+     */
+    public DDF(@Nullable @NotEmpty final String n) {
+        this();
+        name(n);
+    }
+
+    /**
+     * Constructor.
+     *
+     * <p>For compatibility, the name is constrained to <= 255 characters.</p>
+     *
+     * @param n node name
+     * @param val string value, assumed to be "safe" Unicode
+     */
+    public DDF(@Nullable @NotEmpty final String n, @Nullable final String val) {
+        this(n);
+        string(val);
+    }
+
+    /**
+     * Constructor.
+     *
+     * <p>For compatibility, the name is constrained to <= 255 characters.</p>
+     *
+     * @param n node name
+     * @param val integer value
+     */
+    public DDF(@Nullable @NotEmpty final String n, final int val) {
+        this(n);
+        integer(val);
+    }
+
+    /**
+     * Constructor.
+     *
+     * <p>For compatibility, the name is constrained to <= 255 characters.</p>
+     *
+     * @param n node name
+     * @param val floating value
+     */
+    public DDF(@Nullable @NotEmpty final String n, final double val) {
+        this(n);
+        floating(val);
+    }
+
+    /**
+     * Constructor.
+     *
+     * <p>For compatibility, the name is constrained to <= 255 characters.</p>
+     *
+     * @param n node name
+     * @param val object value
+     */
+    public DDF(@Nullable @NotEmpty final String n, @Nullable final Object val) {
+        this(n);
+        pointer(val);
+    }
+
+    /**
+     * Destroys a node's content, resets it to a null object and clears its name.
+     * 
+     * <p>This is primarily for tree maintenance, given the lack of need for explicit
+     * memory management.</p>
+     * 
+     * @return this object
+     */
+    @Nonnull public DDF destroy() {
+        remove().empty().name(null);
+        type = null;
+        return this;
+    }
+    
+    /**
+     * Performs a deep copy of the node and all children, if any.
+     * 
+     * @return the copy
+     */
+    @SuppressWarnings("unchecked")
+    @Nonnull DDF copy() {
+        final DDF dup = new DDF(name);
+        
+        switch (type) {
+            case DDF_EMPTY:
+                break;
+                
+            case DDF_STRING:
+                dup.string((String) value);
+                break;
+                
+            case DDF_STRING_UNSAFE:
+                dup.unsafe_string((String) value);
+                break;
+                
+            case DDF_INT:
+                dup.integer((Integer) value);
+                break;
+                
+            case DDF_FLOAT:
+                dup.floating((Double) value);
+                break;
+                
+            case DDF_STRUCT:
+                dup.structure();
+                for (final DDF ddf : ((Map<String,DDF>) value).values()) {
+                    dup.add(ddf.copy());
+                }
+                break;
+                
+            case DDF_LIST:
+                dup.list();
+                for (final DDF ddf : (List<DDF>) value) {
+                    dup.add(ddf.copy());
+                }
+                break;
+                
+            default:
+        }
+        
+        return dup;
+    }
+    
+    /**
+     * Get the node name.
+     * 
+     * @return the name
+     */
+    @Nullable public String name() {
+        return name;
+    }
+    
+    /**
+     * Set the node name.
+     * 
+     * <p>For compatibility, the name is constrained to <= 255 characters.</p>
+     * 
+     * <p>The name will not be set if the node is already a child of a structure.</p>
+     * 
+     * @param n the new name
+     * 
+     * @return this object
+     */
+    @Nonnull public DDF name(@Nullable @NotEmpty final String n) {
+        if (parent == null || !parent.isstruct()) {
+            if (n != null) {
+                name = Constraint.isNotEmpty(n.substring(0,Integer.min(n.length(), 255)), "Name cannot be empty");
+            } else {
+                name = null;
+            }
+        }
+        return this;
+    }
+    
+    /**
+     * Returns true iff the node is empty.
+     * 
+     * @return true iff the node is empty
+     */
+    public boolean isempty() {
+        return type == DDFType.DDF_EMPTY;
+    }
+
+    /**
+     * Returns true iff the node is a string (safe or not).
+     * 
+     * @return true iff the node is a string (safe or not)
+     */
+    public boolean isstring() {
+        return type == DDFType.DDF_STRING || type == DDFType.DDF_STRING_UNSAFE;
+    }
+
+    /**
+     * Returns true iff the node is an integer.
+     * 
+     * @return true iff the node is an integer
+     */
+    public boolean isint() {
+        return type == DDFType.DDF_INT;
+    }
+
+    /**
+     * Returns true iff the node is a floating point.
+     * 
+     * @return true iff the node is a floating point
+     */
+    public boolean isfloat() {
+        return type == DDFType.DDF_FLOAT;
+    }
+
+    /**
+     * Returns true iff the node is a structure.
+     * 
+     * @return true iff the node is a structure
+     */
+    public boolean isstruct() {
+        return type == DDFType.DDF_STRUCT;
+    }
+
+    /**
+     * Returns true iff the node is a list/array.
+     * 
+     * @return true iff the node is a list/array.
+     */
+    public boolean islist() {
+        return type == DDFType.DDF_LIST;
+    }
+
+    /**
+     * Returns true iff the node is a pointer (i.e., object reference).
+     * 
+     * @return true iff the node is a pointer (i.e., object reference)
+     */
+    public boolean ispointer() {
+        return type == DDFType.DDF_POINTER;
+    }
+
+    /**
+     * Get the string value of this node.
+     * 
+     * <p>The string value of a non-string value is null.</p>
+     * 
+     * @return the string value or null
+     */
+    @Nullable public String string() {
+        return isstring() ? (String) value : null;
+    }
+
+    /**
+     * Get the integer value of this node.
+     * 
+     * <p>Integers are coerced from other types based on numeric conversions
+     * or the count of a structure or list.</p> 
+     * 
+     * @return the integer value or null
+     */
+    @Nullable public Integer integer() {
+        
+        switch(type) {
+            case DDF_INT:
+                return (Integer) value;
+            case DDF_FLOAT:
+                return ((Double) value).intValue();
+            case DDF_STRING:
+                try {
+                    return Integer.valueOf((String) value);
+                } catch (final NumberFormatException e) {
+                    // Swallow.
+                    return null;
+                }
+            case DDF_STRUCT:
+                return ((Map<?,?>) value).size();
+            case DDF_LIST:
+                return ((List<?>) value).size();
+            default:
+                break;
+        }
+        
+        return null;
+    }
+    
+    /**
+     * Get the floating point value of this node.
+     * 
+     * <p>Doubles are coerced from other types based on numeric conversions
+     * or the count of a structure or list.</p> 
+     *
+     * @return the floating point value or null
+     */
+    @Nullable public Double floating() {
+        
+        switch(type) {
+            case DDF_INT:
+                return ((Integer) value).doubleValue();
+            case DDF_FLOAT:
+                return (Double) value;
+            case DDF_STRING:
+                try {
+                    return Double.valueOf((String) value);
+                } catch (final NumberFormatException e) {
+                    // Swallow.
+                    return null;
+                }
+            case DDF_STRUCT:
+                return (double) ((Map<?,?>) value).size();
+            case DDF_LIST:
+                return (double) ((List<?>) value).size();
+            default:
+                break;
+        }
+        
+        return null;
+    }
+
+    /**
+     * Get the pointer/reference value of this node, which is just an {@link Object}.
+     * 
+     * @return pointer/reference value or null
+     */
+    @Nullable public Object pointer() {
+        return ispointer() ? value : null;
+    }
+
+
+    /**
+     * Converts this node to an empty type/value.
+     * 
+     * <p>All children should be considered disposed of, though in Java this is
+     * circumventable by means of maintaining references to them.</p>
+     * 
+     * @return this object
+     */
+    @Nonnull public DDF empty() {
+        type = DDFType.DDF_EMPTY;
+        value = null;
+        return this;
+    }
+
+    /**
+     * Converts this node to a string type/value.
+     * 
+     * @param val the value to inject
+     * 
+     * @return this object
+     */
+    @Nonnull public DDF string(@Nullable final String val) {
+        empty();
+        value = val;
+        type = DDFType.DDF_STRING;
+        return this;
+    }
+
+    /**
+     * Converts this node to an unsafe string type/value.
+     * 
+     * @param val the value to inject
+     * 
+     * @return this object
+     */
+// Checkstyle: MethodName OFF
+    @Nonnull public DDF unsafe_string(@Nullable final String val) {
+        empty();
+        value = val;
+        type = DDFType.DDF_STRING_UNSAFE;
+        return this;
+    }
+// Checkstyle: MethodName ON
+
+    /**
+     * Converts this node to a string type/value based on the converted form of the input.
+     * 
+     * @param val input value
+     * 
+     * @return this object
+     */
+    @Nonnull public DDF string(final int val) {
+        return string(Integer.toString(val));
+    }
+
+    /**
+     * Converts this node to a string type/value based on the converted form of the input.
+     * 
+     * @param val input value
+     * 
+     * @return this object
+     */
+    @Nonnull public DDF string(final double val) {
+        return string(Double.toString(val));
+    }
+
+    /**
+     * Converts this node to an integer type/value.
+     * 
+     * @param val value to inject
+     * 
+     * @return this object
+     */
+    @Nonnull public DDF integer(final int val) {
+        empty();
+        value = Integer.valueOf(val);
+        type = DDFType.DDF_INT;
+        return this;
+    }
+
+    /**
+     * Converts this node to an integer type/value based on the converted form of the input.
+     * 
+     * <p>A conversion error will assign zero as the value.</p>
+     * 
+     * @param val value to inject
+     * 
+     * @return this object
+     */
+    @Nonnull public DDF integer(@Nonnull @NotEmpty final String val) {
+        empty();
+        try {
+            return integer(Integer.valueOf(val));
+        } catch (final NumberFormatException e) {
+            return integer(0);
+        }
+    }
+
+    /**
+     * Converts this node to an floating point type/value.
+     * 
+     * @param val value to inject
+     * 
+     * @return this object
+     */
+    @Nonnull public DDF floating(final double val) {
+        empty();
+        value = Double.valueOf(val);
+        type = DDFType.DDF_FLOAT;
+        return this;
+    }
+
+    /**
+     * Converts this node to a floating point type/value based on the converted form of the input.
+     * 
+     * <p>A conversion error will assign zero as the value.</p>
+     * 
+     * @param val value to inject
+     * 
+     * @return this object
+     */
+    @Nonnull public DDF floating(@Nonnull @NotEmpty final String val) {
+        empty();
+        try {
+            return floating(Double.valueOf(val));
+        } catch (final NumberFormatException e) {
+            return floating(0.0);
+        }
+    }
+
+    /**
+     * Converts this node to a structure.
+     * 
+     * @return this object
+     */
+    @Nonnull public DDF structure() {
+        empty();
+        value = new LinkedHashMap<String,DDF>();
+        type = DDFType.DDF_STRUCT;
+        return this;
+    }
+
+    /**
+     * Converts this node to a list/array.
+     * 
+     * @return this object
+     */
+    @Nonnull public DDF list() {
+        empty();
+        value = new ArrayList<DDF>();
+        type = DDFType.DDF_LIST;
+        return this;
+    }
+
+    /**
+     * Converts this node to a pointer/reference type.
+     * 
+     * @param val value to inject
+     * 
+     * @return this object
+     */
+    @Nonnull public DDF pointer(@Nonnull final Object val) {
+        empty();
+        value = val;
+        type = DDFType.DDF_POINTER;
+        return this;
+    }
+
+    /**
+     * Adds a node to the end of a struct or list and returns it.
+     * 
+     * <p>If this node is not a struct or list or the child is a null node, then it is returned
+     * with no further action.</p>
+     * 
+     * <p>If this node is a struct with an existing member by the same name, the input
+     * replaces that member.</p>
+     * 
+     * @param child the child to add
+     * 
+     * @return the child
+     */
+    @SuppressWarnings("unchecked")
+    @Nonnull public DDF add(@Nonnull final DDF child) {
+        if ((!isstruct() && !islist()) || this == child.parent) {
+            return child;
+        }
+
+        if (isstruct()) {
+            if (child.name == null) {
+                return child;
+            }
+            getmember(child.name).destroy();
+            child.remove();
+            ((Map<String,DDF>) value).put(child.name, child);
+        } else {
+            child.remove();
+            ((List<DDF>) value).add(child);
+        }
+
+        child.parent = this;
+        return child;
+    }
+
+    /**
+     * Adds a node to a list prior to a specified node.
+     * 
+     * <p>If this node is not a list, does not contain the second parameter, or
+     * either parameter is a null node, then the first parameter is returned with
+     * no further action.</p> 
+     * 
+     * @param child the child to add
+     * @param before the node to insert the child before
+     * 
+     * @return the child
+     */
+    @Nonnull public DDF addbefore(@Nonnull final DDF child, @Nonnull final DDF before) {
+        if (!islist() || before.parent != this) {
+            return child;
+        }
+
+        child.remove();
+        @SuppressWarnings("unchecked")
+        final List<DDF> list = (List<DDF>) value;
+        list.add(list.indexOf(before), child);
+        child.parent = this;
+        return child;
+    }
+
+    /**
+     * Adds a node to a list after a specified node.
+     * 
+     * <p>If this node is not a list, does not contain the second parameter, or
+     * either parameter is a null node, then the first parameter is returned with
+     * no further action.</p> 
+     * 
+     * @param child the child to add
+     * @param after the node to insert the child after
+     * 
+     * @return the child
+     */
+    @Nonnull public DDF addafter(@Nonnull final DDF child, @Nonnull final DDF after) {
+        if (!islist() || after.parent != this) {
+            return child;
+        }
+
+        child.remove();
+        
+        @SuppressWarnings("unchecked")
+        final List<DDF> list = (List<DDF>) value;
+        
+        final int i = list.indexOf(after);
+        if (i == list.size() - 1) {
+            list.add(child);
+        } else {
+            list.add(i + 1, child);
+        }
+        child.parent = this;
+        
+        return child;
+    }
+    
+    /**
+     * Isolate this object from its surrounding nodes and return it.
+     * 
+     * @return this object
+     */
+    @SuppressWarnings("unchecked")
+    @Nonnull public DDF remove() {
+        if (parent != null) {
+            if (parent.isstruct()) {
+                ((Map<String,DDF>) parent.value).remove(name);
+            } else {
+                ((List<DDF>) parent.value).remove(this);
+            }
+            
+            parent = null;
+        }
+        return this;
+    }
+    
+    /**
+     * Get the parent node.
+     * 
+     * @return parent node
+     */
+    @Nullable public DDF parent() {
+        return parent;
+    }
+    
+    /**
+     * Expose an immutable map representing a structure or list node.
+     * 
+     * @return immutable map, or null
+     */
+    @SuppressWarnings("unchecked")
+    @Nonnull @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();
+    }
+
+    /**
+     * Expose an immutable list representing a structure or list node.
+     * 
+     * @return immutable list, or null
+     */
+    @SuppressWarnings("unchecked")
+    @Nonnull @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();
+    }
+
+    /**
+     * Adds a new empty node to a structure, possibly creating nested structures based
+     * on dotted path notation (existing nodes matching the path segments are not altered
+     * other than to convert them to structures).
+     * 
+     * <p>The input path MUST contain at least one non-empty path segment.</p>
+     * 
+     * <p>This node will be converted to a structure if not already one.</p>
+     * 
+     * @param path dotted path to use
+     * 
+     * @return the last node added to the nested tree
+     */
+    @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));
+            }
+            
+            base = node;
+        }
+        
+        return base;
+    }
+
+    /**
+     * 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.
+     * 
+     * @param path dotted path to use
+     * 
+     * @return the matching node, or null
+     */
+// Checkstyle: CyclomaticComplexity OFF
+    @SuppressWarnings("unchecked")
+    @Nullable public DDF getmember(@Nonnull @NotEmpty final String path) {
+        final String[] tokens = path.split("\\.");
+        if (tokens == null || tokens.length == 0) {
+            return null;
+        }
+
+        DDF current = this;
+
+        for (int i = 0; i < tokens.length;) {
+            if (tokens[i].startsWith("[") && tokens[i].endsWith("]")) {
+                // Attempt to access a list entry via [n] notation and advance the path.
+                int index;
+                try {
+                    index = Integer.valueOf(tokens[i].substring(1, tokens[i].length() - 1));
+                } catch(final NumberFormatException e) {
+                    index = 0;
+                }
+                if (islist() && index < ((List<DDF>) current.value).size()) {
+                    current = ((List<DDF>) current.value).get(index);
+                } else {
+                    return null;
+                }
+                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;
+                }
+                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;
+                }
+            } else {
+                return null;
+            }
+        }
+        
+        return current;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        
+        if (obj == null) {
+            return false;
+        }
+        
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        
+        final DDF other = (DDF) obj;
+        if (name == null) {
+            if (other.name != null) {
+                return false;
+            }
+        } else if (!name.equals(other.name)) {
+            return false;
+        }
+        
+        if (type != other.type) {
+            return false;
+        }
+        
+        if (value == null) {
+            if (other.value != null) {
+                return false;
+            }
+        } else if (!value.equals(other.value)) {
+            return false;
+        }
+        
+        return true;
+    }
+// Checkstyle: CyclomaticComplexity ON
+        
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((name == null) ? 0 : name.hashCode());
+        result = prime * result + ((parent == null) ? 0 : parent.hashCode());
+        result = prime * result + ((type == null) ? 0 : type.hashCode());
+        result = prime * result + ((value == null) ? 0 : value.hashCode());
+        return result;
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/net/shibboleth/utilities/java/support/ddf/package-info.java b/src/main/java/net/shibboleth/utilities/java/support/ddf/package-info.java
new file mode 100644
index 0000000..2cf59d4
--- /dev/null
+++ b/src/main/java/net/shibboleth/utilities/java/support/ddf/package-info.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Implementation of the Dynamic Dataflow abstraction used in the
+ * Service Provider for interprocess communication.
+ * 
+ * <p>DDFs are somewhat JSON-like, but with a (subjectively) friendlier
+ * API and allow for arbitrary serialization. They support dynamic RPC
+ * interfaces that don't require pre-definition of a data contract or
+ * compilation of client/server stubs.</p>
+ */
+
+package net.shibboleth.utilities.java.support.ddf;
\ No newline at end of file
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
new file mode 100644
index 0000000..1989a9a
--- /dev/null
+++ b/src/test/java/net/shibboleth/utilities/java/support/ddf/DDFTest.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.utilities.java.support.ddf;
+
+import static org.testng.Assert.*;
+
+import org.testng.annotations.Test;
+
+/**
+ * DDF unit tests.
+ */
+ at SuppressWarnings("javadoc")
+public class DDFTest {
+
+    @Test
+    public void testConstructors() {
+        DDF obj = new DDF();
+        assertNull(obj.name());
+        assertTrue(obj.isempty());
+        
+        obj = new DDF("foo");
+        assertEquals(obj.name(), "foo");
+        assertTrue(obj.isempty());
+        
+        obj = new DDF("foo", "bar");
+        assertEquals(obj.name(), "foo");
+        assertTrue(obj.isstring());
+        assertEquals(obj.string(), "bar");
+
+        obj = new DDF("foo", 42);
+        assertEquals(obj.name(), "foo");
+        assertTrue(obj.isint());
+        assertEquals(obj.integer(), Integer.valueOf(42));
+
+        obj = new DDF("foo", 42.42);
+        assertEquals(obj.name(), "foo");
+        assertTrue(obj.isfloat());
+        assertEquals(obj.floating(), Double.valueOf(42.42));
+    }
+
+    @Test
+    public void testConversions() {
+        final DDF obj = new DDF("foo");
+        obj.string("bar");
+        assertTrue(obj.isstring());
+        assertEquals(obj.string(), "bar");
+        assertNull(obj.integer());
+        assertNull(obj.floating());
+        
+        obj.string(42);
+        assertTrue(obj.isstring());
+        assertEquals(obj.string(), "42");
+        assertEquals(obj.integer(), Integer.valueOf(42));
+        assertEquals(obj.floating(), Double.valueOf(42));
+
+        obj.string(42.42);
+        assertTrue(obj.isstring());
+        assertEquals(obj.string(), "42.42");
+        assertNull(obj.integer());
+        assertEquals(obj.floating(), Double.valueOf(42.42));
+        
+        obj.integer(42);
+        assertTrue(obj.isint());
+        assertEquals(obj.integer(), Integer.valueOf(42));
+        assertEquals(obj.floating(), Double.valueOf(42));
+        
+        obj.integer("42");
+        assertTrue(obj.isint());
+        assertEquals(obj.integer(), Integer.valueOf(42));
+        assertEquals(obj.floating(), Double.valueOf(42));
+        
+        obj.floating(42.42);
+        assertTrue(obj.isfloat());
+        assertEquals(obj.integer(), Integer.valueOf(42));
+        assertEquals(obj.floating(), Double.valueOf(42.42));
+    }
+
+}
\ 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