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