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.function.library.MapGet;
12  import gov.nist.secauto.metaschema.core.metapath.item.ICollectionValue;
13  import gov.nist.secauto.metaschema.core.metapath.item.ISequence;
14  import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem;
15  import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIntegerItem;
16  import gov.nist.secauto.metaschema.core.metapath.item.function.IMapItem;
17  import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey;
18  import gov.nist.secauto.metaschema.core.qname.IEnhancedQName;
19  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
20  
21  import java.util.Iterator;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.Objects;
25  import java.util.stream.Collectors;
26  
27  import edu.umd.cs.findbugs.annotations.NonNull;
28  
29  /**
30   * The base class for {@link IMapItem} implementations, that provide an
31   * implementation of common utility methods.
32   *
33   * @param <VALUE>
34   *          the Java type of the value items contained within the map
35   */
36  public abstract class AbstractMapItem<VALUE extends ICollectionValue>
37      extends ImmutableCollections.AbstractImmutableDelegatedMap<IMapKey, VALUE>
38      implements IMapItem<VALUE>, IFeatureCollectionFunctionItem {
39    /**
40     * The function qualified name.
41     */
42    @NonNull
43    private static final IEnhancedQName QNAME = IEnhancedQName.of("map");
44    /**
45     * The function arguments.
46     */
47    @NonNull
48    private static final List<IArgument> ARGUMENTS = ObjectUtils.notNull(List.of(
49        IArgument.builder().name("key").type(IAnyAtomicItem.type()).one().build()));
50    @NonNull
51    private static final IMapItem<?> EMPTY = new MapItemN<>();
52  
53    /**
54     * Get an immutable map item that is empty.
55     *
56     * @param <V>
57     *          the Java type of the collection value
58     * @return the empty map item
59     */
60  
61    @SuppressWarnings("unchecked")
62    @NonNull
63    public static <V extends ICollectionValue> IMapItem<V> empty() {
64      return (IMapItem<V>) EMPTY;
65    }
66  
67    @Override
68    public IEnhancedQName getQName() {
69      return QNAME;
70    }
71  
72    @Override
73    public List<IArgument> getArguments() {
74      return ARGUMENTS;
75    }
76  
77    @Override
78    public ISequence<?> execute(List<? extends ISequence<?>> arguments, DynamicContext dynamicContext,
79        ISequence<?> focus) {
80      ISequence<? extends IIntegerItem> arg = FunctionUtils.asType(
81          ObjectUtils.notNull(arguments.get(0)));
82  
83      IAnyAtomicItem key = arg.getFirstItem(true);
84      if (key == null) {
85        return ISequence.empty(); // NOPMD - readability
86      }
87  
88      ICollectionValue result = MapGet.get(this, key);
89      return result == null ? ISequence.empty() : result.toSequence();
90    }
91  
92    @Override
93    public int hashCode() {
94      return Objects.hash(getValue());
95    }
96  
97    @Override
98    public boolean equals(Object other) {
99      return other == this
100         || other instanceof IMapItem && getValue().equals(((IMapItem<?>) other).getValue());
101   }
102 
103   @SuppressWarnings("PMD.OnlyOneReturn")
104   @Override
105   public boolean deepEquals(ICollectionValue other, DynamicContext dynamicContext) {
106     if (!(other instanceof IMapItem)) {
107       return false;
108     }
109 
110     IMapItem<?> otherItem = (IMapItem<?>) other;
111     if (size() != otherItem.size()) {
112       return false;
113     }
114 
115     Iterator<Map.Entry<IMapKey, VALUE>> thisIterator = entrySet().iterator();
116     Iterator<? extends Map.Entry<IMapKey, ? extends ICollectionValue>> otherIterator = otherItem.entrySet().iterator();
117     boolean retval = true;
118     while (thisIterator.hasNext() && otherIterator.hasNext()) {
119       Map.Entry<IMapKey, ? extends ICollectionValue> i1 = thisIterator.next();
120       Map.Entry<IMapKey, ? extends ICollectionValue> i2 = otherIterator.next();
121 
122       retval = i1.getKey().isSameKey(ObjectUtils.notNull(i2.getKey()))
123           && i1.getValue().deepEquals(i2.getValue(), dynamicContext);
124       if (!retval) {
125         break;
126       }
127     }
128     return retval;
129   }
130 
131   @Override
132   public String toSignature() {
133     return ObjectUtils.notNull(entrySet().stream()
134         .map(entry -> entry.getKey().getKey().toSignature() + "=" + entry.getValue().toSignature())
135         .collect(Collectors.joining(",", "[", "]")));
136   }
137 
138   @Override
139   public String toString() {
140     return toSignature();
141   }
142 }