IArrayItem.java

/*
 * SPDX-FileCopyrightText: none
 * SPDX-License-Identifier: CC0-1.0
 */

package gov.nist.secauto.metaschema.core.metapath.item.function;

import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
import gov.nist.secauto.metaschema.core.metapath.ICollectionValue;
import gov.nist.secauto.metaschema.core.metapath.IPrintable;
import gov.nist.secauto.metaschema.core.metapath.ISequence;
import gov.nist.secauto.metaschema.core.metapath.function.IArgument;
import gov.nist.secauto.metaschema.core.metapath.function.IFunction;
import gov.nist.secauto.metaschema.core.metapath.function.ISequenceType;
import gov.nist.secauto.metaschema.core.metapath.impl.AbstractArrayItem;
import gov.nist.secauto.metaschema.core.metapath.impl.ArrayItemN;
import gov.nist.secauto.metaschema.core.metapath.item.IItem;
import gov.nist.secauto.metaschema.core.metapath.item.IItemVisitor;
import gov.nist.secauto.metaschema.core.util.ObjectUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Stream;

import javax.xml.namespace.QName;

import edu.umd.cs.findbugs.annotations.NonNull;

/**
 * A representation of a Metapath array item type.
 * <p>
 * Instances of this interface are required to enforce non-mutability for array
 * contents.
 *
 * @param <ITEM>
 *          the Metapath item type of array members
 */
@SuppressWarnings("PMD.ShortMethodName")
public interface IArrayItem<ITEM extends ICollectionValue> extends IFunction, IItem, List<ITEM>, IPrintable {
  /**
   * Get an empty, immutable array item.
   *
   * @param <T>
   *          the item Java type
   * @return an immutable map item
   */
  @NonNull
  static <T extends ICollectionValue> IArrayItem<T> empty() {
    return AbstractArrayItem.empty();
  }

  @Override
  default QName getQName() {
    return AbstractArrayItem.QNAME;
  }

  @Override
  default Set<FunctionProperty> getProperties() {
    return AbstractArrayItem.PROPERTIES;
  }

  @Override
  default boolean isDeterministic() {
    return true;
  }

  @Override
  default boolean isContextDepenent() {
    return false;
  }

  @Override
  default boolean isFocusDepenent() {
    return false;
  }

  @Override
  default List<IArgument> getArguments() {
    return AbstractArrayItem.ARGUMENTS;
  }

  @Override
  default int arity() {
    return 1;
  }

  @Override
  default boolean isArityUnbounded() {
    return false;
  }

  @Override
  default ISequenceType getResult() {
    return AbstractArrayItem.RESULT;
  }

  @Override
  ISequence<?> execute(List<ISequence<?>> arguments, DynamicContext dynamicContext, ISequence<?> focus);

  @Override
  default String toSignature() {
    return "array()";
  }

  @Override
  List<ITEM> getValue();

  @Override
  default boolean hasValue() {
    return true;
  }

  /**
   * Determine if this sequence is empty.
   *
   * @return {@code true} if the sequence contains no items, or {@code false}
   *         otherwise
   */
  @Override
  default boolean isEmpty() {
    return getValue().isEmpty();
  }

  /**
   * Get the count of items in this sequence.
   *
   * @return the count of items
   */
  @Override
  default int size() {
    return getValue().size();

  }

  @Override
  default boolean contains(Object obj) {
    return getValue().contains(obj);
  }

  @Override
  default Object[] toArray() {
    return getValue().toArray();
  }

  @Override
  default <T> T[] toArray(T[] array) {
    return getValue().toArray(array);
  }

  @Override
  default boolean containsAll(Collection<?> collection) {
    return getValue().containsAll(collection);
  }

  @Override
  default ITEM get(int index) {
    return getValue().get(index);
  }

  @Override
  default int indexOf(Object obj) {
    return getValue().indexOf(obj);
  }

