1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package gov.nist.secauto.metaschema.core.metapath;
7   
8   import gov.nist.secauto.metaschema.core.metapath.impl.AbstractSequence;
9   import gov.nist.secauto.metaschema.core.metapath.impl.SequenceN;
10  import gov.nist.secauto.metaschema.core.metapath.impl.SingletonSequence;
11  import gov.nist.secauto.metaschema.core.metapath.impl.StreamSequence;
12  import gov.nist.secauto.metaschema.core.metapath.item.IItem;
13  import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem;
14  import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem;
15  import gov.nist.secauto.metaschema.core.metapath.type.InvalidTypeMetapathException;
16  import gov.nist.secauto.metaschema.core.metapath.type.TypeMetapathException;
17  import gov.nist.secauto.metaschema.core.util.CustomCollectors;
18  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
19  
20  import java.util.ArrayList;
21  import java.util.Collection;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.function.Function;
25  import java.util.stream.Stream;
26  
27  import edu.umd.cs.findbugs.annotations.NonNull;
28  import edu.umd.cs.findbugs.annotations.Nullable;
29  
30  /**
31   * Represents an ordered collection of Metapath expression results.
32   * <p>
33   * Items is a sequence are typically ordered based on their position in the
34   * original node graph based on a depth first ordering.
35   *
36   * @param <ITEM>
37   *          the Java type of the items in a sequence
38   */
39  @SuppressWarnings("PMD.ShortMethodName")
40  public interface ISequence<ITEM extends IItem> extends List<ITEM>, ICollectionValue {
41    /**
42     * Get an empty sequence.
43     *
44     * @param <T>
45     *          the item type
46     * @return the empty sequence
47     */
48    @SuppressWarnings("null")
49    @NonNull
50    static <T extends IItem> ISequence<T> empty() {
51      return AbstractSequence.empty();
52    }
53  
54    @Override
55    default Iterator<ITEM> iterator() {
56      return getValue().listIterator();
57    }
58  
59    /**
60     * Get the items in this sequence as a {@link List}.
61     *
62     * @return a list containing all the items of the sequence
63     */
64    @NonNull
65    List<ITEM> getValue();
66  
67    /**
68     * Get the items in this sequence as a {@link Stream}.
69     *
70     * @return a stream containing all the items of the sequence
71     */
72    @Override
73    @NonNull
74    Stream<ITEM> stream();
75  
76    /**
77     * Retrieves the first item in a sequence.
78     * <p>
79     * If the sequence is empty, a {@code null} result is returned. If
80     * requireSingleton is {@code true} and the sequence contains more than one
81     * item, a {@link TypeMetapathException} is thrown.
82     *
83     * @param <T>
84     *          the item type to return derived from the provided sequence
85     * @param items
86     *          the sequence to retrieve the first item from
87     * @param requireSingleton
88     *          if {@code true} then a {@link TypeMetapathException} is thrown if
89     *          the sequence contains more than one item
90     * @return {@code null} if the sequence is empty, or the item otherwise
91     * @throws TypeMetapathException
92     *           if the sequence contains more than one item and requireSingleton is
93     *           {@code true}
94     */
95    static <T extends IItem> T getFirstItem(@NonNull ISequence<T> items, boolean requireSingleton) {
96      return getFirstItem(items.safeStream(), requireSingleton);
97    }
98  
99    /**
100    * Retrieves the first item in a stream of items.
101    * <p>
102    * If the sequence is empty, a {@code null} result is returned. If
103    * requireSingleton is {@code true} and the sequence contains more than one
104    * item, a {@link TypeMetapathException} is thrown.
105    *
106    * @param <T>
107    *          the item type to return derived from the provided sequence
108    * @param items
109    *          the sequence to retrieve the first item from
110    * @param requireSingleton
111    *          if {@code true} then a {@link TypeMetapathException} is thrown if
112    *          the sequence contains more than one item
113    * @return {@code null} if the sequence is empty, or the item otherwise
114    * @throws TypeMetapathException
115    *           if the sequence contains more than one item and requireSingleton is
116    *           {@code true}
117    */
118   static <T extends IItem> T getFirstItem(@NonNull Stream<T> items, boolean requireSingleton) {
119     return items.limit(2)
120         .reduce((t, u) -> {
121           if (requireSingleton) {
122             throw new InvalidTypeMetapathException(
123                 null,
124                 String.format("sequence expected to contain only one item, but found multiple"));
125           }
126           return t;
127         }).orElse(null);
128   }
129 
130   /**
131    * Retrieves the first item in this sequence.
132    * <p>
133    * If the sequence is empty, a {@code null} result is returned. If
134    * requireSingleton is {@code true} and the sequence contains more than one
135    * item, a {@link TypeMetapathException} is thrown.
136    *
137    * @param requireSingleton
138    *          if {@code true} then a {@link TypeMetapathException} is thrown if
139    *          the sequence contains more than one item
140    * @return {@code null} if the sequence is empty, or the item otherwise
141    * @throws TypeMetapathException
142    *           if the sequence contains more than one item and requireSingleton is
143    *           {@code true}
144    */
145   @Nullable
146   default ITEM getFirstItem(boolean requireSingleton) {
147     return getFirstItem(this, requireSingleton);
148   }
149 
150   /**
151    * An implementation of XPath 3.1
152    * <a href="https://www.w3.org/TR/xpath-functions-31/#func-data">fn:data</a>
153    * supporting <a href="https://www.w3.org/TR/xpath-31/#id-atomization">item
154    * atomization</a>.
155    *
156    * @param sequence
157    *          the sequence of items to atomize
158    * @return the atomized result
159    */
160   @Override
161   @NonNull
162   default Stream<IAnyAtomicItem> atomize() {
163     return ObjectUtils.notNull(stream().flatMap(IItem::atomize));
164   }
165 
166   /**
167    * Get this sequence as a collection value.
168    *
169    * @return the collection value
170    */
171   @NonNull
172   default ICollectionValue toCollectionValue() {
173     ICollectionValue retval;
174     switch (size()) {
175     case 0:
176       retval = empty();
177       break;
178     case 1:
179       // get the singleton item
180       retval = ObjectUtils.notNull(stream().findFirst().get());
181       break;
182     default:
183       // get this sequence of 2 or more items
184       retval = this;
185     }
186     return retval;
187   }
188 
189   /**
190    * Get a stream guaranteed to be backed by a list.
191    * <p>
192    * This call ensures that the sequence is backed by a List and not a stream, so
193    * the underlying collection can be reused.
194    *
195    * @return the stream
196    */
197   @NonNull
198   default Stream<ITEM> safeStream() {
199     return ObjectUtils.notNull(getValue().stream());
200   }
201 
202   @Override
203   default Stream<? extends IItem> flatten() {
204     // TODO: Is a safe stream needed here?
205     return safeStream();
206   }
207 
208   @Override
209   default ISequence<ITEM> toSequence() {
210     return this;
211   }
212 
213   @Override
214   default ISequence<?> contentsAsSequence() {
215     return this;
216   }
217 
218   /**
219    * Apply the provided {@code mapFunction} to each item in the sequence.
220    *
221    * @param <T>
222    *          the Java type of the provided items
223    * @param <R>
224    *          the Java type of the resulting items
225    * @param mapFunction
226    *          the map function to apply to each item in the provided sequence
227    * @param seq
228    *          the sequence of items to map
229    * @return a new sequence containing the mapped items
230    */
231   static <T extends R, R extends IItem> ISequence<R> map(
232       @NonNull Function<T, R> mapFunction,
233       @NonNull ISequence<T> seq) {
234     return seq.safeStream()
235         .map(mapFunction::apply)
236         .collect(CustomCollectors.toSequence());
237   }
238 
239   /**
240    * Returns an unmodifiable sequence containing the provided {@code items}.
241    *
242    * @param <ITEM_TYPE>
243    *          the type of items contained in the sequence.
244    * @param items
245    *          the items to add to the sequence
246    * @return the new sequence
247    */
248   @NonNull
249   static <ITEM_TYPE extends IItem> ISequence<ITEM_TYPE> ofCollection( // NOPMD - intentional
250       @NonNull Collection<ITEM_TYPE> items) {
251     ISequence<ITEM_TYPE> retval;
252     if (items.isEmpty()) {
253       retval = empty();
254     } else if (items.size() == 1) {
255       retval = new SingletonSequence<>(ObjectUtils.notNull(items.iterator().next()));
256     } else {
257       retval = new SequenceN<>(items);
258     }
259     return retval;
260   }
261 
262   /**
263    * Returns an unmodifiable sequence containing the provided {@code item}.
264    * <p>
265    * If the item is {@code null} and empty sequence will be created.
266    *
267    * @param <T>
268    *          the type of items contained in the sequence.
269    * @param item
270    *          the item to add to the sequence
271    * @return the new sequence
272    */
273   @NonNull
274   static <T extends IItem> ISequence<T> of( // NOPMD - intentional
275       @Nullable T item) {
276     return item == null ? empty() : new SingletonSequence<>(item);
277   }
278 
279   /**
280    * Returns an unmodifiable sequence containing the provided {@code items}.
281    *
282    * @param <T>
283    *          the type of items contained in the sequence.
284    * @param items
285    *          the items to add to the sequence
286    * @return the new sequence
287    */
288   // TODO: remove null check on callers
289   @NonNull
290   static <T extends IItem> ISequence<T> of(@NonNull Stream<T> items) {
291     return new StreamSequence<>(items);
292   }
293 
294   /**
295    * Returns an unmodifiable sequence containing zero elements.
296    *
297    * @param <T>
298    *          the item type
299    * @return an empty {@code ISequence}
300    */
301   @NonNull
302   static <T extends IItem> ISequence<T> of() {
303     return empty();
304   }
305 
306   /**
307    * Returns an unmodifiable sequence containing two items.
308    *
309    * @param <T>
310    *          the {@code ISequence}'s item type
311    * @param e1
312    *          the first item
313    * @param e2
314    *          the second item
315    * @return an {@code ISequence} containing the specified items
316    * @throws NullPointerException
317    *           if an item is {@code null}
318    */
319   @NonNull
320   static <T extends IItem> ISequence<T> of(T e1, T e2) {
321     return new SequenceN<>(e1, e2);
322   }
323 
324   /**
325    * Returns an unmodifiable sequence containing three elements.
326    *
327    * @param <T>
328    *          the {@code ISequence}'s item type
329    * @param e1
330    *          the first item
331    * @param e2
332    *          the second item
333    * @param e3
334    *          the third item
335    * @return an {@code ISequence} containing the specified items
336    * @throws NullPointerException
337    *           if an item is {@code null}
338    */
339   @NonNull
340   static <T extends IItem> ISequence<T> of(T e1, T e2, T e3) {
341     return new SequenceN<>(e1, e2, e3);
342   }
343 
344   /**
345    * Returns an unmodifiable sequence containing four items.
346    *
347    * @param <T>
348    *          the {@code ISequence}'s item type
349    * @param e1
350    *          the first item
351    * @param e2
352    *          the second item
353    * @param e3
354    *          the third item
355    * @param e4
356    *          the fourth item
357    * @return an {@code ISequence} containing the specified items
358    * @throws NullPointerException
359    *           if an item is {@code null}
360    */
361   @NonNull
362   static <T extends IItem> ISequence<T> of(T e1, T e2, T e3, T e4) {
363     return new SequenceN<>(e1, e2, e3, e4);
364   }
365 
366   /**
367    * Returns an unmodifiable sequence containing five items.
368    *
369    * @param <T>
370    *          the {@code ISequence}'s item type
371    * @param e1
372    *          the first item
373    * @param e2
374    *          the second item
375    * @param e3
376    *          the third item
377    * @param e4
378    *          the fourth item
379    * @param e5
380    *          the fifth item
381    * @return an {@code ISequence} containing the specified items
382    * @throws NullPointerException
383    *           if an item is {@code null}
384    */
385   @NonNull
386   static <T extends IItem> ISequence<T> of(T e1, T e2, T e3, T e4, T e5) {
387     return new SequenceN<>(e1, e2, e3, e4, e5);
388   }
389 
390   /**
391    * Returns an unmodifiable sequence containing six items.
392    *
393    * @param <T>
394    *          the {@code ISequence}'s item type
395    * @param e1
396    *          the first item
397    * @param e2
398    *          the second item
399    * @param e3
400    *          the third item
401    * @param e4
402    *          the fourth item
403    * @param e5
404    *          the fifth item
405    * @param e6
406    *          the sixth item
407    * @return an {@code ISequence} containing the specified items
408    * @throws NullPointerException
409    *           if an item is {@code null}
410    */
411   @NonNull
412   static <T extends IItem> ISequence<T> of(T e1, T e2, T e3, T e4, T e5, T e6) {
413     return new SequenceN<>(e1, e2, e3, e4, e5, e6);
414   }
415 
416   /**
417    * Returns an unmodifiable sequence containing seven items.
418    *
419    * @param <T>
420    *          the {@code ISequence}'s item type
421    * @param e1
422    *          the first item
423    * @param e2
424    *          the second item
425    * @param e3
426    *          the third item
427    * @param e4
428    *          the fourth item
429    * @param e5
430    *          the fifth item
431    * @param e6
432    *          the sixth item
433    * @param e7
434    *          the seventh item
435    * @return an {@code ISequence} containing the specified items
436    * @throws NullPointerException
437    *           if an item is {@code null}
438    */
439   @NonNull
440   static <T extends IItem> ISequence<T> of(T e1, T e2, T e3, T e4, T e5, T e6, T e7) {
441     return new SequenceN<>(e1, e2, e3, e4, e5, e6, e7);
442   }
443 
444   /**
445    * Returns an unmodifiable sequence containing eight items.
446    *
447    * @param <T>
448    *          the {@code ISequence}'s item type
449    * @param e1
450    *          the first item
451    * @param e2
452    *          the second item
453    * @param e3
454    *          the third item
455    * @param e4
456    *          the fourth item
457    * @param e5
458    *          the fifth item
459    * @param e6
460    *          the sixth item
461    * @param e7
462    *          the seventh item
463    * @param e8
464    *          the eighth item
465    * @return an {@code ISequence} containing the specified items
466    * @throws NullPointerException
467    *           if an item is {@code null}
468    */
469   @NonNull
470   static <T extends IItem> ISequence<T> of(T e1, T e2, T e3, T e4, T e5, T e6, T e7, T e8) {
471     return new SequenceN<>(e1, e2, e3, e4, e5, e6, e7, e8);
472   }
473 
474   /**
475    * Returns an unmodifiable sequence containing nine items.
476    *
477    * @param <T>
478    *          the {@code ISequence}'s item type
479    * @param e1
480    *          the first item
481    * @param e2
482    *          the second item
483    * @param e3
484    *          the third item
485    * @param e4
486    *          the fourth item
487    * @param e5
488    *          the fifth item
489    * @param e6
490    *          the sixth item
491    * @param e7
492    *          the seventh item
493    * @param e8
494    *          the eighth item
495    * @param e9
496    *          the ninth item
497    * @return an {@code ISequence} containing the specified items
498    * @throws NullPointerException
499    *           if an item is {@code null}
500    */
501   @NonNull
502   static <T extends IItem> ISequence<T> of(T e1, T e2, T e3, T e4, T e5, T e6, T e7, T e8, T e9) {
503     return new SequenceN<>(e1, e2, e3, e4, e5, e6, e7, e8, e9);
504   }
505 
506   /**
507    * Returns an unmodifiable sequence containing ten items.
508    *
509    * @param <T>
510    *          the {@code ISequence}'s item type
511    * @param e1
512    *          the first item
513    * @param e2
514    *          the second item
515    * @param e3
516    *          the third item
517    * @param e4
518    *          the fourth item
519    * @param e5
520    *          the fifth item
521    * @param e6
522    *          the sixth item
523    * @param e7
524    *          the seventh item
525    * @param e8
526    *          the eighth item
527    * @param e9
528    *          the ninth item
529    * @param e10
530    *          the tenth item
531    * @return an {@code IArrayItem} containing the specified items
532    * @throws NullPointerException
533    *           if an item is {@code null}
534    */
535   @NonNull
536   static <T extends IItem> ISequence<T> of(T e1, T e2, T e3, T e4, T e5, T e6, T e7, T e8, T e9, T e10) {
537     return new SequenceN<>(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
538   }
539 
540   /**
541    * Returns an unmodifiable sequence containing an arbitrary number of items.
542    *
543    * @param <T>
544    *          the {@code ISequence}'s item type
545    * @param items
546    *          the items to be contained in the list
547    * @return an {@code ISequence} containing the specified items
548    * @throws NullPointerException
549    *           if an item is {@code null} or if the array is {@code null}
550    */
551   @SafeVarargs
552   @NonNull
553   static <T extends IItem> ISequence<T> of(@NonNull T... items) {
554     return items.length == 0 ? empty() : new SequenceN<>(items);
555   }
556 
557   /**
558    * Returns an unmodifiable sequence containing the items of the given
559    * Collection, in its iteration order. The given Collection must not be null,
560    * and it must not contain any null items. If the given Collection is
561    * subsequently modified, the returned array item will not reflect such
562    * modifications.
563    *
564    * @param <T>
565    *          the {@code ISequence}'s item type
566    * @param collection
567    *          a {@code Collection} from which items are drawn, must be non-null
568    * @return an {@code ISequence} containing the items of the given
569    *         {@code Collection}
570    * @throws NullPointerException
571    *           if collection is null, or if it contains any nulls
572    * @since 10
573    */
574   @SuppressWarnings("unchecked")
575   @NonNull
576   static <T extends IItem> ISequence<T> copyOf(Collection<? extends T> collection) {
577     return collection instanceof IArrayItem
578         ? (ISequence<T>) collection
579         : collection.isEmpty()
580             ? empty()
581             : new SequenceN<>(new ArrayList<>(collection));
582   }
583 }