com.unboundid.scim2.common.GenericScimResource.java Source code

Java tutorial

Introduction

Here is the source code for com.unboundid.scim2.common.GenericScimResource.java

Source

/*
 * Copyright 2015-2017 UnboundID Corp.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License (GPLv2 only)
 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses>.
 */

package com.unboundid.scim2.common;

import com.fasterxml.jackson.core.Base64Variants;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.BooleanNode;
import com.fasterxml.jackson.databind.node.DoubleNode;
import com.fasterxml.jackson.databind.node.IntNode;
import com.fasterxml.jackson.databind.node.LongNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import com.unboundid.scim2.common.exceptions.ScimException;
import com.unboundid.scim2.common.exceptions.ServerErrorException;
import com.unboundid.scim2.common.types.Meta;
import com.unboundid.scim2.common.utils.GenericScimObjectDeserializer;
import com.unboundid.scim2.common.utils.GenericScimObjectSerializer;
import com.unboundid.scim2.common.utils.JsonUtils;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

/**
 * <p>A generic SCIM object.  This object can be used if you have no
 * Java object representing the SCIM object being returned.</p>
 *
 * <p>This object can be used when the exact structure of the SCIM object
 * that will be received as JSON text is not known.  This will provide
 * methods that can read attributes from those objects without needing
 * to know the schema ahead of time.  Another way to work with SCIM
 * objects is when you know ahead of time what the schema will be.  In
 * that case you could still use this object, but {@link BaseScimResource}
 * might be a better choice.</p>
 *
 * <p>If you have a BaseScimResource derived object, you can always get a
 * {@link GenericScimResource} by calling {@link #asGenericScimResource()}.
 * You could also go the other way by calling
 * {@link GenericScimResource#getObjectNode()}, followed by
 * {@link JsonUtils#nodeToValue(JsonNode, Class)}.</p>
 *
 * @see BaseScimResource
 */
@JsonDeserialize(using = GenericScimObjectDeserializer.class)
@JsonSerialize(using = GenericScimObjectSerializer.class)
public final class GenericScimResource implements ScimResource {
    private static final Path SCHEMAS = Path.root().attribute("schemas");
    private static final Path ID = Path.root().attribute("id");
    private static final Path EXTERNAL_ID = Path.root().attribute("externalId");
    private static final Path META = Path.root().attribute("meta");

    private final ObjectNode objectNode;

    /**
     * Create a new empty GenericScimResource.
     */
    public GenericScimResource() {
        this.objectNode = JsonUtils.getJsonNodeFactory().objectNode();
    }

    /**
     * Create a new GenericScimResource backed by an ObjectNode.
     *
     * @param objectNode The ObjectNode that backs this object.
     */
    public GenericScimResource(final ObjectNode objectNode) {
        this.objectNode = objectNode;
    }

