1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package dev.metaschema.core.metapath.item.function;
7   
8   import java.util.ArrayList;
9   import java.util.Collection;
10  import java.util.Collections;
11  import java.util.List;
12  import java.util.ListIterator;
13  import java.util.Set;
14  import java.util.function.BiConsumer;
15  import java.util.function.BinaryOperator;
16  import java.util.function.Function;
17  import java.util.function.Supplier;
18  import java.util.stream.Collector;
19  import java.util.stream.Stream;
20  
21  import dev.metaschema.core.metapath.function.IFunction;
22  import dev.metaschema.core.metapath.item.ICollectionValue;
23  import dev.metaschema.core.metapath.item.IItem;
24  import dev.metaschema.core.metapath.item.IItemVisitor;
25  import dev.metaschema.core.metapath.item.ISequence;
26  import dev.metaschema.core.metapath.item.atomic.IAnyAtomicItem;
27  import dev.metaschema.core.metapath.item.function.impl.AbstractArrayItem;
28  import dev.metaschema.core.metapath.item.function.impl.ArrayItemN;
29  import dev.metaschema.core.metapath.type.IItemType;
30  import dev.metaschema.core.util.ObjectUtils;
31  import edu.umd.cs.findbugs.annotations.NonNull;
32  
33  /**
34   * A representation of a Metapath array item type.
35   * <p>
36   * Instances of this interface are required to enforce non-mutability for array
37   * contents.
38   *
39   * @param <ITEM>
40   *          the Metapath item type of array members
41   */
42  public interface IArrayItem<ITEM extends ICollectionValue> extends IFunction, List<ITEM> {
43    /**
44     * Get the type information for this item.
45     *
46     * @return the type information
47     */
48    @NonNull
49    static IItemType type() {
50      return IItemType.array();
51    }
52  
53    @Override
54    default IItemType getType() {
55      return type();
56    }
57  
58    /**
59     * Get an empty, immutable array item.
60     *
61     * @param <T>
62     *          the item Java type
63     * @return an immutable map item
64     */
65    @NonNull
66    static <T extends ICollectionValue> IArrayItem<T> empty() {
67      return AbstractArrayItem.empty();
68    }
69  
70    @Override
71    List<ITEM> getValue();
72  
73    @Override
74    default boolean hasValue() {
75      return true;
76    }
77  
78    @Override
79    default ISequence<?> contentsAsSequence() {
80      return ISequence.of(ObjectUtils.notNull(stream()
81          .flatMap(ICollectionValue::normalizeAsItems)));
82    }
83  
84    @Override
85    default Stream<IAnyAtomicItem> atomize() {
86      return ObjectUtils.notNull(stream().flatMap(ICollectionValue::atomize));
87    }
88  
89    /**
90     * Determine if this sequence is empty.
91     *
92     * @return {@code true} if the sequence contains no items, or {@code false}
93     *         otherwise
94     */
95    @Override
96    default boolean isEmpty() {
97      return getValue().isEmpty();
98    }
99  
100   /**
101    * Get the count of items in this sequence.
102    *
103    * @return the count of items
104    */
105   @Override
106   default int size() {
107     return getValue().size();
108 
109   }
110 
111   @Override
112   default boolean contains(Object obj) {
113     return getValue().contains(obj);
114   }
115 
116   @Override
117   default Object[] toArray() {
118     return getValue().toArray();
119   }
120 
121   @Override
122   default <T> T[] toArray(T[] array) {
123     return getValue().toArray(array);
124   }
125 
126   @Override
127   default boolean containsAll(Collection<?> collection) {
128     return getValue().containsAll(collection);
129   }
130 
131   @Override
132   default ITEM get(int index) {
133     return getValue().get(index);
134   }
135 
136   @Override
137   default int indexOf(Object obj) {
138     return getValue().indexOf(obj);
139   }
140 
141   @Override
142   default int lastIndexOf(Object obj) {
143     return getValue().lastIndexOf(obj);
144   }
145 
146   @Override
147   default ListIterator<ITEM> listIterator() {
148     return getValue().listIterator();
149   }
150 
151   @Override
152   default ListIterator<ITEM> listIterator(int index) {
153     return getValue().listIterator(index);
154   }
155 
156   @Override
157   default List<ITEM> subList(int fromIndex, int toIndex) {
158     return getValue().subList(fromIndex, toIndex);
159   }
160 
161   /**
162    * A {@link Collector} implementation to generates a sequence from a stream of
163    * Metapath items.
164    *
165    * @param <T>
166    *          the Java type of the items
167    * @return a collector that will generate a sequence
168    */
169   @NonNull
170   static <T extends ICollectionValue> Collector<T, ?, IArrayItem<T>> toArrayItem() {
171     return new Collector<T, List<T>, IArrayItem<T>>() {
172 
173       @Override
174       public Supplier<List<T>> supplier() {
175         return ArrayList::new;
176       }
177 
178       @Override
179       public BiConsumer<List<T>, T> accumulator() {
180         return List::add;
181       }
182 
183       @Override
184       public BinaryOperator<List<T>> combiner() {
185         return (list1, list2) -> {
186           list1.addAll(list2);
187           return list1;
188         };
189       }
190 
191       @Override
192       public Function<List<T>, IArrayItem<T>> finisher() {
193         return list -> ofCollection(ObjectUtils.notNull(list));
194       }
195 
196       @Override
197       public Set<Characteristics> characteristics() {
198         return Collections.emptySet();
199       }
200     };
201   }
202 
203   @Override
204   default ISequence<? extends IArrayItem<ITEM>> toSequence() {
205     return ISequence.of(this);
206   }
207 
208   @SuppressWarnings("null")
209   @Override
210   default Stream<? extends IItem> flatten() {
211     return stream()
212         .flatMap(ICollectionValue::flatten);
213   }
214 
215   /**
216    * Get a new, immutable array item that contains the items in the provided list.
217    *
218    * @param <T>
219    *          the item Java type
220    * @param items
221    *          the list whose items are to be added to the new array
222    * @return an array item containing the specified entries
223    */
224   @NonNull
225   static <T extends ICollectionValue> IArrayItem<T> ofCollection(
226       @NonNull List<T> items) {
227     return items.isEmpty() ? empty() : new ArrayItemN<>(items);
228   }
229 
230   /**
231    * Returns an unmodifiable array item containing zero elements.
232    *
233    * @param <T>
234    *          the item type
235    * @return an empty {@code IArrayItem}
236    */
237   @NonNull
238   static <T extends ICollectionValue> IArrayItem<T> of() {
239     return AbstractArrayItem.empty();
240   }
241 
242   /**
243    * Returns an unmodifiable array item containing one item.
244    *
245    * @param <T>
246    *          the {@code IArrayItem}'s item type
247    * @param e1
248    *          the single item
249    * @return an {@code IArrayItem} containing the specified item
250    * @throws NullPointerException
251    *           if the item is {@code null}
252    */
253   @NonNull
254   static <T extends ICollectionValue> IArrayItem<T> of(@NonNull T e1) {
255     return new ArrayItemN<>(e1);
256   }
257 
258   /**
259    * Returns an unmodifiable array item containing two items.
260    *
261    * @param <T>
262    *          the {@code IArrayItem}'s item type
263    * @param e1
264    *          the first item
265    * @param e2
266    *          the second item
267    * @return an {@code IArrayItem} containing the specified items
268    * @throws NullPointerException
269    *           if an item is {@code null}
270    */
271   @NonNull
272   static <T extends ICollectionValue> IArrayItem<T> of(@NonNull T e1, @NonNull T e2) {
273     return new ArrayItemN<>(e1, e2);
274   }
275 
276   /**
277    * Returns an unmodifiable array item containing three elements.
278    *
279    * @param <T>
280    *          the {@code IArrayItem}'s item type
281    * @param e1
282    *          the first item
283    * @param e2
284    *          the second item
285    * @param e3
286    *          the third item
287    * @return an {@code IArrayItem} containing the specified items
288    * @throws NullPointerException
289    *           if an item is {@code null}
290    */
291   @NonNull
292   static <T extends ICollectionValue> IArrayItem<T> of(@NonNull T e1, @NonNull T e2, @NonNull T e3) {
293     return new ArrayItemN<>(e1, e2, e3);
294   }
295 
296   /**
297    * Returns an unmodifiable array item containing four items.
298    *
299    * @param <T>
300    *          the {@code IArrayItem}'s item type
301    * @param e1
302    *          the first item
303    * @param e2
304    *          the second item
305    * @param e3
306    *          the third item
307    * @param e4
308    *          the fourth item
309    * @return an {@code IArrayItem} containing the specified items
310    * @throws NullPointerException
311    *           if an item is {@code null}
312    */
313   @NonNull
314   static <T extends ICollectionValue> IArrayItem<T> of(@NonNull T e1, @NonNull T e2, @NonNull T e3, @NonNull T e4) {
315     return new ArrayItemN<>(e1, e2, e3, e4);
316   }
317 
318   /**
319    * Returns an unmodifiable array item containing five items.
320    *
321    * @param <T>
322    *          the {@code IArrayItem}'s item type
323    * @param e1
324    *          the first item
325    * @param e2
326    *          the second item
327    * @param e3
328    *          the third item
329    * @param e4
330    *          the fourth item
331    * @param e5
332    *          the fifth item
333    * @return an {@code IArrayItem} containing the specified items
334    * @throws NullPointerException
335    *           if an item is {@code null}
336    */
337   @NonNull
338   static <T extends ICollectionValue> IArrayItem<T> of(@NonNull T e1, @NonNull T e2, @NonNull T e3, @NonNull T e4,
339       @NonNull T e5) {
340     return new ArrayItemN<>(e1, e2, e3, e4, e5);
341   }
342 
343   /**
344    * Returns an unmodifiable array item containing six items.
345    *
346    * @param <T>
347    *          the {@code IArrayItem}'s item type
348    * @param e1
349    *          the first item
350    * @param e2
351    *          the second item
352    * @param e3
353    *          the third item
354    * @param e4
355    *          the fourth item
356    * @param e5
357    *          the fifth item
358    * @param e6
359    *          the sixth item
360    * @return an {@code IArrayItem} containing the specified items
361    * @throws NullPointerException
362    *           if an item is {@code null}
363    */
364   @NonNull
365   static <T extends ICollectionValue> IArrayItem<T> of(@NonNull T e1, @NonNull T e2, @NonNull T e3, @NonNull T e4,
366       @NonNull T e5, @NonNull T e6) {
367     return new ArrayItemN<>(e1, e2, e3, e4, e5, e6);
368   }
369 
370   /**
371    * Returns an unmodifiable array item containing seven items.
372    *
373    * @param <T>
374    *          the {@code IArrayItem}'s item type
375    * @param e1
376    *          the first item
377    * @param e2
378    *          the second item
379    * @param e3
380    *          the third item
381    * @param e4
382    *          the fourth item
383    * @param e5
384    *          the fifth item
385    * @param e6
386    *          the sixth item
387    * @param e7
388    *          the seventh item
389    * @return an {@code IArrayItem} containing the specified items
390    * @throws NullPointerException
391    *           if an item is {@code null}
392    */
393   @NonNull
394   static <T extends ICollectionValue> IArrayItem<T> of(@NonNull T e1, @NonNull T e2, @NonNull T e3, @NonNull T e4,
395       @NonNull T e5, @NonNull T e6, @NonNull T e7) {
396     return new ArrayItemN<>(e1, e2, e3, e4, e5, e6, e7);
397   }
398 
399   /**
400    * Returns an unmodifiable array item containing eight items.
401    *
402    * @param <T>
403    *          the {@code IArrayItem}'s item type
404    * @param e1
405    *          the first item
406    * @param e2
407    *          the second item
408    * @param e3
409    *          the third item
410    * @param e4
411    *          the fourth item
412    * @param e5
413    *          the fifth item
414    * @param e6
415    *          the sixth item
416    * @param e7
417    *          the seventh item
418    * @param e8
419    *          the eighth item
420    * @return an {@code IArrayItem} containing the specified items
421    * @throws NullPointerException
422    *           if an item is {@code null}
423    */
424   @NonNull
425   static <T extends ICollectionValue> IArrayItem<T> of(@NonNull T e1, @NonNull T e2, @NonNull T e3, @NonNull T e4,
426       @NonNull T e5, @NonNull T e6, @NonNull T e7, @NonNull T e8) {
427     return new ArrayItemN<>(e1, e2, e3, e4, e5, e6, e7, e8);
428   }
429 
430   /**
431    * Returns an unmodifiable array item containing nine items.
432    *
433    * @param <T>
434    *          the {@code IArrayItem}'s item type
435    * @param e1
436    *          the first item
437    * @param e2
438    *          the second item
439    * @param e3
440    *          the third item
441    * @param e4
442    *          the fourth item
443    * @param e5
444    *          the fifth item
445    * @param e6
446    *          the sixth item
447    * @param e7
448    *          the seventh item
449    * @param e8
450    *          the eighth item
451    * @param e9
452    *          the ninth item
453    * @return an {@code IArrayItem} containing the specified items
454    * @throws NullPointerException
455    *           if an item is {@code null}
456    */
457   @NonNull
458   static <T extends ICollectionValue> IArrayItem<T> of(@NonNull T e1, @NonNull T e2, @NonNull T e3, @NonNull T e4,
459       @NonNull T e5, @NonNull T e6, @NonNull T e7, @NonNull T e8, @NonNull T e9) {
460     return new ArrayItemN<>(e1, e2, e3, e4, e5, e6, e7, e8, e9);
461   }
462 
463   /**
464    * Returns an unmodifiable array item containing ten items.
465    *
466    * @param <T>
467    *          the {@code IArrayItem}'s item type
468    * @param e1
469    *          the first item
470    * @param e2
471    *          the second item
472    * @param e3
473    *          the third item
474    * @param e4
475    *          the fourth item
476    * @param e5
477    *          the fifth item
478    * @param e6
479    *          the sixth item
480    * @param e7
481    *          the seventh item
482    * @param e8
483    *          the eighth item
484    * @param e9
485    *          the ninth item
486    * @param e10
487    *          the tenth item
488    * @return an {@code IArrayItem} containing the specified items
489    * @throws NullPointerException
490    *           if an item is {@code null}
491    */
492   @NonNull
493   static <T extends ICollectionValue> IArrayItem<T> of(@NonNull T e1, @NonNull T e2, @NonNull T e3, @NonNull T e4,
494       @NonNull T e5, @NonNull T e6, @NonNull T e7, @NonNull T e8, @NonNull T e9, @NonNull T e10) {
495     return new ArrayItemN<>(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
496   }
497 
498   /**
499    * Returns an unmodifiable array item containing an arbitrary number of items.
500    *
501    * @param <T>
502    *          the {@code IArrayItem}'s item type
503    * @param items
504    *          the items to be contained in the list
505    * @return an {@code IArrayItem} containing the specified items
506    * @throws NullPointerException
507    *           if an item is {@code null} or if the array is {@code null}
508    */
509   @SafeVarargs
510   @NonNull
511   static <T extends ICollectionValue> IArrayItem<T> of(@NonNull T... items) {
512     return items.length == 0 ? empty() : new ArrayItemN<>(items);
513   }
514 
515   /**
516    * Returns an unmodifiable array item containing the items of the given
517    * Collection, in its iteration order. The given Collection must not be null,
518    * and it must not contain any null items. If the given Collection is
519    * subsequently modified, the returned array item will not reflect such
520    * modifications.
521    *
522    * @param <T>
523    *          the {@code IArrayItem}'s item type
524    * @param collection
525    *          a {@code Collection} from which items are drawn, must be non-null
526    * @return an {@code IArrayItem} containing the items of the given
527    *         {@code Collection}
528    * @throws NullPointerException
529    *           if collection is null, or if it contains any nulls
530    */
531   @SuppressWarnings("unchecked")
532   @NonNull
533   static <T extends ICollectionValue> IArrayItem<T> copyOf(@NonNull Collection<? extends T> collection) {
534     return collection instanceof IArrayItem
535         ? (IArrayItem<T>) collection
536         : collection.isEmpty()
537             ? empty()
538             : new ArrayItemN<>(new ArrayList<>(collection));
539   }
540 
541   @Override
542   default void accept(IItemVisitor visitor) {
543     visitor.visit(this);
544   }
545 }