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.Collections;
9 import java.util.LinkedHashMap;
10 import java.util.Map;
11 import java.util.Set;
12 import java.util.function.BiConsumer;
13 import java.util.function.BinaryOperator;
14 import java.util.function.Function;
15 import java.util.function.Supplier;
16 import java.util.stream.Collector;
17
18 import dev.metaschema.core.metapath.function.IFunction;
19 import dev.metaschema.core.metapath.item.ICollectionValue;
20 import dev.metaschema.core.metapath.item.IItemVisitor;
21 import dev.metaschema.core.metapath.item.ISequence;
22 import dev.metaschema.core.metapath.item.atomic.IAnyAtomicItem;
23 import dev.metaschema.core.metapath.item.function.impl.AbstractMapItem;
24 import dev.metaschema.core.metapath.item.function.impl.MapItemN;
25 import dev.metaschema.core.metapath.type.IItemType;
26 import dev.metaschema.core.util.ObjectUtils;
27 import edu.umd.cs.findbugs.annotations.NonNull;
28
29 /**
30 * Represents a mapping of {@link IMapKey} keys to values.
31 *
32 * @param <VALUE>
33 * the value type
34 */
35 public interface IMapItem<VALUE extends ICollectionValue>
36 extends IFunction, Map<IMapKey, VALUE> {
37 /**
38 * Get the type information for this item.
39 *
40 * @return the type information
41 */
42 @NonNull
43 static IItemType type() {
44 return IItemType.map();
45 }
46
47 @Override
48 default IItemType getType() {
49 return type();
50 }
51
52 /**
53 * Get an empty, immutable map item.
54 *
55 * @param <V>
56 * the value Java type
57 * @return an immutable map item
58 */
59 @NonNull
60 static <V extends ICollectionValue> IMapItem<V> empty() {
61 return AbstractMapItem.empty();
62 }
63
64 @Override
65 Map<IMapKey, VALUE> getValue();
66
67 @Override
68 default boolean hasValue() {
69 return true;
70 }
71
72 @Override
73 default ISequence<?> contentsAsSequence() {
74 return ISequence.of(ObjectUtils.notNull(values().stream()
75 .flatMap(ICollectionValue::normalizeAsItems)));
76 }
77
78 /**
79 * Determine if this sequence is empty.
80 *
81 * @return {@code true} if the sequence contains no items, or {@code false}
82 * otherwise
83 */
84 @Override
85 default boolean isEmpty() {
86 return getValue().isEmpty();
87 }
88
89 /**
90 * Get the count of items in this sequence.
91 *
92 * @return the count of items
93 */
94 @Override
95 default int size() {
96 return getValue().size();
97
98 }
99
100 /**
101 * A {@link Collector} implementation to generates a sequence from a stream of
102 * Metapath items.
103 *
104 * @param <T>
105 * the Java type of the items
106 * @return a collector that will generate a sequence
107 */
108 @NonNull
109 static <T extends ICollectionValue> Collector<Map.Entry<IMapKey, T>, ?, IMapItem<T>> toMapItem() {
110 return new Collector<Map.Entry<IMapKey, T>, Map<IMapKey, T>, IMapItem<T>>() {
111
112 @Override
113 public Supplier<Map<IMapKey, T>> supplier() {
114 return LinkedHashMap::new;
115 }
116
117 @Override
118 public BiConsumer<Map<IMapKey, T>, Map.Entry<IMapKey, T>> accumulator() {
119 return (map, entry) -> map.putIfAbsent(entry.getKey(), entry.getValue());
120 }
121
122 @Override
123 public BinaryOperator<Map<IMapKey, T>> combiner() {
124 return (map1, map2) -> {
125 map1.putAll(map2);
126 return map1;
127 };
128 }
129
130 @Override
131 public Function<Map<IMapKey, T>, IMapItem<T>> finisher() {
132 return map -> ofCollection(ObjectUtils.notNull(map));
133 }
134
135 @Override
136 public Set<Characteristics> characteristics() {
137 return Collections.emptySet();
138 }
139 };
140 }
141
142 @Override
143 default ISequence<IMapItem<VALUE>> toSequence() {
144 return ISequence.of(this);
145 }
146
147 /**
148 * Get a new, immutable map item that contains the items in the provided map.
149 *
150 * @param <V>
151 * the value Java type
152 * @param map
153 * the map whose items are to be added to the new map
154 * @return a map item containing the specified entries
155 */
156 @NonNull
157 static <V extends ICollectionValue> IMapItem<V> ofCollection(
158 @NonNull Map<IMapKey, V> map) {
159 return map.isEmpty() ? empty() : new MapItemN<>(map);
160 }
161
162 /**
163 * Returns an unmodifiable map item containing zero mappings.
164 *
165 * @param <V>
166 * the value Java type
167 * @return an empty {@code IMapItem}
168 */
169 @NonNull
170 static <V extends ICollectionValue> IMapItem<V> of() {
171 return AbstractMapItem.empty();
172 }
173
174 /**
175 * Returns an unmodifiable map item containing a single mapping.
176 *
177 * @param <K>
178 * the map item's key type
179 * @param <V>
180 * the map item's value type
181 * @param k1
182 * the mapping's key
183 * @param v1
184 * the mapping's value
185 * @return a map item containing the specified mapping
186 * @throws NullPointerException
187 * if the key or the value is {@code null}
188 */
189 @NonNull
190 static <K extends IAnyAtomicItem, V extends ICollectionValue> IMapItem<V> of(@NonNull K k1, @NonNull V v1) {
191 return new MapItemN<>(entry(k1, v1));
192 }
193
194 /**
195 * Returns an unmodifiable map item containing two mappings.
196 *
197 * @param <K>
198 * the map item's key type
199 * @param <V>
200 * the map item's value type
201 * @param k1
202 * the first mapping's key
203 * @param v1
204 * the first mapping's value
205 * @param k2
206 * the second mapping's key
207 * @param v2
208 * the second mapping's value
209 * @return a map item containing the specified mappings
210 * @throws IllegalArgumentException
211 * if the keys are duplicates
212 * @throws NullPointerException
213 * if any key or value is {@code null}
214 */
215 @NonNull
216 static <K extends IAnyAtomicItem, V extends ICollectionValue> IMapItem<V> of(
217 @NonNull K k1, @NonNull V v1,
218 @NonNull K k2, @NonNull V v2) {
219 return new MapItemN<>(
220 entry(k1, v1),
221 entry(k2, v2));
222 }
223
224 /**
225 * Returns an unmodifiable map item containing three mappings.
226 *
227 * @param <K>
228 * the map item's key type
229 * @param <V>
230 * the map item's value type
231 * @param k1
232 * the first mapping's key
233 * @param v1
234 * the first mapping's value
235 * @param k2
236 * the second mapping's key
237 * @param v2
238 * the second mapping's value
239 * @param k3
240 * the third mapping's key
241 * @param v3
242 * the third mapping's value
243 * @return a map item containing the specified mappings
244 * @throws IllegalArgumentException
245 * if there are any duplicate keys
246 * @throws NullPointerException
247 * if any key or value is {@code null}
248 */
249 @NonNull
250 static <K extends IAnyAtomicItem, V extends ICollectionValue>
251 IMapItem<V> of(
252 @NonNull K k1, @NonNull V v1,
253 @NonNull K k2, @NonNull V v2,
254 @NonNull K k3, @NonNull V v3) {
255 return new MapItemN<>(
256 entry(k1, v1),
257 entry(k2, v2),
258 entry(k3, v3));
259 }
260
261 /**
262 * Returns an unmodifiable map item containing four mappings.
263 *
264 * @param <K>
265 * the map item's key type
266 * @param <V>
267 * the map item's value type
268 * @param k1
269 * the first mapping's key
270 * @param v1
271 * the first mapping's value
272 * @param k2
273 * the second mapping's key
274 * @param v2
275 * the second mapping's value
276 * @param k3
277 * the third mapping's key
278 * @param v3
279 * the third mapping's value
280 * @param k4
281 * the fourth mapping's key
282 * @param v4
283 * the fourth mapping's value
284 * @return a map item containing the specified mappings
285 * @throws IllegalArgumentException
286 * if there are any duplicate keys
287 * @throws NullPointerException
288 * if any key or value is {@code null}
289 */
290 @NonNull
291 static <K extends IAnyAtomicItem, V extends ICollectionValue>
292 IMapItem<V> of(
293 @NonNull K k1, @NonNull V v1,
294 @NonNull K k2, @NonNull V v2,
295 @NonNull K k3, @NonNull V v3,
296 @NonNull K k4, @NonNull V v4) {
297 return new MapItemN<>(
298 entry(k1, v1),
299 entry(k2, v2),
300 entry(k3, v3),
301 entry(k4, v4));
302 }
303
304 /**
305 * Returns an unmodifiable map item containing five mappings.
306 *
307 * @param <K>
308 * the map item's key type
309 * @param <V>
310 * the map item's value type
311 * @param k1
312 * the first mapping's key
313 * @param v1
314 * the first mapping's value
315 * @param k2
316 * the second mapping's key
317 * @param v2
318 * the second mapping's value
319 * @param k3
320 * the third mapping's key
321 * @param v3
322 * the third mapping's value
323 * @param k4
324 * the fourth mapping's key
325 * @param v4
326 * the fourth mapping's value
327 * @param k5
328 * the fifth mapping's key
329 * @param v5
330 * the fifth mapping's value
331 * @return a map item containing the specified mappings
332 * @throws IllegalArgumentException
333 * if there are any duplicate keys
334 * @throws NullPointerException
335 * if any key or value is {@code null}
336 */
337 @NonNull
338 static <K extends IAnyAtomicItem, V extends ICollectionValue>
339 IMapItem<V> of(
340 @NonNull K k1, @NonNull V v1,
341 @NonNull K k2, @NonNull V v2,
342 @NonNull K k3, @NonNull V v3,
343 @NonNull K k4, @NonNull V v4,
344 @NonNull K k5, @NonNull V v5) {
345 return new MapItemN<>(
346 entry(k1, v1),
347 entry(k2, v2),
348 entry(k3, v3),
349 entry(k4, v4),
350 entry(k5, v5));
351 }
352
353 /**
354 * Returns an unmodifiable map item containing six mappings.
355 *
356 * @param <K>
357 * the map item's key type
358 * @param <V>
359 * the map item's value type
360 * @param k1
361 * the first mapping's key
362 * @param v1
363 * the first mapping's value
364 * @param k2
365 * the second mapping's key
366 * @param v2
367 * the second mapping's value
368 * @param k3
369 * the third mapping's key
370 * @param v3
371 * the third mapping's value
372 * @param k4
373 * the fourth mapping's key
374 * @param v4
375 * the fourth mapping's value
376 * @param k5
377 * the fifth mapping's key
378 * @param v5
379 * the fifth mapping's value
380 * @param k6
381 * the sixth mapping's key
382 * @param v6
383 * the sixth mapping's value
384 * @return a map item containing the specified mappings
385 * @throws IllegalArgumentException
386 * if there are any duplicate keys
387 * @throws NullPointerException
388 * if any key or value is {@code null}
389 */
390 @SuppressWarnings("PMD.ExcessiveParameterList")
391 @NonNull
392 static <K extends IAnyAtomicItem, V extends ICollectionValue>
393 IMapItem<V> of(
394 @NonNull K k1, @NonNull V v1,
395 @NonNull K k2, @NonNull V v2,
396 @NonNull K k3, @NonNull V v3,
397 @NonNull K k4, @NonNull V v4,
398 @NonNull K k5, @NonNull V v5,
399 @NonNull K k6, @NonNull V v6) {
400 return new MapItemN<>(
401 entry(k1, v1),
402 entry(k2, v2),
403 entry(k3, v3),
404 entry(k4, v4),
405 entry(k5, v5),
406 entry(k6, v6));
407 }
408
409 // CPD-OFF
410 /**
411 * Returns an unmodifiable map item containing seven mappings.
412 *
413 * @param <K>
414 * the map item's key type
415 * @param <V>
416 * the map item's value type
417 * @param k1
418 * the first mapping's key
419 * @param v1
420 * the first mapping's value
421 * @param k2
422 * the second mapping's key
423 * @param v2
424 * the second mapping's value
425 * @param k3
426 * the third mapping's key
427 * @param v3
428 * the third mapping's value
429 * @param k4
430 * the fourth mapping's key
431 * @param v4
432 * the fourth mapping's value
433 * @param k5
434 * the fifth mapping's key
435 * @param v5
436 * the fifth mapping's value
437 * @param k6
438 * the sixth mapping's key
439 * @param v6
440 * the sixth mapping's value
441 * @param k7
442 * the seventh mapping's key
443 * @param v7
444 * the seventh mapping's value
445 * @return a map item containing the specified mappings
446 * @throws IllegalArgumentException
447 * if there are any duplicate keys
448 * @throws NullPointerException
449 * if any key or value is {@code null}
450 */
451 @SuppressWarnings("PMD.ExcessiveParameterList")
452 @NonNull
453 static <K extends IAnyAtomicItem, V extends ICollectionValue> IMapItem<V> of(
454 @NonNull K k1, @NonNull V v1,
455 @NonNull K k2, @NonNull V v2,
456 @NonNull K k3, @NonNull V v3,
457 @NonNull K k4, @NonNull V v4,
458 @NonNull K k5, @NonNull V v5,
459 @NonNull K k6, @NonNull V v6,
460 @NonNull K k7, @NonNull V v7) {
461 return new MapItemN<>(
462 entry(k1, v1),
463 entry(k2, v2),
464 entry(k3, v3),
465 entry(k4, v4),
466 entry(k5, v5),
467 entry(k6, v6),
468 entry(k7, v7));
469 }
470
471 /**
472 * Returns an unmodifiable map item containing eight mappings. See
473 * <a href="#unmodifiable">Unmodifiable Maps</a> for details.
474 *
475 * @param <K>
476 * the map item's key type
477 * @param <V>
478 * the map item's value type
479 * @param k1
480 * the first mapping's key
481 * @param v1
482 * the first mapping's value
483 * @param k2
484 * the second mapping's key
485 * @param v2
486 * the second mapping's value
487 * @param k3
488 * the third mapping's key
489 * @param v3
490 * the third mapping's value
491 * @param k4
492 * the fourth mapping's key
493 * @param v4
494 * the fourth mapping's value
495 * @param k5
496 * the fifth mapping's key
497 * @param v5
498 * the fifth mapping's value
499 * @param k6
500 * the sixth mapping's key
501 * @param v6
502 * the sixth mapping's value
503 * @param k7
504 * the seventh mapping's key
505 * @param v7
506 * the seventh mapping's value
507 * @param k8
508 * the eighth mapping's key
509 * @param v8
510 * the eighth mapping's value
511 * @return a map item containing the specified mappings
512 * @throws IllegalArgumentException
513 * if there are any duplicate keys
514 * @throws NullPointerException
515 * if any key or value is {@code null}
516 */
517 @SuppressWarnings("PMD.ExcessiveParameterList")
518 @NonNull
519 static <K extends IAnyAtomicItem, V extends ICollectionValue>
520 IMapItem<V> of(
521 @NonNull K k1, @NonNull V v1,
522 @NonNull K k2, @NonNull V v2,
523 @NonNull K k3, @NonNull V v3,
524 @NonNull K k4, @NonNull V v4,
525 @NonNull K k5, @NonNull V v5,
526 @NonNull K k6, @NonNull V v6,
527 @NonNull K k7, @NonNull V v7,
528 @NonNull K k8, @NonNull V v8) {
529 return new MapItemN<>(
530 entry(k1, v1),
531 entry(k2, v2),
532 entry(k3, v3),
533 entry(k4, v4),
534 entry(k5, v5),
535 entry(k6, v6),
536 entry(k7, v7),
537 entry(k8, v8));
538 }
539
540 /**
541 * Returns an unmodifiable map item containing nine mappings.
542 *
543 * @param <K>
544 * the map item's key type
545 * @param <V>
546 * the map item's value type
547 * @param k1
548 * the first mapping's key
549 * @param v1
550 * the first mapping's value
551 * @param k2
552 * the second mapping's key
553 * @param v2
554 * the second mapping's value
555 * @param k3
556 * the third mapping's key
557 * @param v3
558 * the third mapping's value
559 * @param k4
560 * the fourth mapping's key
561 * @param v4
562 * the fourth mapping's value
563 * @param k5
564 * the fifth mapping's key
565 * @param v5
566 * the fifth mapping's value
567 * @param k6
568 * the sixth mapping's key
569 * @param v6
570 * the sixth mapping's value
571 * @param k7
572 * the seventh mapping's key
573 * @param v7
574 * the seventh mapping's value
575 * @param k8
576 * the eighth mapping's key
577 * @param v8
578 * the eighth mapping's value
579 * @param k9
580 * the ninth mapping's key
581 * @param v9
582 * the ninth mapping's value
583 * @return a map item containing the specified mappings
584 * @throws IllegalArgumentException
585 * if there are any duplicate keys
586 * @throws NullPointerException
587 * if any key or value is {@code null}
588 */
589 @SuppressWarnings("PMD.ExcessiveParameterList")
590 @NonNull
591 static <K extends IAnyAtomicItem, V extends ICollectionValue>
592 IMapItem<V> of(
593 @NonNull K k1, @NonNull V v1,
594 @NonNull K k2, @NonNull V v2,
595 @NonNull K k3, @NonNull V v3,
596 @NonNull K k4, @NonNull V v4,
597 @NonNull K k5, @NonNull V v5,
598 @NonNull K k6, @NonNull V v6,
599 @NonNull K k7, @NonNull V v7,
600 @NonNull K k8, @NonNull V v8,
601 @NonNull K k9, @NonNull V v9) {
602 return new MapItemN<>(entry(k1, v1), entry(k2, v2), entry(k3, v3), entry(k4, v4), entry(k5, v5), entry(k6, v6),
603 entry(k7, v7), entry(k8, v8), entry(k9, v9));
604 }
605
606 /**
607 * Returns an unmodifiable map item containing ten mappings.
608 *
609 * @param <K>
610 * the map item's key type
611 * @param <V>
612 * the map item's value type
613 * @param k1
614 * the first mapping's key
615 * @param v1
616 * the first mapping's value
617 * @param k2
618 * the second mapping's key
619 * @param v2
620 * the second mapping's value
621 * @param k3
622 * the third mapping's key
623 * @param v3
624 * the third mapping's value
625 * @param k4
626 * the fourth mapping's key
627 * @param v4
628 * the fourth mapping's value
629 * @param k5
630 * the fifth mapping's key
631 * @param v5
632 * the fifth mapping's value
633 * @param k6
634 * the sixth mapping's key
635 * @param v6
636 * the sixth mapping's value
637 * @param k7
638 * the seventh mapping's key
639 * @param v7
640 * the seventh mapping's value
641 * @param k8
642 * the eighth mapping's key
643 * @param v8
644 * the eighth mapping's value
645 * @param k9
646 * the ninth mapping's key
647 * @param v9
648 * the ninth mapping's value
649 * @param k10
650 * the tenth mapping's key
651 * @param v10
652 * the tenth mapping's value
653 * @return a map item containing the specified mappings
654 * @throws IllegalArgumentException
655 * if there are any duplicate keys
656 * @throws NullPointerException
657 * if any key or value is {@code null}
658 */
659 @SuppressWarnings("PMD.ExcessiveParameterList")
660 @NonNull
661 static <K extends IAnyAtomicItem, V extends ICollectionValue> IMapItem<V> of(
662 @NonNull K k1, @NonNull V v1,
663 @NonNull K k2, @NonNull V v2,
664 @NonNull K k3, @NonNull V v3,
665 @NonNull K k4, @NonNull V v4,
666 @NonNull K k5, @NonNull V v5,
667 @NonNull K k6, @NonNull V v6,
668 @NonNull K k7, @NonNull V v7,
669 @NonNull K k8, @NonNull V v8,
670 @NonNull K k9, @NonNull V v9,
671 @NonNull K k10, @NonNull V v10) {
672 return new MapItemN<>(
673 entry(k1, v1),
674 entry(k2, v2),
675 entry(k3, v3),
676 entry(k4, v4),
677 entry(k5, v5),
678 entry(k6, v6),
679 entry(k7, v7),
680 entry(k8, v8),
681 entry(k9, v9),
682 entry(k10, v10));
683 // CPD-ON
684 }
685
686 /**
687 * Returns an unmodifiable map item containing keys and values extracted from
688 * the given entries. The entries themselves are not stored in the map.
689 *
690 * @param <K>
691 * the map item's key type
692 * @param <V>
693 * the map item's value type
694 * @param entries
695 * {@code Map.Entry}s containing the keys and values from which the map
696 * is populated
697 * @return a map item containing the specified mappings
698 * @throws IllegalArgumentException
699 * if there are any duplicate keys
700 * @throws NullPointerException
701 * if any entry, key, or value is {@code null}, or if the
702 * {@code entries} array is {@code null}
703 */
704 @SafeVarargs
705 @SuppressWarnings("varargs")
706 @NonNull
707 static <K extends IAnyAtomicItem, V extends ICollectionValue>
708 IMapItem<V> ofEntries(Map.Entry<IMapKey, ? extends V>... entries) {
709 return entries.length == 0 ? empty() : new MapItemN<>(entries);
710 }
711
712 /**
713 * Returns an unmodifiable {@link java.util.Map.Entry} containing the given key
714 * and value.
715 *
716 * @param <V>
717 * the value's type
718 * @param key
719 * the key
720 * @param value
721 * the value
722 * @return an {@code Map.Entry} containing the specified key and value
723 * @throws NullPointerException
724 * if the key or value is {@code null}
725 */
726 @NonNull
727 static <V extends ICollectionValue> Map.Entry<IMapKey, V> entry(@NonNull IAnyAtomicItem key, @NonNull V value) {
728 return entry(key.asMapKey(), value);
729 }
730
731 /**
732 * Returns an unmodifiable {@link java.util.Map.Entry} containing the given key
733 * and value.
734 *
735 * @param <V>
736 * the value's type
737 * @param key
738 * the key
739 * @param value
740 * the value
741 * @return an {@code Map.Entry} containing the specified key and value
742 * @throws NullPointerException
743 * if the key or value is {@code null}
744 */
745 @SuppressWarnings("null")
746 @NonNull
747 static <V extends ICollectionValue> Map.Entry<IMapKey, V> entry(@NonNull IMapKey key, @NonNull V value) {
748 return Map.entry(key, value);
749 }
750
751 /**
752 * Returns an unmodifiable Map item containing the entries of the given Map. The
753 * given Map must not be null, and it must not contain any null keys or values.
754 * If the given Map is subsequently modified, the returned Map will not reflect
755 * such modifications.
756 *
757 * @param <K>
758 * the map item's key type
759 * @param <V>
760 * the map item's value type
761 * @param map
762 * a map item from which entries are drawn, must be non-null
763 * @return a map item containing the entries of the given {@code Map}
764 * @throws NullPointerException
765 * if map is null, or if it contains any null keys or values
766 */
767 @SuppressWarnings("unchecked")
768 @NonNull
769 static <K extends IAnyAtomicItem, V extends ICollectionValue>
770 IMapItem<V> copyOf(Map<? extends IMapKey, ? extends V> map) {
771 return map instanceof IMapItem
772 ? (IMapItem<V>) map
773 : map.isEmpty()
774 ? empty()
775 : new MapItemN<>(new LinkedHashMap<>(map));
776 }
777
778 @Override
779 default void accept(IItemVisitor visitor) {
780 visitor.visit(this);
781 }
782 }