  @Override
  default int lastIndexOf(Object obj) {
    return getValue().lastIndexOf(obj);
  }

  @Override
  default ListIterator<ITEM> listIterator() {
    return getValue().listIterator();
  }

  @Override
  default ListIterator<ITEM> listIterator(int index) {
    return getValue().listIterator(index);
  }

  @Override
  default List<ITEM> subList(int fromIndex, int toIndex) {
    return getValue().subList(fromIndex, toIndex);
  }

  /**
   * A {@link Collector} implementation to generates a sequence from a stream of
   * Metapath items.
   *
   * @param <T>
   *          the Java type of the items
   * @return a collector that will generate a sequence
   */
  @NonNull
  static <T extends ICollectionValue> Collector<T, ?, IArrayItem<T>> toArrayItem() {
    return new Collector<T, List<T>, IArrayItem<T>>() {

      @SuppressWarnings("null")
      @Override
      public Supplier<List<T>> supplier() {
        return ArrayList::new;
      }

      @Override
      public BiConsumer<List<T>, T> accumulator() {
        return List::add;
      }

      @Override
      public BinaryOperator<List<T>> combiner() {
        return (list1, list2) -> {
          list1.addAll(list2);
          return list1;
        };
      }

      @Override
      public Function<List<T>, IArrayItem<T>> finisher() {
        return list -> ofCollection(ObjectUtils.notNull(list));
      }

      @Override
      public Set<Characteristics> characteristics() {
        return Collections.emptySet();
      }
    };
  }

  @Override
  default ISequence<? extends IArrayItem<ITEM>> asSequence() {
    return ISequence.of(this);
  }

  @SuppressWarnings("null")
  @Override
  default Stream<? extends IItem> flatten() {
    return stream()
        .flatMap(ICollectionValue::flatten);
  }

  /**
   * Get a new, immutable array item that contains the items in the provided list.
   *
   * @param <T>
   *          the item Java type
   * @param items
   *          the list whose items are to be added to the new array
   * @return an array item containing the specified entries
   */
  @NonNull
  static <T extends ICollectionValue> IArrayItem<T> ofCollection( // NOPMD - intentional
      @NonNull List<T> items) {
    return items.isEmpty() ? empty() : new ArrayItemN<>(items);
  }

  /**
   * Returns an unmodifiable array item containing zero elements.
   *
   * @param <T>
   *          the item type
   * @return an empty {@code IArrayItem}
   */
  @NonNull
  static <T extends ICollectionValue> IArrayItem<T> of() {
    return AbstractArrayItem.empty();
  }

  /**
   * Returns an unmodifiable array item containing one item.
   *
   * @param <T>
   *          the {@code IArrayItem}'s item type
   * @param e1
   *          the single item
   * @return an {@code IArrayItem} containing the specified item
   * @throws NullPointerException
   *           if the item is {@code null}
   */
  @NonNull
  static <T extends ICollectionValue> IArrayItem<T> of(@NonNull T e1) {
    return new ArrayItemN<>(e1);
  }

  /**
   * Returns an unmodifiable array item containing two items.
   *
   * @param <T>
   *          the {@code IArrayItem}'s item type
   * @param e1
   *          the first item
   * @param e2
   *          the second item
   * @return an {@code IArrayItem} containing the specified items
   * @throws NullPointerException
   *           if an item is {@code null}
   */
  @NonNull
  static <T extends ICollectionValue> IArrayItem<T> of(@NonNull T e1, @NonNull T e2) {
    return new ArrayItemN<>(e1, e2);
  }

  /**
   * Returns an unmodifiable array item containing three elements.
   *
   * @param <T>
   *          the {@code IArrayItem}'s item type
   * @param e1
   *          the first item
   * @param e2
   *          the second item
   * @param e3
   *          the third item
   * @return an {@code IArrayItem} containing the specified items
   * @throws NullPointerException
   *           if an item is {@code null}
   */
  @NonNull
  static <T extends ICollectionValue> IArrayItem<T> of(@NonNull T e1, @NonNull T e2, @NonNull T e3) {
    return new ArrayItemN<>(e1, e2, e3);
  }

