1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package gov.nist.secauto.metaschema.core.metapath.impl;
7   
8   import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
9   import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils;
10  import gov.nist.secauto.metaschema.core.metapath.function.IArgument;
11  import gov.nist.secauto.metaschema.core.metapath.item.ICollectionValue;
12  import gov.nist.secauto.metaschema.core.metapath.item.ISequence;
13  import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIntegerItem;
14  import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem;
15  import gov.nist.secauto.metaschema.core.qname.IEnhancedQName;
16  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
17  
18  import java.util.Iterator;
19  import java.util.List;
20  import java.util.Objects;
21  import java.util.stream.Collectors;
22  
23  import edu.umd.cs.findbugs.annotations.NonNull;
24  
25  /**
26   * The base class for {@link IArrayItem} implementations, that provides an
27   * implementation of common methods.
28   *
29   * @param <ITEM>
30   *          the Java type of the items contained within the sequence
31   */
32  public abstract class AbstractArrayItem<ITEM extends ICollectionValue>
33      extends ImmutableCollections.AbstractImmutableDelegatedList<ITEM>
34      implements IArrayItem<ITEM>, IFeatureCollectionFunctionItem {
35    @NonNull
36    private static final IEnhancedQName QNAME = IEnhancedQName.of("array");
37    @NonNull
38    private static final List<IArgument> ARGUMENTS = ObjectUtils.notNull(List.of(
39        IArgument.builder().name("position").type(IIntegerItem.type()).one().build()));
40  
41    @NonNull
42    private static final IArrayItem<?> EMPTY = new ArrayItemN<>();
43  
44    /**
45     * Get an immutable array item that is empty.
46     *
47     * @param <T>
48     *          the item Java type
49     * @return the empty array item
50     */
51    @SuppressWarnings("unchecked")
52    @NonNull
53    public static <T extends ICollectionValue> IArrayItem<T> empty() {
54      return (IArrayItem<T>) EMPTY;
55    }
56  
57    @Override
58    public IEnhancedQName getQName() {
59      return QNAME;
60    }
61  
62    @Override
63    public List<IArgument> getArguments() {
64      return ARGUMENTS;
65    }
66  
67    @Override
68    public ISequence<?> execute(List<? extends ISequence<?>> arguments, DynamicContext dynamicContext,
69        ISequence<?> focus) {
70      ISequence<? extends IIntegerItem> arg = FunctionUtils.asType(
71          ObjectUtils.notNull(arguments.get(0)));
72  
73      IIntegerItem position = arg.getFirstItem(true);
74      if (position == null) {
75        return ISequence.empty(); // NOPMD - readability
76      }
77  
78      int index = position.toIntValueExact() - 1;
79      ICollectionValue result = getValue().get(index);
80      return result.toSequence();
81    }
82  
83    @Override
84    public int hashCode() {
85      return Objects.hash(getValue());
86    }
87  
88    @Override
89    public boolean equals(Object other) {
90      return other == this
91          || other instanceof IArrayItem && getValue().equals(((IArrayItem<?>) other).getValue());
92    }
93  
94    @SuppressWarnings("PMD.OnlyOneReturn")
95    @Override
96    public boolean deepEquals(ICollectionValue other, DynamicContext dynamicContext) {
97      if (!(other instanceof IArrayItem)) {
98        return false;
99      }
100 
101     IArrayItem<?> otherArray = (IArrayItem<?>) other;
102     if (size() != otherArray.size()) {
103       return false;
104     }
105 
106     Iterator<? extends ICollectionValue> thisIterator = iterator();
107     Iterator<? extends ICollectionValue> otherIterator = otherArray.iterator();
108     boolean retval = true;
109     while (thisIterator.hasNext() && otherIterator.hasNext()) {
110       ICollectionValue i1 = thisIterator.next();
111       ICollectionValue i2 = otherIterator.next();
112       if (!i1.deepEquals(i2, dynamicContext)) {
113         retval = false;
114         break;
115       }
116     }
117     return retval;
118   }
119 
120   @Override
121   public String toSignature() {
122     return ObjectUtils.notNull(stream()
123         .map(ICollectionValue::toSignature)
124         .collect(Collectors.joining(",", "[", "]")));
125   }
126 
127   @Override
128   public String toString() {
129     return toSignature();
130   }
131 }