    /**
     * Gets the <code>ObjectNode</code> that backs this object.
     * @return a <code>ObjectNode</code>.
     */
    public ObjectNode getObjectNode() {
        return this.objectNode;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Meta getMeta() {
        try {
            List<JsonNode> values = JsonUtils.findMatchingPaths(META, objectNode);
            if (values.isEmpty()) {
                return null;
            }
            return JsonUtils.nodeToValue(values.get(0), Meta.class);
        } catch (Exception e) {
            // This should never happen.
            throw new RuntimeException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setMeta(final Meta meta) {
        try {
            JsonUtils.replaceValue(META, objectNode, JsonUtils.valueToNode(meta));
        } catch (Exception e) {
            // This should never happen.
            throw new RuntimeException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getId() {
        try {
            JsonNode value = JsonUtils.getValue(ID, objectNode);
            if (value.isNull()) {
                return null;
            }
            return JsonUtils.nodeToValue(value, String.class);
        } catch (Exception e) {
            // This should never happen.
            throw new RuntimeException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setId(final String id) {
        try {
            JsonUtils.replaceValue(ID, objectNode, JsonUtils.valueToNode(id));
        } catch (Exception e) {
            // This should never happen.
            throw new RuntimeException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    public List<String> getSchemaUrns() {
        try {
            JsonNode value = JsonUtils.getValue(SCHEMAS, objectNode);
            if (value.isNull() || !value.isArray()) {
                return Collections.emptyList();
            }
            return JsonUtils.nodeToValues((ArrayNode) value, String.class);
        } catch (Exception e) {
            // This should never happen.
            throw new RuntimeException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    public void setSchemaUrns(final Collection<String> schemaUrns) {
        try {
            JsonUtils.replaceValue(SCHEMAS, objectNode, JsonUtils.valueToNode(schemaUrns));
        } catch (Exception e) {
            // This should never happen.
            throw new RuntimeException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getExternalId() {
        try {
            JsonNode value = JsonUtils.getValue(EXTERNAL_ID, objectNode);
            if (value.isNull()) {
                return null;
            }
            return JsonUtils.nodeToValue(value, String.class);
        } catch (Exception e) {
            // This should never happen.
            throw new RuntimeException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setExternalId(final String externalId) {
        try {
            JsonUtils.replaceValue(EXTERNAL_ID, objectNode, JsonUtils.valueToNode(externalId));
        } catch (ScimException e) { // This should never happen.
            throw new RuntimeException(e);
        }
    }

    /**
     * Gets a single JsonNode from a generic SCIM resource.  This value may
     * be an ArrayNode.
     *   <p>
     *
     * For example:
     * With a generic SCIM resource representing the folowing JSON:
     * <pre><code>
     *   {
     *     "name" : "Bob",
     *     "friends" : [ "Amy", "Beth", "Carol" ]
     *   }
     * </code></pre>
     * <p>
     * gsr.getValue("name");
     * would return a TextNode containing "Bob"
     *   <p>
     *
     * gsr.getValue("friends");
     * would return an ArrayNode containing 3 TextNodes with the values
     * "Amy", "Beth", and "Carol"
     *
     * @param path the String path of the object.
     * @return the JsonNode at the path, or a NullNode if nothing is found
     * @throws ScimException thrown if an error occurs.
     */
    public JsonNode getValue(final String path) throws ScimException {
        return getValue(Path.fromString(path));
    }

    /**
     * Gets a single JsonNode from a generic SCIM resource.  This value may
     * be an ArrayNode.
     *   <p>
     *
     * For example:
     * With a generic SCIM resource representing the folowing JSON:
     * <pre><code>
     *   {
     *     "name" : "Bob",
     *     "friends" : [ "Amy", "Beth", "Carol" ]
     *   }
     * </code></pre>
     *   <p>
     *
     * gsr.getValue("name");
     * would return a TextNode containing "Bob"
     *   <p>
     *
     * gsr.getValue("friends");
     * would return an ArrayNode containing 3 TextNodes with the values
     * "Amy", "Beth", and "Carol"
     *
     * @param path the path of the object.
     * @return the JsonNode at the path, or a NullNode if nothing is found
     * @throws ScimException thrown if an error occurs.
     */
    public JsonNode getValue(final Path path) throws ScimException {
        return JsonUtils.getValue(path, objectNode);
    }

    /**
     * Update the value at the provided path. Equivalent to using the
     * {@link JsonUtils#replaceValue(Path, ObjectNode, JsonNode)} method:
     * JsonUtils.replaceValues(Path.fromString(path), getObjectNode(), value).
     *   <p>
     *
     * The {@link JsonUtils#valueToNode(Object)} method may be used to convert
     * the given value instance to a JSON node.
     *   <p>
     *
     * @param path The path to the attribute whose value to set.
     * @param value The value(s) to set.
     * @return This object.
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource replaceValue(final String path, final JsonNode value) throws ScimException {
        replaceValue(Path.fromString(path), value);
        return this;
    }

    /**
     * Update the value at the provided path. Equivalent to using the
     * {@link JsonUtils#replaceValue(Path, ObjectNode, JsonNode)} method:
     * JsonUtils.replaceValues(path, getObjectNode(), value).
     *   <p>
     *
     * The {@link JsonUtils#valueToNode(Object)} method may be used to convert
     * the given value instance to a JSON node.
     *   <p>
     *
     * @param path The path to the attribute whose value to set.
     * @param value The value(s) to set.
     * @return This object.
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource replaceValue(final Path path, final JsonNode value) throws ScimException {
        JsonUtils.replaceValue(path, objectNode, value);
        return this;
    }

    /**
     * Add new values at the provided path. Equivalent to using the
     * {@link JsonUtils#addValue(Path, ObjectNode, JsonNode)} method:
     * JsonUtils.addValue(Path.fromString(path), getObjectNode(), values).
     *   <p>
     *
     * The {@link JsonUtils#valueToNode(Object)} method may be used to convert
     * the given value instance to a JSON node.
     *   <p>
     *
     * @param path The path to the attribute whose values to add.
     * @param values The value(s) to add.
     * @return This object.
     * @throws ScimException If the path is invalid.
     */
    public GenericScimResource addValues(final String path, final ArrayNode values) throws ScimException {
        addValues(Path.fromString(path), values);
        return this;
    }

    /**
     * Add new values at the provided path. Equivalent to using the
     * {@link JsonUtils#addValue(Path, ObjectNode, JsonNode)} method:
     * JsonUtils.addValue(path, getObjectNode(), values).
     *   <p>
     *
     * The {@link JsonUtils#valueToNode(Object)} method may be used to convert
     * the given value instance to a JSON node.
     *   <p>
     *
     * @param path The path to the attribute whose values to add.
     * @param values The value(s) to add.
     * @return This object.
     * @throws ScimException If the path is invalid.
     */
    public GenericScimResource addValues(final Path path, final ArrayNode values) throws ScimException {
        JsonUtils.addValue(path, objectNode, values);
        return this;
    }

    /**
     * Removes values at the provided path. Equivalent
     * to using the {@link JsonUtils#removeValues(Path, ObjectNode)} method:
     * JsonUtils.removeValue(Path.fromString(path), getObjectNode(), values).
     *
     * @param path The path to the attribute whose values to remove.
     * @return Whether one or more values where removed.
     * @throws ScimException If the path is invalid.
     */
    public boolean removeValues(final String path) throws ScimException {
        return removeValues(Path.fromString(path));
    }

    /**
     * Removes values at the provided path. Equivalent
     * to using the {@link JsonUtils#removeValues(Path, ObjectNode)} method:
     * JsonUtils.removeValue(Path.fromString(path), getObjectNode(), values).
     *
     * @param path The path to the attribute whose values to remove.
     * @return Whether one or more values where removed.
     * @throws ScimException If the path is invalid.
     */
    public boolean removeValues(final Path path) throws ScimException {
        List<JsonNode> nodes = JsonUtils.removeValues(path, objectNode);
        return !nodes.isEmpty();
    }

    /**
     * {@inheritDoc}
     */
    public GenericScimResource asGenericScimResource() {
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        try {
            return JsonUtils.getObjectWriter().withDefaultPrettyPrinter().writeValueAsString(this);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    /////////////////////////////////////
    // SCIM String methods
    /////////////////////////////////////
    /**
     * Adds or replaces a String value in a generic SCIM resource.
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":"stringValue"
     * }
     * </code></pre>
     *   <p>
     *   gsr.replaceValue("path1", path1value)
     *   where path1value is a String
     *   would change the "path1" field to the value of the path1value
     *   variable
     *   <p>
     *
     *   gsr.replaceValue("path2", path2value)
     *   where path2value is a String
     *   would add a field called "path2" with the value of the path2value
     *   variable
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to replace the value for.
     * @param value the new value.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource replaceValue(final String path, final String value) throws ScimException {
        return replaceValue(Path.fromString(path), value);
    }

    /**
     * Adds or replaces a String value in a generic SCIM resource.
     *   <p>
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":"stringValue"
     * }
     * </code></pre>
     *   <p>
     *   gsr.replaceValue(Path.fromString("path1"), path1value)
     *   where path1value is a String
     *   would change the "path1" field to the value of the path1value
     *   variable
     *   <p>
     *
     *   gsr.replaceValue(Path.fromString("path2"), path2value)
     *   where path2value is a String
     *   would add a field called "path2" with the value of the path2value
     *   variable
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to replace the value for.
     * @param value the new value.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource replaceValue(final Path path, final String value) throws ScimException {
        return replaceValue(path, TextNode.valueOf(value));
    }

    /**
     * Adds String values to an array node.  If no array node exists at the
     * specified path, a new array node will be created.
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":[ "stringValue1", "stringValue2" ]
     * }
     * </code></pre>
     *   <p>
     *   gsr.addStringValues("path1", path1values)
     *   where path1Value is a List of String.
     *   Would add each of the items in the path1values list to the
     *   "path1" list in the generic SCIM resource
     *   <p>
     *
     *   gsr.addStringValues("path2", path2values)
     *   where path2values is a List of String.
     *   Would create a new array called "path2"
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to add the list to.
     * @param values a list containing the new values.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource addStringValues(final String path, final List<String> values) throws ScimException {
        return addStringValues(Path.fromString(path), values);
    }

    /**
     * Adds String values to an array node.  If no array node exists at the
     * specified path, a new array node will be created.
     *   <p>
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":[ "stringValue1", "stringValue2" ]
     * }
     * </code></pre>
     *   <p>
     *   gsr.addStringValues(Path.fromString("path1"), path1values)
     *   where path1Value is a List of String.
     *   Would add each of the items in the path1values list to the
     *   "path1" list in the generic SCIM resource
     *   <p>
     *
     *   gsr.addStringValues(Path.fromString("path2"), path2values)
     *   where path2values is a List of String.
     *   Would create a new array called "path2"
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to add the list to.
     * @param values a list containing the new values.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource addStringValues(final Path path, final List<String> values) throws ScimException {
        ArrayNode valuesArrayNode = JsonUtils.getJsonNodeFactory().arrayNode();
        for (String value : values) {
            valuesArrayNode.add(value);
        }

        return addValues(path, valuesArrayNode);
    }

    /**
     * Gets a String value from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a String.  If the path does not exist,
     * "{@code null}" will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":"stringValue1"
     *   }
     * </code></pre>
     *   <p>
     *
     *   getStringValue("path1")
     *   returns "stringValue1"
     *   <p>
     *
     *   getStringValue("bogusPath")
     *   returns null
     *
     * @param path the path to get the value from.
     * @return the value at the path, or null.
     * @throws ScimException thrown if an error occurs.
     */
    public String getStringValue(final String path) throws ScimException {
        return getStringValue(Path.fromString(path));
    }

    /**
     * Gets a String value from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a String.  If the path does not exist,
     * "{@code null}" will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":"stringValue1"
     *   }
     * </code></pre>
     *   <p>
     *
     *   getStringValue(Path.fromString("path1"))
     *   returns Stringexample
     *   <p>
     *
     *   getStringValue(Path.fromString("bogusPath"))
     *   returns null
     *
     * @param path the path to get the value from.
     * @return the value at the path, or null.
     * @throws ScimException thrown if an error occurs.
     */
    public String getStringValue(final Path path) throws ScimException {
        JsonNode jsonNode = getValue(path);
        return jsonNode.isNull() ? null : jsonNode.textValue();
    }

    /**
     * Gets a list of String from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a list of String.  If the path does
     * not exist, an empty list will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1": ["stringValue1", "stringValue2"]
     *   }
     * </code></pre>
     *   <p>
     *
     *   getStringValueList("path1")
     *   returns a list containing "stringValue1", "stringValue2"
     *   <p>
     *
     *   getStringValueList("bogusPath")
     *   returns an empty list
     *
     * @param path the path to get the value from.
     * @return the value at the path, or an empty list.
     * @throws ScimException thrown if an error occurs.
     */
    public List<String> getStringValueList(final String path) throws ScimException {
        return getStringValueList(Path.fromString(path));
    }

    /**
     * Gets a list of String from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a list of String.  If the path
     * does not exist, an empty list will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":["stringValue1", "stringValue2"]
     *   }
     * </code></pre>
     *   <p>
     *
     *   getStringValueList(Path.fromString("path1"))
     *   returns a list containing "stringValue1", "stringValue2"
     *   <p>
     *
     *   getStringValueList(Path.fromString("bogusPath"))
     *   returns an empty list
     *
     * @param path the path to get the value from.
     * @return the value at the path, or an empty list.
     * @throws ScimException thrown if an error occurs.
     */
    public List<String> getStringValueList(final Path path) throws ScimException {
        JsonNode valueNode = getValue(path);
        List<String> values = new ArrayList<String>();

        Iterator<JsonNode> iterator = valueNode.iterator();
        while (iterator.hasNext()) {
            values.add(iterator.next().textValue());
        }
        return values;
    }

    /////////////////////////////////////
    // SCIM Boolean methods
    /////////////////////////////////////
    /**
     * Adds or replaces a Boolean value in a generic SCIM resource.
     *   <p>
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":true
     * }
     * </code></pre>
     *   <p>
     *   gsr.replaceValue("path1", path1value)
     *   where path1value is a Boolean
     *   would change the "path1" field to the value of the path1value
     *   variable
     *   <p>
     *
     *   gsr.replaceValue("path2", path2value)
     *   where path2value is a Boolean
     *   would add a field called "path2" with the value of the path2value
     *   variable
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to replace the value for.
     * @param value the new value.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource replaceValue(final String path, final Boolean value) throws ScimException {
        return replaceValue(Path.fromString(path), value);
    }

    /**
     * Adds or replaces a Boolean value in a generic SCIM resource.
     *   <p>
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":true
     * }
     * </code></pre>
     *   <p>
     *   gsr.replaceValue(Path.fromString("path1"), path1value)
     *   where path1value is a Boolean
     *   would change the "path1" field to the value of the path1value
     *   variable
     *   <p>
     *
     *   gsr.replaceValue(Path.fromString("path2"), path2value)
     *   where path2value is a Boolean
     *   would add a field called "path2" with the value of the path2value
     *   variable
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *   <p>
     *
     * @param path the path to replace the value for.
     * @param value the new value.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource replaceValue(final Path path, final Boolean value) throws ScimException {
        return replaceValue(path, BooleanNode.valueOf(value));
    }

    /**
     * Adds Boolean values to an array node.  If no array node exists at the
     * specified path, a new array node will be created.
     *   <p>
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":[ true, false ]
     * }
     * </code></pre>
     *   <p>
     *   gsr.addBooleanValues("path1", path1values)
     *   where path1Value is a List of Boolean.
     *   Would add each of the items in the path1values list to the
     *   "path1" list in the generic SCIM resource
     *   <p>
     *
     *   gsr.addBooleanValues("path2", path2values)
     *   where path2values is a List of Boolean.
     *   Would create a new array called "path2"
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to add the list to.
     * @param values a list containing the new values.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource addBooleanValues(final String path, final List<Boolean> values)
            throws ScimException {
        return addBooleanValues(Path.fromString(path), values);
    }

    /**
     * Adds Boolean values to an array node.  If no array node exists at the
     * specified path, a new array node will be created.
     *   <p>
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":[ true, false ]
     * }
     * </code></pre>
     *   <p>
     *   gsr.addBooleanValues(Path.fromString("path1"), path1values)
     *   where path1Value is a List of Boolean.
     *   Would add each of the items in the path1values list to the
     *   "path1" list in the generic SCIM resource
     *   <p>
     *
     *   gsr.addBooleanValues(Path.fromString("path2"), path2values)
     *   where path2values is a List of Boolean.
     *   Would create a new array called "path2"
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to add the list to.
     * @param values a list containing the new values.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource addBooleanValues(final Path path, final List<Boolean> values) throws ScimException {
        ArrayNode valuesArrayNode = JsonUtils.getJsonNodeFactory().arrayNode();
        for (Boolean value : values) {
            valuesArrayNode.add(value);
        }

        return addValues(path, valuesArrayNode);
    }

    /**
     * Gets a Boolean value from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a Boolean.  If the path does not exist,
     * "{@code null}" will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":true
     *   }
     * </code></pre>
     *   <p>
     *
     *   getBooleanValue("path1")
     *   returns true
     *   <p>
     *
     *   getBooleanValue("bogusPath")
     *   returns null
     *   <p>
     *
     * @param path the path to get the value from.
     * @return the value at the path, or null.
     * @throws ScimException thrown if an error occurs.
     */
    public Boolean getBooleanValue(final String path) throws ScimException {
        return getBooleanValue(Path.fromString(path));
    }

    /**
     * Gets a Boolean value from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a Boolean.  If the path does not exist,
     * "{@code null}" will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":true
     *   }
     * </code></pre>
     *   <p>
     *
     *   getBooleanValue(Path.fromString("path1"))
     *   returns true
     *   <p>
     *
     *   getBooleanValue(Path.fromString("bogusPath"))
     *   returns null
     *
     * @param path the path to get the value from.
     * @return the value at the path, or null.
     * @throws ScimException thrown if an error occurs.
     */
    public Boolean getBooleanValue(final Path path) throws ScimException {
        JsonNode jsonNode = getValue(path);
        return jsonNode.isNull() ? null : jsonNode.booleanValue();
    }

    /**
     * Gets a list of Boolean from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a list of Boolean.  If the path does
     * not exist, an empty list will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":[true, false]
     *   }
     * </code></pre>
     *   <p>
     *
     *   getBooleanValueList("path1")
     *   returns a list containing true, false
     *   <p>
     *
     *   getBooleanValueList("bogusPath")
     *   returns an empty list
     *   <p>
     *
     * @param path the path to get the value from.
     * @return the value at the path, or an empty list.
     * @throws ScimException thrown if an error occurs.
     */
    public List<Boolean> getBooleanValueList(final String path) throws ScimException {
        return getBooleanValueList(Path.fromString(path));
    }

    /**
     * Gets a list of Boolean from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a list of Boolean.  If the path
     * does not exist, an empty list will be returned.
     *   <p>
     *
     * For example:
     * <pre><code>
     *   In a GenericScimResource (gsr) representing the following resource:
     *   {
     *     "path1":[true, false]
     *   }
     * </code></pre>
     *   <p>
     *
     *   getBooleanValueList(Path.fromString("path1"))
     *   returns a list containing true, false
     *   <p>
     *
     *   getBooleanValueList(Path.fromString("bogusPath"))
     *   returns an empty list
     *
     * @param path the path to get the value from.
     * @return the value at the path, or an empty list.
     * @throws ScimException thrown if an error occurs.
     */
    public List<Boolean> getBooleanValueList(final Path path) throws ScimException {
        JsonNode valueNode = getValue(path);
        List<Boolean> values = new ArrayList<Boolean>();

        Iterator<JsonNode> iterator = valueNode.iterator();
        while (iterator.hasNext()) {
            values.add(iterator.next().booleanValue());
        }
        return values;
    }

    /////////////////////////////////////
    // SCIM Decimal methods
    /////////////////////////////////////
    /**
     * Adds or replaces a Double value in a generic SCIM resource.
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":2.0
     * }
     * </code></pre>
     *   <p>
     *   gsr.replaceValue("path1", path1value)
     *   where path1value is a Double
     *   would change the "path1" field to the value of the path1value
     *   variable
     *   <p>
     *
     *   gsr.replaceValue("path2", path2value)
     *   where path2value is a Double
     *   would add a field called "path2" with the value of the path2value
     *   variable
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to replace the value for.
     * @param value the new value.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource replaceValue(final String path, final Double value) throws ScimException {
        return replaceValue(Path.fromString(path), value);
    }

    /**
     * Adds or replaces a Double value in a generic SCIM resource.
     *   <p>
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":2.0
     * }
     * </code></pre>
     *   <p>
     *   gsr.replaceValue(Path.fromString("path1"), path1value)
     *   where path1value is a Double
     *   would change the "path1" field to the value of the path1value
     *   variable
     *   <p>
     *
     *   gsr.replaceValue(Path.fromString("path2"), path2value)
     *   where path2value is a Double
     *   would add a field called "path2" with the value of the path2value
     *   variable
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to replace the value for.
     * @param value the new value.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource replaceValue(final Path path, final Double value) throws ScimException {
        return replaceValue(path, DoubleNode.valueOf(value));
    }

    /**
     * Adds Double values to an array node.  If no array node exists at the
     * specified path, a new array node will be created.
     *   <p>
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":[ 2.1, 2.2 ]
     * }
     * </code></pre>
     *   <p>
     *   gsr.addDoubleValues("path1", path1values)
     *   where path1Value is a List of Double.
     *   Would add each of the items in the path1values list to the
     *   "path1" list in the generic SCIM resource
     *   <p>
     *
     *   gsr.addDoubleValues("path2", path2values)
     *   where path2values is a List of Double.
     *   Would create a new array called "path2"
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to add the list to.
     * @param values a list containing the new values.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource addDoubleValues(final String path, final List<Double> values) throws ScimException {
        return addDoubleValues(Path.fromString(path), values);
    }

    /**
     * Adds Double values to an array node.  If no array node exists at the
     * specified path, a new array node will be created.
     *   <p>
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":[ 2.1, 2.2 ]
     * }
     * </code></pre>
     *   <p>
     *   gsr.addDoubleValues(Path.fromString("path1"), path1values)
     *   where path1Value is a List of Double.
     *   Would add each of the items in the path1values list to the
     *   "path1" list in the generic SCIM resource
     *   <p>
     *
     *   gsr.addDoubleValues(Path.fromString("path2"), path2values)
     *   where path2values is a List of Double.
     *   Would create a new array called "path2"
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to add the list to.
     * @param values a list containing the new values.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource addDoubleValues(final Path path, final List<Double> values) throws ScimException {
        ArrayNode valuesArrayNode = JsonUtils.getJsonNodeFactory().arrayNode();
        for (Double value : values) {
            valuesArrayNode.add(value);
        }

        return addValues(path, valuesArrayNode);
    }

    /**
     * Gets a Double value from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a Double.  If the path does not exist,
     * "{@code null}" will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":2.0
     *   }
     * </code></pre>
     *   <p>
     *
     *   getDoubleValue("path1")
     *   returns 2.0
     *   <p>
     *
     *   getDoubleValue("bogusPath")
     *   returns null
     *
     * @param path the path to get the value from.
     * @return the value at the path, or null.
     * @throws ScimException thrown if an error occurs.
     */
    public Double getDoubleValue(final String path) throws ScimException {
        return getDoubleValue(Path.fromString(path));
    }

    /**
     * Gets a Double value from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a Double.  If the path does not exist,
     * "{@code null}" will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":2.0
     *   }
     * </code></pre>
     *   <p>
     *
     *   getDoubleValue(Path.fromString("path1"))
     *   returns 2.0
     *   <p>
     *
     *   getDoubleValue(Path.fromString("bogusPath"))
     *   returns null
     *
     * @param path the path to get the value from.
     * @return the value at the path, or null.
     * @throws ScimException thrown if an error occurs.
     */
    public Double getDoubleValue(final Path path) throws ScimException {
        JsonNode jsonNode = getValue(path);
        return jsonNode.isNull() ? null : jsonNode.doubleValue();
    }

    /**
     * Gets a list of Double from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a list of Double.  If the path does
     * not exist, an empty list will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":[2.1, 2.2]
     *   }
     * </code></pre>
     *   <p>
     *
     *   getDoubleValueList("path1")
     *   returns a list containing 2.1, 2.2
     *   <p>
     *
     *   getDoubleValueList("bogusPath")
     *   returns an empty list
     *
     * @param path the path to get the value from.
     * @return the value at the path, or an empty list.
     * @throws ScimException thrown if an error occurs.
     */
    public List<Double> getDoubleValueList(final String path) throws ScimException {
        return getDoubleValueList(Path.fromString(path));
    }

    /**
     * Gets a list of Double from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a list of Double.  If the path
     * does not exist, an empty list will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":[2.1, 2.2]
     *   }
     * </code></pre>
     *   <p>
     *
     *   getDoubleValueList(Path.fromString("path1"))
     *   returns a list containing 2.1, 2.2
     *   <p>
     *
     *   getDoubleValueList(Path.fromString("bogusPath"))
     *   returns an empty list
     *
     * @param path the path to get the value from.
     * @return the value at the path, or an empty list.
     * @throws ScimException thrown if an error occurs.
     */
    public List<Double> getDoubleValueList(final Path path) throws ScimException {
        JsonNode valueNode = getValue(path);
        List<Double> values = new ArrayList<Double>();

        Iterator<JsonNode> iterator = valueNode.iterator();
        while (iterator.hasNext()) {
            values.add(iterator.next().doubleValue());
        }
        return values;
    }

    /////////////////////////////////////
    // SCIM Integer methods
    /////////////////////////////////////
    /**
     * Adds or replaces an Integer value in a generic SCIM resource.
     *   <p>
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":7
     * }
     * </code></pre>
     *   <p>
     *   gsr.replaceValue("path1", path1value)
     *   where path1value is an Integer
     *   would change the "path1" field to the value of the path1value
     *   variable
     *   <p>
     *
     *   gsr.replaceValue("path2", path2value)
     *   where path2value is an Integer
     *   would add a field called "path2" with the value of the path2value
     *   variable
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to replace the value for.
     * @param value the new value.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource replaceValue(final String path, final Integer value) throws ScimException {
        return replaceValue(Path.fromString(path), value);
    }

    /**
     * Adds or replaces an Integer value in a generic SCIM resource.
     *   <p>
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":7
     * }
     * </code></pre>
     *   <p>
     *   gsr.replaceValue(Path.fromString("path1"), path1value)
     *   where path1value is an Integer
     *   would change the "path1" field to the value of the path1value
     *   variable
     *   <p>
     *
     *   gsr.replaceValue(Path.fromString("path2"), path2value)
     *   where path2value is an Integer
     *   would add a field called "path2" with the value of the path2value
     *   variable
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to replace the value for.
     * @param value the new value.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource replaceValue(final Path path, final Integer value) throws ScimException {
        return replaceValue(path, IntNode.valueOf(value));
    }

    /**
     * Adds Integer values to an array node.  If no array node exists at the
     * specified path, a new array node will be created.
     *   <p>
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":[ 11, 13 ]
     * }
     * </code></pre>
     *   <p>
     *   gsr.addIntegerValues("path1", path1values)
     *   where path1Value is a List of Integer.
     *   Would add each of the items in the path1values list to the
     *   "path1" list in the generic SCIM resource
     *   <p>
     *
     *   gsr.addIntegerValues("path2", path2values)
     *   where path2values is a List of Integer.
     *   Would create a new array called "path2"
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to add the list to.
     * @param values a list containing the new values.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource addIntegerValues(final String path, final List<Integer> values)
            throws ScimException {
        return addIntegerValues(Path.fromString(path), values);
    }

    /**
     * Adds Integer values to an array node.  If no array node exists at the
     * specified path, a new array node will be created.
     *   <p>
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":[ 11, 13 ]
     * }
     * </code></pre>
     *   <p>
     *   gsr.addIntegerValues(Path.fromString("path1"), path1values)
     *   where path1Value is a List of Integer.
     *   Would add each of the items in the path1values list to the
     *   "path1" list in the generic SCIM resource
     *   <p>
     *
     *   gsr.addIntegerValues(Path.fromString("path2"), path2values)
     *   where path2values is a List of Integer.
     *   Would create a new array called "path2"
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to add the list to.
     * @param values a list containing the new values.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource addIntegerValues(final Path path, final List<Integer> values) throws ScimException {
        ArrayNode valuesArrayNode = JsonUtils.getJsonNodeFactory().arrayNode();
        for (Integer value : values) {
            valuesArrayNode.add(value);
        }

        return addValues(path, valuesArrayNode);
    }

    /**
     * Gets an Integer value from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be an Integer.  If the path does not exist,
     * "{@code null}" will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":7
     *   }
     * </code></pre>
     *   <p>
     *
     *   getIntegerValue("path1")
     *   returns 7
     *   <p>
     *
     *   getIntegerValue("bogusPath")
     *   returns null
     *
     * @param path the path to get the value from.
     * @return the value at the path, or null.
     * @throws ScimException thrown if an error occurs.
     */
    public Integer getIntegerValue(final String path) throws ScimException {
        return getIntegerValue(Path.fromString(path));
    }

    /**
     * Gets an Integer value from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be an Integer.  If the path does not exist,
     * "{@code null}" will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":7
     *   }
     * </code></pre>
     *   <p>
     *
     *   getIntegerValue(Path.fromString("path1"))
     *   returns 7
     *   <p>
     *
     *   getIntegerValue(Path.fromString("bogusPath"))
     *   returns null
     *
     * @param path the path to get the value from.
     * @return the value at the path, or null.
     * @throws ScimException thrown if an error occurs.
     */
    public Integer getIntegerValue(final Path path) throws ScimException {
        JsonNode jsonNode = getValue(path);
        return jsonNode.isNull() ? null : jsonNode.intValue();
    }

    /**
     * Gets a list of Integer from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a list of Integer.  If the path does
     * not exist, an empty list will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":[11, 13]
     *   }
     * </code></pre>
     *   <p>
     *
     *   getIntegerValueList("path1")
     *   returns a list containing 11, 13
     *   <p>
     *
     *   getIntegerValueList("bogusPath")
     *   returns an empty list
     *
     * @param path the path to get the value from.
     * @return the value at the path, or an empty list.
     * @throws ScimException thrown if an error occurs.
     */
    public List<Integer> getIntegerValueList(final String path) throws ScimException {
        return getIntegerValueList(Path.fromString(path));
    }

    /**
     * Gets a list of Integer from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a list of Integer.  If the path
     * does not exist, an empty list will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":[11, 13]
     *   }
     * </code></pre>
     *   <p>
     *
     *   getIntegerValueList(Path.fromString("path1"))
     *   returns a list containing 11, 13
     *   <p>
     *
     *   getIntegerValueList(Path.fromString("bogusPath"))
     *   returns an empty list
     *
     * @param path the path to get the value from.
     * @return the value at the path, or an empty list.
     * @throws ScimException thrown if an error occurs.
     */
    public List<Integer> getIntegerValueList(final Path path) throws ScimException {
        JsonNode valueNode = getValue(path);
        List<Integer> values = new ArrayList<Integer>();

        Iterator<JsonNode> iterator = valueNode.iterator();
        while (iterator.hasNext()) {
            values.add(iterator.next().intValue());
        }
        return values;
    }

    /////////////////////////////////////
    // SCIM Long methods
    /////////////////////////////////////
    /**
     * Adds or replaces a Long value in a generic SCIM resource.
     *   <p>
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":7
     * }
     * </code></pre>
     *   <p>
     *   gsr.replaceValue("path1", path1value)
     *   where path1value is a Long
     *   would change the "path1" field to the value of the path1value
     *   variable
     *   <p>
     *
     *   gsr.replaceValue("path2", path2value)
     *   where path2value is a Long
     *   would add a field called "path2" with the value of the path2value
     *   variable
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to replace the value for.
     * @param value the new value.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource replaceValue(final String path, final Long value) throws ScimException {
        return replaceValue(Path.fromString(path), value);
    }

    /**
     * Adds or replaces a Long value in a generic SCIM resource.
     *   <p>
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":7
     * }
     * </code></pre>
     *   <p>
     *   gsr.replaceValue(Path.fromString("path1"), path1value)
     *   where path1value is a Long
     *   would change the "path1" field to the value of the path1value
     *   variable
     *   <p>
     *
     *   gsr.replaceValue(Path.fromString("path2"), path2value)
     *   where path2value is a Long
     *   would add a field called "path2" with the value of the path2value
     *   variable
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to replace the value for.
     * @param value the new value.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource replaceValue(final Path path, final Long value) throws ScimException {
        return replaceValue(path, LongNode.valueOf(value));
    }

    /**
     * Adds Long values to an array node.  If no array node exists at the
     * specified path, a new array node will be created.
     *   <p>
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":[ 11, 13 ]
     * }
     * </code></pre>
     *   <p>
     *   gsr.addLongValues("path1", path1values)
     *   where path1Value is a List of Long.
     *   Would add each of the items in the path1values list to the
     *   "path1" list in the generic SCIM resource
     *   <p>
     *
     *   gsr.addLongValues("path2", path2values)
     *   where path2values is a List of Long.
     *   Would create a new array called "path2"
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to add the list to.
     * @param values a list containing the new values.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource addLongValues(final String path, final List<Long> values) throws ScimException {
        return addLongValues(Path.fromString(path), values);
    }

    /**
     * Adds Long values to an array node.  If no array node exists at the
     * specified path, a new array node will be created.
     *   <p>
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":[ 11, 13 ]
     * }
     * </code></pre>
     *   <p>
     *   gsr.addLongValues(Path.fromString("path1"), path1values)
     *   where path1Value is a List of Long.
     *   Would add each of the items in the path1values list to the
     *   "path1" list in the generic SCIM resource
     *   <p>
     *
     *   gsr.addLongValues(Path.fromString("path2"), path2values)
     *   where path2values is a List of Long.
     *   Would create a new array called "path2"
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to add the list to.
     * @param values a list containing the new values.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource addLongValues(final Path path, final List<Long> values) throws ScimException {
        ArrayNode valuesArrayNode = JsonUtils.getJsonNodeFactory().arrayNode();
        for (Long value : values) {
            valuesArrayNode.add(value);
        }

        return addValues(path, valuesArrayNode);
    }

    /**
     * Gets a Long value from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a Long.  If the path does not exist,
     * "{@code null}" will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":7
     *   }
     * </code></pre>
     *   <p>
     *
     *   getLongValue("path1")
     *   returns 7
     *   <p>
     *
     *   getLongValue("bogusPath")
     *   returns null
     *
     * @param path the path to get the value from.
     * @return the value at the path, or null.
     * @throws ScimException thrown if an error occurs.
     */
    public Long getLongValue(final String path) throws ScimException {
        return getLongValue(Path.fromString(path));
    }

    /**
     * Gets a Long value from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a Long.  If the path does not exist,
     * "{@code null}" will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":7
     *   }
     * </code></pre>
     *   <p>
     *
     *   getLongValue(Path.fromString("path1"))
     *   returns 7
     *   <p>
     *
     *   getLongValue(Path.fromString("bogusPath"))
     *   returns null
     *
     * @param path the path to get the value from.
     * @return the value at the path, or null.
     * @throws ScimException thrown if an error occurs.
     */
    public Long getLongValue(final Path path) throws ScimException {
        JsonNode jsonNode = getValue(path);
        return jsonNode.isNull() ? null : jsonNode.longValue();
    }

    /**
     * Gets a list of Long from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a list of Long.  If the path does
     * not exist, an empty list will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":[11, 13]
     *   }
     * </code></pre>
     *   <p>
     *
     *   getLongValueList("path1")
     *   returns a list containing 11, 13
     *   <p>
     *
     *   getLongValueList("bogusPath")
     *   returns an empty list
     *
     * @param path the path to get the value from.
     * @return the value at the path, or an empty list.
     * @throws ScimException thrown if an error occurs.
     */
    public List<Long> getLongValueList(final String path) throws ScimException {
        return getLongValueList(Path.fromString(path));
    }

    /**
     * Gets a list of Long from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a list of Long.  If the path
     * does not exist, an empty list will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":[11, 13]
     *   }
     * </code></pre>
     *   <p>
     *
     *   getLongValueList(Path.fromString("path1"))
     *   returns a list containing 11, 13
     *   <p>
     *
     *   getLongValueList(Path.fromString("bogusPath"))
     *   returns an empty list
     *
     * @param path the path to get the value from.
     * @return the value at the path, or an empty list.
     * @throws ScimException thrown if an error occurs.
     */
    public List<Long> getLongValueList(final Path path) throws ScimException {
        JsonNode valueNode = getValue(path);
        List<Long> values = new ArrayList<Long>();

        Iterator<JsonNode> iterator = valueNode.iterator();
        while (iterator.hasNext()) {
            values.add(iterator.next().longValue());
        }
        return values;
    }

    /////////////////////////////////////
    // SCIM Date/Time methods
    /////////////////////////////////////
    /**
     * Adds or replaces a Date value in a generic SCIM resource.
     *   <p>
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":"1970-04-20T17:54:47.542Z"
     * }
     * </code></pre>
     *   <p>
     *   gsr.replaceValue("path1", path1value)
     *   where path1value is a Date
     *   would change the "path1" field to the value of the path1value
     *   variable
     *   <p>
     *
     *   gsr.replaceValue("path2", path2value)
     *   where path2value is a Date
     *   would add a field called "path2" with the value of the path2value
     *   variable
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to replace the value for.
     * @param value the new value.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource replaceValue(final String path, final Date value) throws ScimException {
        return replaceValue(Path.fromString(path), value);
    }

    /**
     * Adds or replaces a Date value in a generic SCIM resource.
     *   <p>
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":"1970-04-20T17:54:47.542Z"
     * }
     * </code></pre>
     *   <p>
     *   gsr.replaceValue(Path.fromString("path1"), path1value)
     *   where path1value is a Date
     *   would change the "path1" field to the value of the path1value
     *   variable
     *   <p>
     *
     *   gsr.replaceValue(Path.fromString("path2"), path2value)
     *   where path2value is a Date
     *   would add a field called "path2" with the value of the path2value
     *   variable
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to replace the value for.
     * @param value the new value.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource replaceValue(final Path path, final Date value) throws ScimException {
        return replaceValue(path, GenericScimResource.getDateJsonNode(value));
    }

    /**
     * Adds Date values to an array node.  If no array node exists at the
     * specified path, a new array node will be created.
     *   <p>
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":[ "1970-04-20T17:54:47.542Z", "2000-04-20T17:54:47.542Z" ]
     * }
     * </code></pre>
     *   <p>
     *   gsr.addDateValues("path1", path1values)
     *   where path1Value is a List of Date.
     *   Would add each of the items in the path1values list to the
     *   "path1" list in the generic SCIM resource
     *   <p>
     *
     *   gsr.addDateValues("path2", path2values)
     *   where path2values is a List of Date.
     *   Would create a new array called "path2"
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to add the list to.
     * @param values a list containing the new values.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource addDateValues(final String path, final List<Date> values) throws ScimException {
        return addDateValues(Path.fromString(path), values);
    }

    /**
     * Adds Date values to an array node.  If no array node exists at the
     * specified path, a new array node will be created.
     *   <p>
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":[ "1970-04-20T17:54:47.542Z", "2000-04-20T17:54:47.542Z" ]
     * }
     * </code></pre>
     *   <p>
     *   gsr.addDateValues(Path.fromString("path1"), path1values)
     *   where path1Value is a List of Date.
     *   Would add each of the items in the path1values list to the
     *   "path1" list in the generic SCIM resource
     *   <p>
     *
     *   gsr.addDateValues(Path.fromString("path2"), path2values)
     *   where path2values is a List of Date.
     *   Would create a new array called "path2"
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to add the list to.
     * @param values a list containing the new values.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource addDateValues(final Path path, final List<Date> values) throws ScimException {
        ArrayNode valuesArrayNode = JsonUtils.getJsonNodeFactory().arrayNode();
        for (Date value : values) {
            valuesArrayNode.add(GenericScimResource.getDateJsonNode(value));
        }

        return addValues(path, valuesArrayNode);
    }

    /**
     * Gets a Date value from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a Date.  If the path does not exist,
     * "{@code null}" will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":"1970-04-20T17:54:47.542Z"
     *   }
     * </code></pre>
     *   <p>
     *
     *   getDateValue("path1")
     *   returns a Date representing "1970-04-20T17:54:47.542Z"
     *   <p>
     *
     *   getDateValue("bogusPath")
     *   returns null
     *
     * @param path the path to get the value from.
     * @return the value at the path, or null.
     * @throws ScimException thrown if an error occurs.
     */
    public Date getDateValue(final String path) throws ScimException {
        return getDateValue(Path.fromString(path));
    }

    /**
     * Gets a Date value from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a Date.  If the path does not exist,
     * "{@code null}" will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":"1970-04-20T17:54:47.542Z"
     *   }
     * </code></pre>
     *   <p>
     *
     *   getDateValue(Path.fromString("path1"))
     *   returns a Date representing "1970-04-20T17:54:47.542Z"
     *   <p>
     *
     *   getDateValue(Path.fromString("bogusPath"))
     *   returns null
     *
     * @param path the path to get the value from.
     * @return the value at the path, or null.
     * @throws ScimException thrown if an error occurs.
     */
    public Date getDateValue(final Path path) throws ScimException {
        JsonNode jsonNode = getValue(path);
        if (jsonNode.isNull()) {
            return null;
        }
        String dateString = jsonNode.textValue();
        return getDateForString(dateString);
    }

    /**
     * Gets a list of Date from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a list of Date.  If the path does
     * not exist, an empty list will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":["1970-04-20T17:54:47.542Z", "2000-04-20T17:54:47.542Z"]
     *   }
     * </code></pre>
     *   <p>
     *
     *   getDateValueList("path1")
     *   returns a list containing dates representing
     *       "1970-04-20T17:54:47.542Z", "2000-04-20T17:54:47.542Z"
     *   <p>
     *
     *   getDateValueList("bogusPath")
     *   returns an empty list
     *
     * @param path the path to get the value from.
     * @return the value at the path, or an empty list.
     * @throws ScimException thrown if an error occurs.
     */
    public List<Date> getDateValueList(final String path) throws ScimException {
        return getDateValueList(Path.fromString(path));
    }

    /**
     * Gets a list of Date from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a list of Date.  If the path
     * does not exist, an empty list will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":["1970-04-20T17:54:47.542Z", "2000-04-20T17:54:47.542Z"]
     *   }
     * </code></pre>
     *   <p>
     *
     *   getDateValueList(Path.fromString("path1"))
     *   returns a list containing dates representing
     *       "1970-04-20T17:54:47.542Z", "2000-04-20T17:54:47.542Z"
     *   <p>
     *
     *   getDateValueList(Path.fromString("bogusPath"))
     *   returns an empty list
     *
     * @param path the path to get the value from.
     * @return the value at the path, or an empty list.
     * @throws ScimException thrown if an error occurs.
     */
    public List<Date> getDateValueList(final Path path) throws ScimException {
        JsonNode valueNode = getValue(path);
        List<Date> values = new ArrayList<Date>();

        Iterator<JsonNode> iterator = valueNode.iterator();
        while (iterator.hasNext()) {
            values.add(GenericScimResource.getDateFromJsonNode(iterator.next()));
        }
        return values;
    }

    private static String getStringForDate(final Date date) throws ScimException {
        try {
            return JsonUtils.getObjectWriter().writeValueAsString(date);
        } catch (JsonProcessingException ex) {
            // this really should not happen, but we will handle it and
            // translate to a SCIM exception just in case.
            throw new ServerErrorException(ex.getMessage());
        }
    }

    private static Date getDateForString(final String dateString) throws ScimException {
        try {
            return JsonUtils.getObjectReader().forType(Date.class).readValue(dateString);
        } catch (JsonProcessingException ex) {
            throw new ServerErrorException(ex.getMessage());
        } catch (IOException ex) {
            throw new ServerErrorException(ex.getMessage());
        }
    }

    /**
     * Gets a JsonNode that represents the supplied date.
     *
     * @param date the date to represent as a JsonNode.
     * @return the JsonNode representing the date.
     * @throws ScimException thrown if an error occurs.
     */
    public static TextNode getDateJsonNode(final Date date) throws ScimException {
        return new TextNode(getStringForDate(date));
    }

    /**
     * Gets the date represented by the supplied JsonNode.
     *
     * @param node the JsonNode representing the date.
     * @return the date represented by the JsonNode.
     * @throws ScimException thrown if an error occurs.
     */
    public static Date getDateFromJsonNode(final JsonNode node) throws ScimException {
        return getDateForString(node.textValue());
    }

    /////////////////////////////////////
    // SCIM Binary methods
    /////////////////////////////////////
    /**
     * Adds or replaces a binary value in a generic SCIM resource.
     *   <p>
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":"AjIzLg=="
     * }
     * </code></pre>
     *   <p>
     *   gsr.replaceValue("path1", path1value)
     *   where path1value is a byte[]
     *   would change the "path1" field to the value of the path1value
     *   variable
     *   <p>
     *
     *   gsr.replaceValue("path2", path2value)
     *   where path2value is a byte[]
     *   would add a field called "path2" with the value of the path2value
     *   variable
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to replace the value for.
     * @param value the new value.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource replaceValue(final String path, final byte[] value) throws ScimException {
        return replaceValue(Path.fromString(path), value);
    }

    /**
     * Adds or replaces a binary value in a generic SCIM resource.
     *   <p>
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":"AjIzLg=="
     * }
     * </code></pre>
     *   <p>
     *   gsr.replaceValue(Path.fromString("path1"), path1value)
     *   where path1value is a byte[]
     *   would change the "path1" field to the value of the path1value
     *   variable
     *   <p>
     *
     *   gsr.replaceValue(Path.fromString("path2"), path2value)
     *   where path2value is a byte[]
     *   would add a field called "path2" with the value of the path2value
     *   variabl
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to replace the value for.
     * @param value the new value.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource replaceValue(final Path path, final byte[] value) throws ScimException {
        return replaceValue(path, TextNode.valueOf(getStringForBinary(value)));
    }

    /**
     * Adds binary values to an array node.  If no array node exists at the
     * specified path, a new array node will be created.
     *   <p>
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":[ "AjIzLg==", "AjNjLp==" ]
     * }
     * </code></pre>
     *   <p>
     *   gsr.addBinaryValues("path1", path1values)
     *   where path1Value is a List of byte[].
     *   Would add each of the items in the path1values list to the
     *   "path1" list in the generic SCIM resource
     *   <p>
     *
     *   gsr.addBinaryValues("path2", path2values)
     *   where path2values is a List of byte[].
     *   Would create a new array called "path2"
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to add the list to.
     * @param values a list containing the new values.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource addBinaryValues(final String path, final List<byte[]> values) throws ScimException {
        return addBinaryValues(Path.fromString(path), values);
    }

    /**
     * Adds binary values to an array node.  If no array node exists at the
     * specified path, a new array node will be created.
     *   <p>
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":[ "AjIzLg==", "AjNjLp==" ]
     * }
     * </code></pre>
     *   <p>
     *   gsr.addBinaryValues(Path.fromString("path1"), path1values)
     *   where path1Value is a List of byte[].
     *   Would add each of the items in the path1values list to the
     *   "path1" list in the generic SCIM resource
     *   <p>
     *
     *   gsr.addBinaryValues(Path.fromString("path2"), path2values)
     *   where path2values is a List of byte[].
     *   Would create a new array called "path2"
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to add the list to.
     * @param values a list containing the new values.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource addBinaryValues(final Path path, final List<byte[]> values) throws ScimException {
        ArrayNode valuesArrayNode = JsonUtils.getJsonNodeFactory().arrayNode();
        for (byte[] value : values) {
            valuesArrayNode.add(TextNode.valueOf(getStringForBinary(value)));
        }

        return addValues(path, valuesArrayNode);
    }

    /**
     * Gets a binary value from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a binary value.  If the path does not
     * exist, "{@code null}" will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":"AjIzLg=="
     *   }
     * </code></pre>
     *   <p>
     *
     *   getBinaryValue("path1")
     *   returns the byte array decoded from "AjIzLg=="
     *   <p>
     *
     *   getBinaryValue("bogusPath")
     *   returns null
     *   <p>
     *
     * @param path the path to get the value from.
     * @return the value at the path, or null.
     * @throws ScimException thrown if an error occurs.
     */
    public byte[] getBinaryValue(final String path) throws ScimException {
        return getBinaryValue(Path.fromString(path));
    }

    /**
     * Gets a binary value from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a binary value.  If the path does
     * not exist, "{@code null}" will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":"AjIzLg=="
     *   }
     * </code></pre>
     *   <p>
     *
     *   getBinaryValue(Path.fromString("path1"))
     *   returns the byte array decoded from "AjIzLg=="
     *   <p>
     *
     *   getBinaryValue(Path.fromString("bogusPath"))
     *   returns null
     *
     * @param path the path to get the value from.
     * @return the value at the path, or null.
     * @throws ScimException thrown if an error occurs.
     */
    public byte[] getBinaryValue(final Path path) throws ScimException {
        JsonNode jsonNode = getValue(path);
        if (jsonNode.isNull()) {
            return null;
        }

        String binaryString = jsonNode.textValue();
        return getBinaryForString(binaryString);
    }

    /**
     * Gets a list of byte[] from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a list of binary values.  If the path
     * does not exist, an empty list will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":["AjIzLg==", "AjNjLp=="]
     *   }
     * </code></pre>
     *   <p>
     *
     *   getBinaryValueList("path1")
     *   returns a list containing the byte arrays decoded from
     *       "AjIzLg==", "AjNjLp=="
     *   <p>
     *
     *   getBinaryValueList("bogusPath")
     *   returns an empty list
     *
     * @param path the path to get the value from.
     * @return the value at the path, or an empty list.
     * @throws ScimException thrown if an error occurs.
     */
    public List<byte[]> getBinaryValueList(final String path) throws ScimException {
        return getBinaryValueList(Path.fromString(path));
    }

    /**
     * Gets a list of byte[] from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a list of binary values.  If the path
     * does not exist, an empty list will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":["AjIzLg==", "AjNjLp=="]
     *   }
     * </code></pre>
     *   <p>
     *
     *   getBinaryValueList(Path.fromString("path1"))
     *   returns a list containing the byte arrays decoded from
     *       "AjIzLg==", "AjNjLp=="
     *   <p>
     *
     *   getBinaryValueList(Path.fromString("bogusPath"))
     *   returns an empty list
     *
     * @param path the path to get the value from.
     * @return the value at the path, or an empty list.
     * @throws ScimException thrown if an error occurs.
     */
    public List<byte[]> getBinaryValueList(final Path path) throws ScimException {
        JsonNode valueNode = getValue(path);
        List<byte[]> values = new ArrayList<byte[]>();

        Iterator<JsonNode> iterator = valueNode.iterator();
        while (iterator.hasNext()) {
            values.add(getBinaryForString(iterator.next().textValue()));
        }
        return values;
    }

    private String getStringForBinary(final byte[] binary) throws ScimException {
        return Base64Variants.getDefaultVariant().encode(binary);
    }

    private byte[] getBinaryForString(final String binaryString) throws ScimException {
        return Base64Variants.getDefaultVariant().decode(binaryString);
    }

    /////////////////////////////////////
    // SCIM Ref methods
    /////////////////////////////////////
    /**
     * Adds or replaces a URI value in a generic SCIM resource.
     *   <p>
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":"http://localhost:8080/uri/One"
     * }
     * </code></pre>
     *   <p>
     *   gsr.replaceValue("path1", path1value)
     *   where path1value is a URI
     *   would change the "path1" field to the value of the path1value
     *   variable
     *   <p>
     *
     *   gsr.replaceValue("path2", path2value)
     *   where path2value is a URI
     *   would add a field called "path2" with the value of the path2value
     *   variable
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to replace the value for.
     * @param value the new value.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource replaceValue(final String path, final URI value) throws ScimException {
        return replaceValue(Path.fromString(path), value);
    }

    /**
     * Adds or replaces a URI value in a generic SCIM resource.
     *   <p>
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":"http://localhost:8080/uri/One"
     * }
     * </code></pre>
     *   <p>
     *   gsr.replaceValue(Path.fromString("path1"), path1value)
     *   where path1value is a URI
     *   would change the "path1" field to the value of the path1value
     *   variable
     *   <p>
     *   gsr.replaceValue(Path.fromString("path2"), path2value)
     *   where path2value is a URI
     *   would add a field called "path2" with the value of the path2value
     *   variable
     *   <p>
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to replace the value for.
     * @param value the new value.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource replaceValue(final Path path, final URI value) throws ScimException {
        return replaceValue(path, TextNode.valueOf(value.toString()));
    }

    /**
     * Adds URI values to an array node.  If no array node exists at the
     * specified path, a new array node will be created.
     * For example:
     *   <p>
     * In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     * {
     *   "path1":
     *   [
     *       "http://localhost:8080/uri/One", "http://localhost:8080/uri/Two"
     *   ]
     * }
     * </code></pre>
     *
     *   <p>
     *   gsr.addURIValues("path1", path1values)
     *   where path1Value is a List of URI.
     *   Would add each of the items in the path1values list to the
     *   "path1" list in the generic SCIM resource
     *   <p>
     *
     *   gsr.addURIValues("path2", path2values)
     *   where path2values is a List of URI.
     *   Would create a new array called "path2"
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to add the list to.
     * @param values a list containing the new values.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource addURIValues(final String path, final List<URI> values) throws ScimException {
        return addURIValues(Path.fromString(path), values);
    }

    /**
     * Adds URI values to an array node.  If no array node exists at the
     * specified path, a new array node will be created.
     *   <p>
     * For example:
     * In a GenericScimResource (gsr) representing the following resource:
     * {
     * <pre><code>
     *   "path1":
     *   [
     *       "http://localhost:8080/uri/One", "http://localhost:8080/uri/Two"
     *   ]
     * }
     * </code></pre>
     *   <p>
     *   gsr.addURIValues(Path.fromString("path1"), path1values)
     *   where path1Value is a List of URI.
     *   Would add each of the items in the path1values list to the
     *   "path1" list in the generic SCIM resource
     *   <p>
     *
     *   gsr.addURIValues(Path.fromString("path2"), path2values)
     *   where path2values is a List of URI.
     *   Would create a new array called "path2"
     *   <p>
     *
     *   Note that in a case where multiple paths match (for example
     *   a path with a filter), all paths that match will be affected.
     *
     * @param path the path to add the list to.
     * @param values a list containing the new values.
     * @return returns the new generic SCIM resource (this).
     * @throws ScimException thrown if an error occurs (for example
     * if the path or value is "{@code null}" or invalid).
     */
    public GenericScimResource addURIValues(final Path path, final List<URI> values) throws ScimException {
        ArrayNode valuesArrayNode = JsonUtils.getJsonNodeFactory().arrayNode();
        for (URI value : values) {
            valuesArrayNode.add(value.toString());
        }

        return addValues(path, valuesArrayNode);
    }

    /**
     * Gets a URI value from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a URI.  If the path does not exist,
     * "{@code null}" will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":"http://localhost:8080/uri/One"
     *   }
     * </code></pre>
     *   <p>
     *
     *   getURIValue("path1")
     *   returns "http://localhost:8080/uri/One"
     *   <p>
     *
     *   getURIValue("bogusPath")
     *   returns null
     *
     * @param path the path to get the value from.
     * @return the value at the path, or null.
     * @throws ScimException thrown if an error occurs.
     */
    public URI getURIValue(final String path) throws ScimException {
        return getURIValue(Path.fromString(path));
    }

    /**
     * Gets a URI value from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a URI.  If the path does not exist,
     * "{@code null}" will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":"http://localhost:8080/uri/One"
     *   }
     * </code></pre>
     *   <p>
     *
     *   getURIValue(Path.fromString("path1"))
     *   returns "http://localhost:8080/uri/One"
     *   <p>
     *
     *   getURIValue(Path.fromString("bogusPath"))
     *   returns null
     *
     * @param path the path to get the value from.
     * @return the value at the path, or null.
     * @throws ScimException thrown if an error occurs.
     */
    public URI getURIValue(final Path path) throws ScimException {
        try {
            JsonNode jsonNode = getValue(path);
            return jsonNode.isNull() ? null : new URI(jsonNode.textValue());
        } catch (URISyntaxException ex) {
            throw new ServerErrorException(ex.getMessage());
        }
    }

    /**
     * Gets a list of URI from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a list of URI.  If the path does
     * not exist, an empty list will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":
     *     [
     *       "http://localhost:8080/uri/One", "http://localhost:8080/uri/Two"
     *     ]
     *   }
     * </code></pre>
     *   <p>
     *
     *   getURIValueList("path1")
     *   returns a list containing
     *       "http://localhost:8080/uri/One", "http://localhost:8080/uri/Two"
     *   <p>
     *
     *   getURIValueList("bogusPath")
     *   returns an empty list
     *
     * @param path the path to get the value from.
     * @return the value at the path, or an empty list.
     * @throws ScimException thrown if an error occurs.
     */
    public List<URI> getURIValueList(final String path) throws ScimException {
        return getURIValueList(Path.fromString(path));
    }

    /**
     * Gets a list of URI from a generic SCIM resource.  If the path exists,
     * the JSON node at the path must be a list of URI.  If the path
     * does not exist, an empty list will be returned.
     *   <p>
     *
     * For example:
     *   In a GenericScimResource (gsr) representing the following resource:
     * <pre><code>
     *   {
     *     "path1":
     *     [
     *         "http://localhost:8080/uri/One", "http://localhost:8080/uri/Two"
     *     ]
     *   }
     * </code></pre>
     *   <p>
     *
     *   getURIValueList(Path.fromString("path1"))
     *   returns a list containing
     *       "http://localhost:8080/uri/One", "http://localhost:8080/uri/Two"
     *   <p>
     *
     *   getURIValueList(Path.fromString("bogusPath"))
     *   returns an empty list
     *   <p>
     *
     * @param path the path to get the value from.
     * @return the value at the path, or an empty list.
     * @throws ScimException thrown if an error occurs.
     */
    public List<URI> getURIValueList(final Path path) throws ScimException {
        try {
            JsonNode valueNode = getValue(path);
            List<URI> values = new ArrayList<URI>();

            Iterator<JsonNode> iterator = valueNode.iterator();
            while (iterator.hasNext()) {
                String uriString = iterator.next().textValue();
                values.add(new URI(uriString));
            }
            return values;
        } catch (URISyntaxException ex) {
            throw new ServerErrorException(ex.getMessage());
        }
    }

}