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