  /**
   * Returns an unmodifiable array item containing four items.
   *
   * @param <T>
   *          the {@code IArrayItem}'s item type
   * @param e1
   *          the first item
   * @param e2
   *          the second item
   * @param e3
   *          the third item
   * @param e4
   *          the fourth item
   * @return an {@code IArrayItem} containing the specified items
   * @throws NullPointerException
   *           if an item is {@code null}
   */
  @NonNull
  static <T extends ICollectionValue> IArrayItem<T> of(@NonNull T e1, @NonNull T e2, @NonNull T e3, @NonNull T e4) {
    return new ArrayItemN<>(e1, e2, e3, e4);
  }

  /**
   * Returns an unmodifiable array item containing five items.
   *
   * @param <T>
   *          the {@code IArrayItem}'s item type
   * @param e1
   *          the first item
   * @param e2
   *          the second item
   * @param e3
   *          the third item
   * @param e4
   *          the fourth item
   * @param e5
   *          the fifth item
   * @return an {@code IArrayItem} containing the specified items
   * @throws NullPointerException
   *           if an item is {@code null}
   */
  @NonNull
  static <T extends ICollectionValue> IArrayItem<T> of(@NonNull T e1, @NonNull T e2, @NonNull T e3, @NonNull T e4,
      @NonNull T e5) {
    return new ArrayItemN<>(e1, e2, e3, e4, e5);
  }

  /**
   * Returns an unmodifiable array item containing six items.
   *
   * @param <T>
   *          the {@code IArrayItem}'s item type
   * @param e1
   *          the first item
   * @param e2
   *          the second item
   * @param e3
   *          the third item
   * @param e4
   *          the fourth item
   * @param e5
   *          the fifth item
   * @param e6
   *          the sixth item
   * @return an {@code IArrayItem} containing the specified items
   * @throws NullPointerException
   *           if an item is {@code null}
   */
  @NonNull
  static <T extends ICollectionValue> IArrayItem<T> of(@NonNull T e1, @NonNull T e2, @NonNull T e3, @NonNull T e4,
      @NonNull T e5, @NonNull T e6) {
    return new ArrayItemN<>(e1, e2, e3, e4, e5, e6);
  }

  /**
   * Returns an unmodifiable array item containing seven items.
   *
   * @param <T>
   *          the {@code IArrayItem}'s item type
   * @param e1
   *          the first item
   * @param e2
   *          the second item
   * @param e3
   *          the third item
   * @param e4
   *          the fourth item
   * @param e5
   *          the fifth item
   * @param e6
   *          the sixth item
   * @param e7
   *          the seventh item
   * @return an {@code IArrayItem} containing the specified items
   * @throws NullPointerException
   *           if an item is {@code null}
   */
  @NonNull
  static <T extends ICollectionValue> IArrayItem<T> of(@NonNull T e1, @NonNull T e2, @NonNull T e3, @NonNull T e4,
      @NonNull T e5, @NonNull T e6, @NonNull T e7) {
    return new ArrayItemN<>(e1, e2, e3, e4, e5, e6, e7);
  }

  /**
   * Returns an unmodifiable array item containing eight items.
   *
   * @param <T>
   *          the {@code IArrayItem}'s item type
   * @param e1
   *          the first item
   * @param e2
   *          the second item
   * @param e3
   *          the third item
   * @param e4
   *          the fourth item
   * @param e5
   *          the fifth item
   * @param e6
   *          the sixth item
   * @param e7
   *          the seventh item
   * @param e8
   *          the eighth item
   * @return an {@code IArrayItem} containing the specified items
   * @throws NullPointerException
   *           if an item is {@code null}
   */
  @NonNull
  static <T extends ICollectionValue> IArrayItem<T> of(@NonNull T e1, @NonNull T e2, @NonNull T e3, @NonNull T e4,
      @NonNull T e5, @NonNull T e6, @NonNull T e7, @NonNull T e8) {
    return new ArrayItemN<>(e1, e2, e3, e4, e5, e6, e7, e8);
  }

