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.stream(), 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   @SuppressWarnings("null")
186   @Override
187   default Stream<? extends IItem> flatten() {
188     // TODO: Is a safe stream needed here?
189     return safeStream();
190   }
191 
192   /**
193    * A {@link Collector} implementation to generates a sequence from a stream of
194    * Metapath items.
195    *
196    * @param <ITEM_TYPE>
197    *          the Java type of the items
198    * @return a collector that will generate a sequence
199    */
200   @NonNull
201   static <ITEM_TYPE extends IItem> Collector<ITEM_TYPE, ?, ISequence<ITEM_TYPE>> toSequence() {
202     return new Collector<ITEM_TYPE, List<ITEM_TYPE>, ISequence<ITEM_TYPE>>() {
203 
204       @Override
205       public Supplier<List<ITEM_TYPE>> supplier() {
206         return ArrayList::new;
207       }
208 
209       @Override
210       public BiConsumer<List<ITEM_TYPE>, ITEM_TYPE> accumulator() {
211         return List::add;
212       }
213 
214       @Override
215       public BinaryOperator<List<ITEM_TYPE>> combiner() {
216         return (list1, list2) -> {
217           list1.addAll(list2);
218           return list1;
219         };
220       }
221 
222       @Override
223       public Function<List<ITEM_TYPE>, ISequence<ITEM_TYPE>> finisher() {
224         return list -> ofCollection(ObjectUtils.notNull(list));
225       }
226 
227       @Override
228       public Set<Characteristics> characteristics() {
229         return Collections.emptySet();
230       }
231     };
232   }
233 
234   @Override
235   default ISequence<ITEM> asSequence() {
236     return this;
237   }
238 
239   /**
240    * Apply the provided {@code mapFunction} to each item in the sequence.
241    *
242    * @param <T>
243    *          the Java type of the provided items
244    * @param <R>
245    *          the Java type of the resulting items
246    * @param mapFunction
247    *          the map function to apply to each item in the provided sequence
248    * @param seq
249    *          the sequence of items to map
250    * @return a new sequence containing the mapped items
251    */
252   static <T extends R, R extends IItem> ISequence<R> map(
253       @NonNull Function<T, R> mapFunction,
254       @NonNull ISequence<T> seq) {
255     return seq.safeStream()
256         .map(mapFunction::apply)
257         .collect(toSequence());
258   }
259 
260   /**
261    * Returns an unmodifiable sequence containing the provided {@code items}.
262    *
263    * @param <ITEM_TYPE>
264    *          the type of items contained in the sequence.
265    * @param items
266    *          the items to add to the sequence
267    * @return the new sequence
268    */
269   @NonNull
270   static <ITEM_TYPE extends IItem> ISequence<ITEM_TYPE> ofCollection( // NOPMD - intentional
271       @NonNull List<ITEM_TYPE> items) {
272     ISequence<ITEM_TYPE> retval;
273     if (items.isEmpty()) {
274       retval = empty();
275     } else if (items.size() == 1) {
276       retval = new SingletonSequence<>(ObjectUtils.notNull(items.iterator().next()));
277     } else {
278       retval = new SequenceN<>(items);
279     }
280     return retval;
281   }
282 
283   /**
284    * Returns an unmodifiable sequence containing the provided {@code item}.
285    * <p>
286    * If the item is {@code null} and empty sequence will be created.
287    *
288    * @param <T>
289    *          the type of items contained in the sequence.
290    * @param item
291    *          the item to add to the sequence
292    * @return the new sequence
293    */
294   @NonNull
295   static <T extends IItem> ISequence<T> of( // NOPMD - intentional
296       @Nullable T item) {
297     return item == null ? empty() : new SingletonSequence<>(item);
298   }
299 
300   /**
301    * Returns an unmodifiable sequence containing the provided {@code items}.
302    *
303    * @param <T>
304    *          the type of items contained in the sequence.
305    * @param items
306    *          the items to add to the sequence
307    * @return the new sequence
308    */
309   // TODO: remove null check on callers
310   @NonNull
311   static <T extends IItem> ISequence<T> of(@NonNull Stream<T> items) {
312     return new StreamSequence<>(items);
313   }
314 
315   /**
316    * Returns an unmodifiable sequence containing zero elements.
317    *
318    * @param <T>
319    *          the item type
320    * @return an empty {@code ISequence}
321    */
322   @NonNull
323   static <T extends IItem> ISequence<T> of() {
324     return empty();
325   }
326 
327   /**
328    * Returns an unmodifiable sequence containing two items.
329    *
330    * @param <T>
331    *          the {@code ISequence}'s item type
332    * @param e1
333    *          the first item
334    * @param e2
335    *          the second item
336    * @return an {@code ISequence} containing the specified items
337    * @throws NullPointerException
338    *           if an item is {@code null}
339    */
340   @NonNull
341   static <T extends IItem> ISequence<T> of(T e1, T e2) {
342     return new SequenceN<>(e1, e2);
343   }
344 
345   /**
346    * Returns an unmodifiable sequence containing three elements.
347    *
348    * @param <T>
349    *          the {@code ISequence}'s item type
350    * @param e1
351    *          the first item
352    * @param e2
353    *          the second item
354    * @param e3
355    *          the third item
356    * @return an {@code ISequence} containing the specified items
357    * @throws NullPointerException
358    *           if an item is {@code null}
359    */
360   @NonNull
361   static <T extends IItem> ISequence<T> of(T e1, T e2, T e3) {
362     return new SequenceN<>(e1, e2, e3);
363   }
364 
365   /**
366    * Returns an unmodifiable sequence containing four items.
367    *
368    * @param <T>
369    *          the {@code ISequence}'s item type
370    * @param e1
371    *          the first item
372    * @param e2
373    *          the second item
374    * @param e3
375    *          the third item
376    * @param e4
377    *          the fourth item
378    * @return an {@code ISequence} containing the specified items
379    * @throws NullPointerException
380    *           if an item is {@code null}
381    */
382   @NonNull
383   static <T extends IItem> ISequence<T> of(T e1, T e2, T e3, T e4) {
384     return new SequenceN<>(e1, e2, e3, e4);
385   }
386 
387   /**
388    * Returns an unmodifiable sequence containing five items.
389    *
390    * @param <T>
391    *          the {@code ISequence}'s item type
392    * @param e1
393    *          the first item
394    * @param e2
395    *          the second item
396    * @param e3
397    *          the third item
398    * @param e4
399    *          the fourth item
400    * @param e5
401    *          the fifth item
402    * @return an {@code ISequence} containing the specified items
403    * @throws NullPointerException
404    *           if an item is {@code null}
405    */
406   @NonNull
407   static <T extends IItem> ISequence<T> of(T e1, T e2, T e3, T e4, T e5) {
408     return new SequenceN<>(e1, e2, e3, e4, e5);
409   }
410 
411   /**
412    * Returns an unmodifiable sequence containing six items.
413    *
414    * @param <T>
415    *          the {@code ISequence}'s item type
416    * @param e1
417    *          the first item
418    * @param e2
419    *          the second item
420    * @param e3
421    *          the third item
422    * @param e4
423    *          the fourth item
424    * @param e5
425    *          the fifth item
426    * @param e6
427    *          the sixth item
428    * @return an {@code ISequence} containing the specified items
429    * @throws NullPointerException
430    *           if an item is {@code null}
431    */
432   @NonNull
433   static <T extends IItem> ISequence<T> of(T e1, T e2, T e3, T e4, T e5, T e6) {
434     return new SequenceN<>(e1, e2, e3, e4, e5, e6);
435   }
436 
437   /**
438    * Returns an unmodifiable sequence containing seven items.
439    *
440    * @param <T>
441    *          the {@code ISequence}'s item type
442    * @param e1
443    *          the first item
444    * @param e2
445    *          the second item
446    * @param e3
447    *          the third item
448    * @param e4
449    *          the fourth item
450    * @param e5
451    *          the fifth item
452    * @param e6
453    *          the sixth item
454    * @param e7
455    *          the seventh item
456    * @return an {@code ISequence} containing the specified items
457    * @throws NullPointerException
458    *           if an item is {@code null}
459    */
460   @NonNull
461   static <T extends IItem> ISequence<T> of(T e1, T e2, T e3, T e4, T e5, T e6, T e7) {
462     return new SequenceN<>(e1, e2, e3, e4, e5, e6, e7);
463   }
464 
465   /**
466    * Returns an unmodifiable sequence containing eight items.
467    *
468    * @param <T>
469    *          the {@code ISequence}'s item type
470    * @param e1
471    *          the first item
472    * @param e2
473    *          the second item
474    * @param e3
475    *          the third item
476    * @param e4
477    *          the fourth item
478    * @param e5
479    *          the fifth item
480    * @param e6
481    *          the sixth item
482    * @param e7
483    *          the seventh item
484    * @param e8
485    *          the eighth item
486    * @return an {@code ISequence} containing the specified items
487    * @throws NullPointerException
488    *           if an item is {@code null}
489    */
490   @NonNull
491   static <T extends IItem> ISequence<T> of(T e1, T e2, T e3, T e4, T e5, T e6, T e7, T e8) {
492     return new SequenceN<>(e1, e2, e3, e4, e5, e6, e7, e8);
493   }
494 
495   /**
496    * Returns an unmodifiable sequence containing nine items.
497    *
498    * @param <T>
499    *          the {@code ISequence}'s item type
500    * @param e1
501    *          the first item
502    * @param e2
503    *          the second item
504    * @param e3
505    *          the third item
506    * @param e4
507    *          the fourth item
508    * @param e5
509    *          the fifth item
510    * @param e6
511    *          the sixth item
512    * @param e7
513    *          the seventh item
514    * @param e8
515    *          the eighth item
516    * @param e9
517    *          the ninth item
518    * @return an {@code ISequence} containing the specified items
519    * @throws NullPointerException
520    *           if an item is {@code null}
521    */
522   @NonNull
523   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) {
524     return new SequenceN<>(e1, e2, e3, e4, e5, e6, e7, e8, e9);
525   }
526 
527   /**
528    * Returns an unmodifiable sequence containing ten items.
529    *
530    * @param <T>
531    *          the {@code ISequence}'s item type
532    * @param e1
533    *          the first item
534    * @param e2
535    *          the second item
536    * @param e3
537    *          the third item
538    * @param e4
539    *          the fourth item
540    * @param e5
541    *          the fifth item
542    * @param e6
543    *          the sixth item
544    * @param e7
545    *          the seventh item
546    * @param e8
547    *          the eighth item
548    * @param e9
549    *          the ninth item
550    * @param e10
551    *          the tenth item
552    * @return an {@code IArrayItem} containing the specified items
553    * @throws NullPointerException
554    *           if an item is {@code null}
555    */
556   @NonNull
557   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) {
558     return new SequenceN<>(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
559   }
560 
561   /**
562    * Returns an unmodifiable sequence containing an arbitrary number of items.
563    *
564    * @param <T>
565    *          the {@code ISequence}'s item type
566    * @param items
567    *          the items to be contained in the list
568    * @return an {@code ISequence} containing the specified items
569    * @throws NullPointerException
570    *           if an item is {@code null} or if the array is {@code null}
571    */
572   @SafeVarargs
573   @NonNull
574   static <T extends IItem> ISequence<T> of(@NonNull T... items) {
575     return items.length == 0 ? empty() : new SequenceN<>(items);
576   }
577 
578   /**
579    * Returns an unmodifiable sequence containing the items of the given
580    * Collection, in its iteration order. The given Collection must not be null,
581    * and it must not contain any null items. If the given Collection is
582    * subsequently modified, the returned array item will not reflect such
583    * modifications.
584    *
585    * @param <T>
586    *          the {@code ISequence}'s item type
587    * @param collection
588    *          a {@code Collection} from which items are drawn, must be non-null
589    * @return an {@code ISequence} containing the items of the given
590    *         {@code Collection}
591    * @throws NullPointerException
592    *           if collection is null, or if it contains any nulls
593    * @since 10
594    */
595   @SuppressWarnings("unchecked")
596   @NonNull
597   static <T extends IItem> ISequence<T> copyOf(Collection<? extends T> collection) {
598     return collection instanceof IArrayItem
599         ? (ISequence<T>) collection
600         : collection.isEmpty()
601             ? empty()
602             : new SequenceN<>(new ArrayList<>(collection));
603   }
604 }