1 /*
2 * SPDX-FileCopyrightText: none
3 * SPDX-License-Identifier: CC0-1.0
4 */
5
6 package gov.nist.secauto.metaschema.core.metapath.item;
7
8 import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
9 import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem;
10 import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem;
11 import gov.nist.secauto.metaschema.core.metapath.item.function.IMapItem;
12 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
13
14 import java.util.LinkedList;
15 import java.util.List;
16 import java.util.function.Predicate;
17 import java.util.stream.Stream;
18
19 import edu.umd.cs.findbugs.annotations.NonNull;
20 import edu.umd.cs.findbugs.annotations.Nullable;
21
22 /**
23 * A data value that can be a value in a Metapath array or map.
24 */
25 public interface ICollectionValue {
26 /**
27 * Get the collection value as a sequence.
28 * <p>
29 * If the value is already a sequence, the value is returned as a sequence.
30 * Otherwise, if the value is an item, a new sequence will be created containing
31 * only that item.
32 *
33 * @return the resulting sequence
34 */
35 @NonNull
36 default ISequence<?> toSequence() {
37 return this instanceof ISequence
38 // return the sequence
39 ? (ISequence<?>) this
40 // return the item as a new sequence
41 : ISequence.of((IItem) this);
42 }
43
44 /**
45 * Get the collection value as a sequence.
46 * <p>
47 * If the value is already a sequence, the value is returned as a sequence.
48 * Otherwise, if the value is an item, what is returned depends on the item
49 * type:
50 * <ul>
51 * <li>{@link IArrayItem} or {@link IMapItem}: the contents of the returned
52 * sequence are the items of the array or map. Any member values that are a
53 * sequence are flattened.
54 * <li>Any other item: A singleton sequence is returned containing the item.
55 * </ul>
56 *
57 * @return the resulting sequence
58 */
59 @NonNull
60 ISequence<?> contentsAsSequence();
61
62 /**
63 * Get the stream of items for the collection value.
64 * <p>
65 * If the collection value is a sequence, then the items in the collection are
66 * returned.
67 *
68 * @param value
69 * the collection value
70 * @return the sequence of related items
71 */
72 @NonNull
73 static Stream<? extends IItem> normalizeAsItems(@NonNull ICollectionValue value) {
74 return value instanceof IItem
75 ? ObjectUtils.notNull(Stream.of((IItem) value))
76 : value.toSequence().stream();
77 }
78
79 /**
80 * Produce a stream of atomic items based on the atomic value of these items.
81 * <p>
82 * Supports <a href="https://www.w3.org/TR/xpath-31/#id-atomization">item
83 * atomization</a>.
84 *
85 * @return a stream of atomized atomic items.
86 */
87 @NonNull
88 Stream<IAnyAtomicItem> atomize();
89
90 /**
91 * Get the stream of items for the collection value.
92 * <p>
93 * If the collection value contains items, then these items are returned.
94 *
95 * @return a stream of related items
96 */
97 @NonNull
98 Stream<? extends IItem> flatten();
99
100 /**
101 * Determine if this and the other value are deeply equal.
102 * <p>
103 * Item equality is defined by the
104 * <a href="https://www.w3.org/TR/xpath-functions-31/#func-deep-equal">XPath 3.1
105 * fn:deep-equal</a> specification.
106 *
107 * @param other
108 * the other value to compare to this value to
109 * @param dynamicContext
110 * used to provide evaluation information, including the implicit
111 * timezone
112 * @return the {@code true} if the two values are equal, or {@code false}
113 * otherwise
114 */
115 boolean deepEquals(@Nullable ICollectionValue other, @NonNull DynamicContext dynamicContext);
116
117 /**
118 * Get a representation of the value based on its type signature.
119 *
120 * @return the signature
121 */
122 @NonNull
123 String toSignature();
124
125 /**
126 * Provides a {@link Predicate} which filters items in a stream returning
127 * distinct values based on
128 * {@link #deepEquals(ICollectionValue, DynamicContext)}.
129 *
130 * @param dynamicContext
131 * used to provide evaluation information, including the implicit
132 * timezone
133 * @return the predicate
134 */
135 static Predicate<? super ICollectionValue> distinctByDeepEquals(@NonNull DynamicContext dynamicContext) {
136 return distinctByDeepEquals(ICollectionValue.class, dynamicContext);
137 }
138
139 /**
140 * Provides a {@link Predicate} which filters items in a stream returning
141 * distinct values based on
142 * {@link #deepEquals(ICollectionValue, DynamicContext)}.
143 *
144 * @param <T>
145 * the Java type of the values filtered
146 * @param clazz
147 * the Java class for the type handled by the predicate
148 * @param dynamicContext
149 * used to provide evaluation information, including the implicit
150 * timezone
151 * @return the predicate
152 */
153 static <T extends ICollectionValue> Predicate<? super T> distinctByDeepEquals(
154 @NonNull Class<T> clazz,
155 @NonNull DynamicContext dynamicContext) {
156 List<T> seen = new LinkedList<>();
157 return item -> !seen.stream().anyMatch(other -> item.deepEquals(other, dynamicContext))
158 && seen.add(item); // true
159 }
160 }