  /**
   * Returns an unmodifiable array item containing nine items.
   *
   * @param <T>
   *          the {@code IArrayItem}'s item type
   * @param e1
   *          the first item
   * @param e2
   *          the second item
   * @param e3
   *          the third item
   * @param e4
   *          the fourth item
   * @param e5
   *          the fifth item
   * @param e6
   *          the sixth item
   * @param e7
   *          the seventh item
   * @param e8
   *          the eighth item
   * @param e9
   *          the ninth item
   * @return an {@code IArrayItem} containing the specified items
   * @throws NullPointerException
   *           if an item is {@code null}
   */
  @NonNull
  static <T extends ICollectionValue> IArrayItem<T> of(@NonNull T e1, @NonNull T e2, @NonNull T e3, @NonNull T e4,
      @NonNull T e5, @NonNull T e6, @NonNull T e7, @NonNull T e8, @NonNull T e9) {
    return new ArrayItemN<>(e1, e2, e3, e4, e5, e6, e7, e8, e9);
  }

  /**
   * Returns an unmodifiable array item containing ten items.
   *
   * @param <T>
   *          the {@code IArrayItem}'s item type
   * @param e1
   *          the first item
   * @param e2
   *          the second item
   * @param e3
   *          the third item
   * @param e4
   *          the fourth item
   * @param e5
   *          the fifth item
   * @param e6
   *          the sixth item
   * @param e7
   *          the seventh item
   * @param e8
   *          the eighth item
   * @param e9
   *          the ninth item
   * @param e10
   *          the tenth item
   * @return an {@code IArrayItem} containing the specified items
   * @throws NullPointerException
   *           if an item is {@code null}
   */
  @NonNull
  static <T extends ICollectionValue> IArrayItem<T> of(@NonNull T e1, @NonNull T e2, @NonNull T e3, @NonNull T e4,
      @NonNull T e5, @NonNull T e6, @NonNull T e7, @NonNull T e8, @NonNull T e9, @NonNull T e10) {
    return new ArrayItemN<>(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
  }

  /**
   * Returns an unmodifiable array item containing an arbitrary number of items.
   *
   * @param <T>
   *          the {@code IArrayItem}'s item type
   * @param items
   *          the items to be contained in the list
   * @return an {@code IArrayItem} containing the specified items
   * @throws NullPointerException
   *           if an item is {@code null} or if the array is {@code null}
   */
  @SafeVarargs
  @NonNull
  static <T extends ICollectionValue> IArrayItem<T> of(@NonNull T... items) {
    return items.length == 0 ? empty() : new ArrayItemN<>(items);
  }

  /**
   * Returns an unmodifiable array item containing the items of the given
   * Collection, in its iteration order. The given Collection must not be null,
   * and it must not contain any null items. If the given Collection is
   * subsequently modified, the returned array item will not reflect such
   * modifications.
   *
   * @param <T>
   *          the {@code IArrayItem}'s item type
   * @param collection
   *          a {@code Collection} from which items are drawn, must be non-null
   * @return an {@code IArrayItem} containing the items of the given
   *         {@code Collection}
   * @throws NullPointerException
   *           if collection is null, or if it contains any nulls
   */
  @SuppressWarnings("unchecked")
  @NonNull
  static <T extends ICollectionValue> IArrayItem<T> copyOf(@NonNull Collection<? extends T> collection) {
    return collection instanceof IArrayItem
        ? (IArrayItem<T>) collection
        : collection.isEmpty()
            ? empty()
            : new ArrayItemN<>(new ArrayList<>(collection));
  }

  @Override
  default void accept(IItemVisitor visitor) {
    visitor.visit(this);
  }
}