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 }