All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.unboundid.scim2.common.messages.PatchOperation Maven / Gradle / Ivy

/*
 * Copyright 2015-2020 Ping Identity Corporation
 *
 * 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 .
 */

package com.unboundid.scim2.common.messages;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.core.Base64Variants;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
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.GenericScimResource;
import com.unboundid.scim2.common.Path;
import com.unboundid.scim2.common.exceptions.BadRequestException;
import com.unboundid.scim2.common.exceptions.ScimException;
import com.unboundid.scim2.common.filters.Filter;
import com.unboundid.scim2.common.utils.JsonUtils;
import com.unboundid.scim2.common.utils.SchemaUtils;

import java.net.URI;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

/**
 * An individual patch operation.
 */
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.PROPERTY,
    property = "op")
@JsonSubTypes({
    @JsonSubTypes.Type(value = PatchOperation.AddOperation.class,
        name="add"),
    @JsonSubTypes.Type(value = PatchOperation.RemoveOperation.class,
        name="remove"),
    @JsonSubTypes.Type(value = PatchOperation.ReplaceOperation.class,
        name="replace")})
public abstract class PatchOperation
{
  static final class AddOperation extends PatchOperation
  {
    @JsonProperty
    private final JsonNode value;

    /**
     * Create a new add patch operation.
     *
     * @param path The path targeted by this patch operation.
     * @param value The value(s) to add.
     * @throws ScimException If a value is not valid.
     */
    @JsonCreator
    private AddOperation(
        @JsonProperty(value = "path") final Path path,
        @JsonProperty(value = "value", required = true) final JsonNode value)
        throws ScimException
    {
      super(path);
      if(value == null || value.isNull() ||
           ((value.isArray() || value.isObject()) && value.size() == 0))
       {
         throw BadRequestException.invalidSyntax(
             "value field must not be null or an empty container");
       }
      if(path == null && !value.isObject())
      {
        throw BadRequestException.invalidSyntax(
            "value field must be a JSON object containing the attributes to " +
                "add");
      }
      if(path != null)
      {
        for (Path.Element element : path)
        {
          if(element.getValueFilter() != null)
          {
            throw BadRequestException.invalidPath(
                "path field for add operations must not include any value " +
                    "selection filters");
          }
        }
      }
      this.value = value;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public PatchOpType getOpType()
    {
      return PatchOpType.ADD;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JsonNode getJsonNode()
    {
      return value.deepCopy();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  T getValue(final Class cls)
        throws JsonProcessingException, ScimException, IllegalArgumentException
    {
      if(value.isArray())
      {
        throw new IllegalArgumentException("Patch operation contains " +
            "multiple values");
      }
      return JsonUtils.getObjectReader().treeToValue(
          value, cls);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  List getValues(final Class cls)
        throws JsonProcessingException, ScimException
    {
      ArrayList objects = new ArrayList(value.size());
      for(JsonNode node : value)
      {
        objects.add(JsonUtils.getObjectReader().treeToValue(node, cls));
      }
      return objects;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void apply(final ObjectNode node) throws ScimException
    {
      JsonUtils.addValue(getPath() == null ? Path.root() :
          getPath(), node, value);
      addMissingSchemaUrns(node);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals(final Object o)
    {
      if (this == o)
      {
        return true;
      }
      if (o == null || getClass() != o.getClass())
      {
        return false;
      }

      AddOperation that = (AddOperation) o;
      if (getPath() != null ? !getPath().equals(that.getPath()) :
          that.getPath() != null)
      {
        return false;
      }
      if (!value.equals(that.value))
      {
        return false;
      }

      return true;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int hashCode()
    {
      int result = getPath() != null ? getPath().hashCode() : 0;
      result = 31 * result + value.hashCode();
      return result;
    }
  }

  static final class RemoveOperation extends PatchOperation
  {
    /**
     * Create a new remove patch operation.
     *
     * @param path The path targeted by this patch operation.
     * @throws ScimException If a path is null.
     */
    @JsonCreator
    private RemoveOperation(
        @JsonProperty(value = "path", required = true) final Path path)
        throws ScimException
    {
      super(path);
      if(path == null)
      {
        throw BadRequestException.noTarget(
            "path field must not be null for remove operations");
      }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public PatchOpType getOpType()
    {
      return PatchOpType.REMOVE;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void apply(final ObjectNode node) throws ScimException
    {
      JsonUtils.removeValues(getPath(), node);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals(final Object o)
    {
      if (this == o)
      {
        return true;
      }
      if (o == null || getClass() != o.getClass())
      {
        return false;
      }

      RemoveOperation that = (RemoveOperation) o;

      if (getPath() != null ? !getPath().equals(that.getPath()) :
          that.getPath() != null)
      {
        return false;
      }

      return true;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int hashCode()
    {
      return getPath() != null ? getPath().hashCode() : 0;
    }
  }

  static final class ReplaceOperation extends PatchOperation
  {
    @JsonProperty
    private final JsonNode value;

    /**
     * Create a new replace patch operation.
     *
     * @param path The path targeted by this patch operation.
     * @param value The value(s) to replace.
     * @throws ScimException If a value is not valid.
     */
    @JsonCreator
    private ReplaceOperation(
        @JsonProperty(value = "path") final Path path,
        @JsonProperty(value = "value", required = true) final JsonNode value)
        throws ScimException
    {
      super(path);
      if(value == null || value.isNull() ||
           ((value.isArray() || value.isObject()) && value.size() == 0))
       {
         throw BadRequestException.invalidSyntax(
             "value field must not be null or an empty container");
       }
      if(path == null && !value.isObject())
      {
        throw BadRequestException.invalidSyntax(
            "value field must be a JSON object containing the attributes to " +
                "replace");
      }
      this.value = value;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public PatchOpType getOpType()
    {
      return PatchOpType.REPLACE;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JsonNode getJsonNode()
    {
      return value.deepCopy();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  T getValue(final Class cls)
        throws JsonProcessingException, ScimException, IllegalArgumentException
    {
      if(value.isArray())
      {
        throw new IllegalArgumentException("Patch operation contains " +
            "multiple values");
      }
      return JsonUtils.getObjectReader().treeToValue(value, cls);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  List getValues(final Class cls)
        throws JsonProcessingException, ScimException
    {
      ArrayList objects = new ArrayList(value.size());
      for(JsonNode node : value)
      {
        objects.add(JsonUtils.getObjectReader().treeToValue(node, cls));
      }
      return objects;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void apply(final ObjectNode node) throws ScimException
    {
      JsonUtils.replaceValue(getPath() == null ? Path.root() :
          getPath(), node, value);
      addMissingSchemaUrns(node);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals(final Object o)
    {
      if (this == o)
      {
        return true;
      }
      if (o == null || getClass() != o.getClass())
      {
        return false;
      }

      ReplaceOperation that = (ReplaceOperation) o;

      if (getPath() != null ? !getPath().equals(that.getPath()) :
          that.getPath() != null)
      {
        return false;
      }
      if (!value.equals(that.value))
      {
        return false;
      }

      return true;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int hashCode()
    {
      int result = getPath() != null ? getPath().hashCode() : 0;
      result = 31 * result + value.hashCode();
      return result;
    }
  }

  private final Path path;

  /**
   * Create a new patch operation.
   *
   * @param path The path targeted by this patch operation.
   * @throws ScimException If an value is not valid.
   */
  PatchOperation(final Path path) throws ScimException
  {
    if(path != null)
    {
      if(path.size() > 2)
      {
        throw BadRequestException.invalidPath(
            "Path can not target sub-attributes more than one level deep");
      }

      if(path.size() == 2)
      {
        Filter valueFilter = path.getElement(1).getValueFilter();
        // Allow use of the special case "value" path to reference the value itself.
        // Any other value filter is for a sub-attribute, which is not permitted.
        if (valueFilter != null &&
            !valueFilter.getAttributePath().getElement(0).getAttribute().equals("value"))
        {
          throw BadRequestException.invalidPath(
              "Path can not include a value filter on sub-attributes");
        }
      }
    }
    this.path = path;
  }

  /**
   * Retrieves the operation type.
   *
   * @return The operation type.
   */
  @JsonIgnore
  public abstract PatchOpType getOpType();

  /**
   * Retrieves the path targeted by this operation.
   *
   * @return The path targeted by this operation.
   */
  public Path getPath()
  {
    return path;
  }

  /**
   * Retrieve the value or values of the patch operation as a JsonNode. The
   * returned JsonNode is a copy so it may be altered without altering this
   * operation.
   *
   * @return  The value or values of the patch operation, or {@code null}
   *          if this operation is a remove operation.
   */
  @JsonIgnore
  public JsonNode getJsonNode()
  {
    return null;
  }

  /**
   * Retrieve the value of the patch operation.
   *
   * @param cls The Java class object used to determine the type to return.
   * @param  The generic type parameter of the Java class used to determine
   *            the type to return.
   * @return The value of the patch operation.
   * @throws JsonProcessingException If the value can not be parsed to the
   *         type specified by the Java class object.
   * @throws ScimException If the path is invalid.
   * @throws IllegalArgumentException If the operation contains more than one
   *         value, in which case, the getValues method should be used to
   *         retrieve all values.
   */
  public  T getValue(final Class cls)
      throws JsonProcessingException, ScimException, IllegalArgumentException
  {
    return null;
  }

  /**
   * Retrieve all values of the patch operation.
   *
   * @param cls The Java class object used to determine the type to return.
   * @param  The generic type parameter of the Java class used to determine
   *            the type to return.
   * @return The values of the patch operation.
   * @throws JsonProcessingException If the value can not be parsed to the
   *         type specified by the Java class object.
   * @throws ScimException If the path is invalid.
   */
  public  List getValues(final Class cls)
      throws JsonProcessingException, ScimException
  {
    return null;
  }

  /**
   * Apply this patch operation to an ObjectNode.
   *
   * @param node The ObjectNode to apply this patch operation to.
   *
   * @throws ScimException If the patch operation is invalid.
   */
  public abstract void apply(final ObjectNode node) throws ScimException;

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

  /**
   * Implicitly add any schema URNs of any extended attributes that are missing
   * from the schemas attribute.
   *
   * @param node The ObjectNode to apply this patch operation to.
   */
  protected void addMissingSchemaUrns(final ObjectNode node)
  {
    // Implicitly add the schema URN of any extended attributes to the
    // schemas attribute.
    JsonNode schemasNode =
        node.path(SchemaUtils.SCHEMAS_ATTRIBUTE_DEFINITION.getName());
    if(schemasNode.isArray())
    {
      ArrayNode schemas = (ArrayNode) schemasNode;
      if (getPath() == null)
      {
        Iterator i = getJsonNode().fieldNames();
        while (i.hasNext())
        {
          String field = i.next();
          if (SchemaUtils.isUrn(field))
          {
            addSchemaUrnIfMissing(schemas, field);
          }
        }
      }
      else if(getPath().getSchemaUrn() != null)
      {
        addSchemaUrnIfMissing(schemas, getPath().getSchemaUrn());
      }
    }
  }

  private void addSchemaUrnIfMissing(final ArrayNode schemas,
                                     final String schemaUrn)
  {
    for(JsonNode node : schemas)
    {
      if(node.isTextual() && node.textValue().equalsIgnoreCase(schemaUrn))
      {
        return;
      }
    }

    schemas.add(schemaUrn);
  }

  /**
   * Create a new add patch operation.
   *
   * @param value The value(s) to add.
   *
   * @return The new add patch operation.
   */
  public static PatchOperation add(final JsonNode value)
  {
    return add((Path) null, value);
  }

  /**
   * Create a new add patch operation.
   *
   * @param path The path targeted by this patch operation.
   * @param value The value(s) to add.
   *
   * @return The new add patch operation.
   * @throws ScimException If the path is invalid.
   */
  public static PatchOperation add(final String path, final JsonNode value)
      throws ScimException
  {
    return add(Path.fromString(path), value);
  }

  /**
   * Create a new add patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   * @param value The value(s) to add.
   *
   * @return The new add patch operation.
   */
  public static PatchOperation add(final Path path, final JsonNode value)
  {
    try
    {
      return new AddOperation(path, value);
    }
    catch (ScimException e)
    {
      throw new IllegalArgumentException(e);
    }
  }

  // String
  /**
   * Create a new add patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   *             Path string examples:
   *               "{@code userName eq 'bjensen'}"
   *               "{@code userName}"
   * @param values The values to add.
   *
   * @return The new add patch operation.
   * @throws ScimException If the path is invalid.
   */
  public static PatchOperation addStringValues(
      final String path, final List values) throws ScimException
  {
    return addStringValues(Path.fromString(path), values);
  }

  /**
   * Create a new add patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   * @param values The values to add.
   *
   * @return The new add patch operation.
   */
  public static PatchOperation addStringValues(
      final Path path, final List values)
  {
    ArrayNode arrayNode = JsonUtils.getJsonNodeFactory().arrayNode();
    for(String value : values)
    {
      arrayNode.add(value);
    }
    return add(path, arrayNode);
  }

  /**
   * Create a new replace patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   *             Path string examples:
   *               "{@code userName eq 'bjensen'}"
   *               "{@code userName}"
   * @param value The value(s) to replace.  The value(s) must not be {@code null}.
   *
   * @return The new replace patch operation.
   * @throws ScimException If the path is invalid.
   */
  public static PatchOperation replace(
      final String path, final String value) throws ScimException
  {
    return replace(path, TextNode.valueOf(value));
  }

  /**
   * Create a new replace patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   * @param value The value(s) to replace.  The value(s) must not be {@code null}.
   *
   * @return The new replace patch operation.
   */
  public static PatchOperation replace(
      final Path path, final String value)
  {
    return replace(path, TextNode.valueOf(value));
  }

  // Boolean
  /**
   * Create a new add patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   *             Path string examples:
   *               "{@code userName eq 'bjensen'}"
   *               "{@code userName}"
   * @param values The values to add.
   *
   * @return The new add patch operation.
   * @throws ScimException If the path is invalid.
   */
  public static PatchOperation addBooleanValues(
      final String path, final List values) throws ScimException
  {
    return addBooleanValues(Path.fromString(path), values);
  }

  /**
   * Create a new add patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   * @param values The values to add.
   *
   * @return The new add patch operation.
   */
  public static PatchOperation addBooleanValues(
      final Path path, final List values)
  {
    ArrayNode arrayNode = JsonUtils.getJsonNodeFactory().arrayNode();
    for(Boolean value : values)
    {
      arrayNode.add(value);
    }
    return add(path, arrayNode);
  }

  /**
   * Create a new replace patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   *             Path string examples:
   *               "{@code userName eq 'bjensen'}"
   *               "{@code userName}"
   * @param value The value(s) to replace.  The value(s) must not be {@code null}.
   *
   * @return The new replace patch operation.
   * @throws ScimException If the path is invalid.
   */
  public static PatchOperation replace(
      final String path, final Boolean value) throws ScimException
  {
    return replace(path, BooleanNode.valueOf(value));
  }

  /**
   * Create a new replace patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   * @param value The value(s) to replace.  The value(s) must not be {@code null}.
   *
   * @return The new replace patch operation.
   */
  public static PatchOperation replace(
      final Path path, final Boolean value)
  {
    return replace(path, BooleanNode.valueOf(value));
  }


  // Double
  /**
   * Create a new add patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   *             Path string examples:
   *               "{@code userName eq 'bjensen'}"
   *               "{@code userName}"
   * @param values The values to add.
   *
   * @return The new add patch operation.
   * @throws ScimException If the path is invalid.
   */
  public static PatchOperation addDoubleValues(
      final String path, final List values)
      throws ScimException
  {
    return addDoubleValues(Path.fromString(path), values);
  }

  /**
   * Create a new add patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   * @param values The values to add.
   *
   * @return The new add patch operation.
   */
  public static PatchOperation addDoubleValues(
      final Path path, final List values)
  {
    ArrayNode arrayNode = JsonUtils.getJsonNodeFactory().arrayNode();
    for(Double value : values)
    {
      arrayNode.add(value);
    }
    return add(path, arrayNode);
  }

  /**
   * Create a new replace patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   *             Path string examples:
   *               "{@code userName eq 'bjensen'}"
   *               "{@code userName}"
   * @param value The value(s) to replace.  The value(s) must not be {@code null}.
   *
   * @return The new replace patch operation.
   * @throws ScimException If the path is invalid.
   */
  public static PatchOperation replace(
      final String path, final Double value) throws ScimException
  {
    return replace(path, DoubleNode.valueOf(value));
  }

  /**
   * Create a new replace patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   * @param value The value(s) to replace.  The value(s) must not be {@code null}.
   *
   * @return The new replace patch operation.
   */
  public static PatchOperation replace(
      final Path path, final Double value)
  {
    return replace(path, DoubleNode.valueOf(value));
  }

  // Integer
  /**
   * Create a new add patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   *             Path string examples:
   *               "{@code userName eq 'bjensen'}"
   *               "{@code userName}"
   * @param values The values to add.
   *
   * @return The new add patch operation.
   * @throws ScimException If the path is invalid.
   */
  public static PatchOperation addIntegerValues(
      final String path, final List values) throws ScimException
  {
    return addIntegerValues(Path.fromString(path), values);
  }

  /**
   * Create a new add patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   * @param values The values to add.
   *
   * @return The new add patch operation.
   */
  public static PatchOperation addIntegerValues(
      final Path path, final List values)
  {
    ArrayNode arrayNode = JsonUtils.getJsonNodeFactory().arrayNode();
    for(Integer value : values)
    {
      arrayNode.add(value);
    }
    return add(path, arrayNode);
  }

  /**
   * Create a new replace patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   *             Path string examples:
   *               "{@code userName eq 'bjensen'}"
   *               "{@code userName}"
   * @param value The value(s) to replace.  The value(s) must not be {@code null}.
   *
   * @return The new replace patch operation.
   * @throws ScimException If the path is invalid.
   */
  public static PatchOperation replace(
      final String path, final Integer value) throws ScimException
  {
    return replace(path, IntNode.valueOf(value));
  }

  /**
   * Create a new replace patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   * @param value The value(s) to replace.  The value(s) must not be {@code null}.
   *
   * @return The new replace patch operation.
   */
  public static PatchOperation replace(
      final Path path, final Integer value)
  {
    return replace(path, IntNode.valueOf(value));
  }

  // Long
  /**
   * Create a new add patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   *             Path string examples:
   *               "{@code userName eq 'bjensen'}"
   *               "{@code userName}"
   * @param values The values to add.
   *
   * @return The new add patch operation.
   * @throws ScimException If the path is invalid.
   */
  public static PatchOperation addLongValues(
      final String path, final List values) throws ScimException
  {
    return addLongValues(Path.fromString(path), values);
  }

  /**
   * Create a new add patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   * @param values The values to add.
   *
   * @return The new add patch operation.
   */
  public static PatchOperation addLongValues(
      final Path path, final List values)
  {
    ArrayNode arrayNode = JsonUtils.getJsonNodeFactory().arrayNode();
    for(Long value : values)
    {
      arrayNode.add(value);
    }
    return add(path, arrayNode);
  }

  /**
   * Create a new replace patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   *             Path string examples:
   *               "{@code userName eq 'bjensen'}"
   *               "{@code userName}"
   * @param value The value(s) to replace.  The value(s) must not be {@code null}.
   *
   * @return The new replace patch operation.
   * @throws ScimException If the path is invalid.
   */
  public static PatchOperation replace(
      final String path, final Long value) throws ScimException
  {
    return replace(path, LongNode.valueOf(value));
  }

  /**
   * Create a new replace patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   * @param value The value(s) to replace.  The value(s) must not be {@code null}.
   *
   * @return The new replace patch operation.
   */
  public static PatchOperation replace(
      final Path path, final Long value)
  {
    return replace(path, LongNode.valueOf(value));
  }

  // Date
  /**
   * Create a new add patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   *             Path string examples:
   *               "{@code userName eq 'bjensen'}"
   *               "{@code userName}"
   * @param values The values to add.
   *
   * @return The new add patch operation.
   * @throws ScimException If the path is invalid.
   */
  public static PatchOperation addDateValues(
      final String path, final List values) throws ScimException
  {
    return addDateValues(Path.fromString(path), values);
  }

  /**
   * Create a new add patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   * @param values The values to add.
   *
   * @return The new add patch operation.
   * @throws ScimException if an error occurs.
   */
  public static PatchOperation addDateValues(
      final Path path, final List values) throws ScimException
  {
    ArrayNode arrayNode = JsonUtils.getJsonNodeFactory().arrayNode();
    for(Date value : values)
    {
      arrayNode.add(GenericScimResource.getDateJsonNode(value).textValue());
    }
    return add(path, arrayNode);
  }

  /**
   * Create a new replace patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   *             Path string examples:
   *               "{@code userName eq 'bjensen'}"
   *               "{@code userName}"
   * @param value The value(s) to replace.  The value(s) must not be {@code null}.
   *
   * @return The new replace patch operation.
   * @throws ScimException If the path is invalid.
   */
  public static PatchOperation replace(
      final String path, final Date value) throws ScimException
  {
    String valueString =
        GenericScimResource.getDateJsonNode(value).textValue();
    return replace(path, valueString);
  }

  /**
   * Create a new replace patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   * @param value The value(s) to replace.  The value(s) must not be {@code null}.
   *
   * @return The new replace patch operation.
   * @throws ScimException if an error occurs.
   */
  public static PatchOperation replace(
      final Path path, final Date value) throws ScimException
  {
    String valueString =
        GenericScimResource.getDateJsonNode(value).textValue();
    return replace(path, valueString);
  }

  // Binary
  /**
   * Create a new add patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   *             Path string examples:
   *               "{@code userName eq 'bjensen'}"
   *               "{@code userName}"
   * @param values The value(s) to add.
   *
   * @return The new add patch operation.
   * @throws ScimException If the path is invalid.
   */
  public static PatchOperation addBinaryValues(
      final String path, final List values) throws ScimException
  {
    return addBinaryValues(Path.fromString(path), values);
  }

  /**
   * Create a new add patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   * @param values The values to add.
   *
   * @return The new add patch operation.
   */
  public static PatchOperation addBinaryValues(
      final Path path, final List values)
  {
    ArrayNode arrayNode = JsonUtils.getJsonNodeFactory().arrayNode();
    for(byte[] value : values)
    {
      arrayNode.add(Base64Variants.getDefaultVariant().encode(value));
    }
    return add(path, arrayNode);
  }

  /**
   * Create a new replace patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   *             Path string examples:
   *               "{@code userName eq 'bjensen'}"
   *               "{@code userName}"
   * @param value The value(s) to replace.  The value(s) must not be {@code null}.
   *
   * @return The new replace patch operation.
   * @throws ScimException If the path is invalid.
   */
  public static PatchOperation replace(
      final String path, final byte[] value) throws ScimException
  {
    String valueString = Base64Variants.getDefaultVariant().encode(value);
    return replace(path, valueString);
  }

  /**
   * Create a new replace patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   * @param value The value(s) to replace.  The value(s) must not be {@code null}.
   *
   * @return The new replace patch operation.
   */
  public static PatchOperation replace(
      final Path path, final byte[] value)
  {
    String valueString = Base64Variants.getDefaultVariant().encode(value);
    return replace(path, valueString);
  }

  // URI
  /**
   * Create a new add patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   *             Path string examples:
   *               "{@code userName eq 'bjensen'}"
   *               "{@code userName}"
   * @param values The values to add.
   *
   * @return The new add patch operation.
   * @throws ScimException If the path is invalid.
   */
  public static PatchOperation addURIValues(
      final String path, final List values) throws ScimException
  {
    return addURIValues(Path.fromString(path), values);
  }

  /**
   * Create a new add patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   * @param values The values to add.
   *
   * @return The new add patch operation.
   */
  public static PatchOperation addURIValues(
      final Path path, final List values)
  {
    ArrayNode arrayNode = JsonUtils.getJsonNodeFactory().arrayNode();
    for(URI value : values)
    {
      arrayNode.add(value.toString());
    }
    return add(path, arrayNode);
  }

  /**
   * Create a new replace patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   *             Path string examples:
   *               "{@code userName eq 'bjensen'}"
   *               "{@code userName}"
   * @param value The value(s) to replace.  The value must not be {@code null}.
   *
   * @return The new replace patch operation.
   * @throws ScimException If the path is invalid.
   */
  public static PatchOperation replace(
      final String path, final URI value) throws ScimException
  {
    return replace(path, value.toString());
  }

  /**
   * Create a new replace patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   * @param value The value(s) to replace.  The value(s) must not be {@code null}.
   *
   * @return The new replace patch operation.
   */
  public static PatchOperation replace(
      final Path path, final URI value)
  {
    return replace(path, value.toString());
  }

  /**
   * Create a new replace patch operation.
   *
   * @param value The value(s) to replace.  The value(s) must not be {@code null}.
   *
   * @return The new replace patch operation.
   */
  public static PatchOperation replace(final ObjectNode value)
  {
    return replace((Path) null, value);
  }

  /**
   * Create a new replace patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   *             Path string examples:
   *               "{@code userName eq 'bjensen'}"
   *               "userName"
   * @param value The value(s) to replace.  The value(s) must not be {@code null}.
   *
   * @return The new replace patch operation.
   * @throws ScimException If the path is invalid.
   */
  public static PatchOperation replace(final String path, final JsonNode value)
      throws ScimException
  {
    return replace(Path.fromString(path), value);
  }

  /**
   * Create a new replace patch operation.
   *
   * @param path The path targeted by this patch operation.  The path
   *             must not be {@code null}.
   * @param value The value(s) to replace.  The value(s) must not be {@code null}.
   *
   * @return The new replace patch operation.
   */
  public static PatchOperation replace(final Path path, final JsonNode value)
  {
    try
    {
      return new ReplaceOperation(path, value);
    }
    catch (ScimException e)
    {
      throw new IllegalArgumentException(e);
    }
  }

  /**
   * Create a new remove patch operation.
   *
   * @param path The path targeted by this patch operation.
   *
   * @return The new delete patch operation.
   * @throws ScimException If the path is invalid.
   */
  public static PatchOperation remove(final String path) throws ScimException
  {
    return remove(Path.fromString(path));
  }

  /**
   * Create a new remove patch operation.
   *
   * @param path The path targeted by this patch operation.
   *
   * @return The new delete patch operation.
   */
  public static PatchOperation remove(final Path path)
  {
    try
    {
      return new RemoveOperation(path);
    }
    catch (ScimException e)
    {
      throw new IllegalArgumentException(e);
    }
  }

  /**
   * Create a new patch operation based on the parameters provided.
   *
   * @param opType The operation type.
   * @param path The path targeted by this patch operation.
   * @param value The value(s).
   *
   * @return The new patch operation.
   * @throws ScimException If the path is invalid.
   */
  public static PatchOperation create(final PatchOpType opType,
                                      final String path,
                                      final JsonNode value) throws ScimException
  {
    return create(opType, Path.fromString(path), value);
  }

  /**
   * Create a new patch operation based on the parameters provided.
   *
   * @param opType The operation type.
   * @param path The path targeted by this patch operation.
   * @param value The value(s).
   *
   * @return The new patch operation.
   */
  public static PatchOperation create(final PatchOpType opType,
                                      final Path path,
                                      final JsonNode value)
  {
    switch (opType)
    {
      case ADD:
        return add(path, value);
      case REPLACE:
        return replace(path, value);
      case REMOVE:
        return remove(path);
      default:
        throw new IllegalArgumentException("Unknown patch op type " + opType);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy