Factor out {SharedSingleton,ImmutableOffset}MapTemplate
[yangtools.git] / common / util / src / main / java / org / opendaylight / yangtools / util / ImmutableMapTemplate.java
1 /*
2  * Copyright (c) 2018 Pantheon Technologies, s.r.o. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.util;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11
12 import com.google.common.annotations.Beta;
13 import java.util.Collection;
14 import java.util.Map;
15 import java.util.Set;
16 import java.util.function.BiFunction;
17 import org.eclipse.jdt.annotation.NonNull;
18 import org.opendaylight.yangtools.concepts.Immutable;
19
20 /**
21  * Template for instantiating {@link UnmodifiableMapPhase} instances with a fixed set of keys. The template can then be
22  * used as a factory for instances via using {@link #instantiateTransformed(Map, BiFunction)} or, more efficiently,
23  * using {@link #instantiateWithValues(Object[])} where the argument array has values ordered corresponding to the key
24  * order defined by {@link #keySet()}.
25  *
26  * <p>
27  * If the keySet is static known to contain only a single key, consider using {@link SharedSingletonMapTemplate}. If
28  * it is statically known to contain multiple keys, consider using {@link ImmutableOffsetMapTemplate}.
29  *
30  * @param <K> the type of keys maintained by this template
31  */
32 @Beta
33 public abstract class ImmutableMapTemplate<K> implements Immutable {
34     ImmutableMapTemplate() {
35         // Hidden on purpose
36     }
37
38     /**
39      * Create a template which produces Maps with specified keys, with iteration order matching the iteration order
40      * of {@code keys}. {@link #keySet()} will return these keys in exactly the same order. The resulting map will
41      * retain insertion order through {@link UnmodifiableMapPhase#toModifiableMap()} transformations.
42      *
43      * @param keys Keys in requested iteration order.
44      * @param <K> the type of keys maintained by resulting template
45      * @return A template object.
46      * @throws NullPointerException if {@code keys} or any of its elements is null
47      * @throws IllegalArgumentException if {@code keys} is empty
48      */
49     public static <K> @NonNull ImmutableMapTemplate<K> ordered(final Collection<K> keys) {
50         switch (keys.size()) {
51             case 0:
52                 throw new IllegalArgumentException("Proposed keyset must not be empty");
53             case 1:
54                 return SharedSingletonMapTemplate.ordered(keys.iterator().next());
55             default:
56                 return ImmutableOffsetMapTemplate.ordered(keys);
57         }
58     }
59
60     /**
61      * Create a template which produces Maps with specified keys, with unconstrained iteration order. Produced maps
62      * will have the iteration order matching the order returned by {@link #keySet()}.  The resulting map will
63      * NOT retain ordering through {@link UnmodifiableMapPhase#toModifiableMap()} transformations.
64      *
65      * @param keys Keys in any iteration order.
66      * @param <K> the type of keys maintained by resulting template
67      * @return A template object.
68      * @throws NullPointerException if {@code keys} or any of its elements is null
69      * @throws IllegalArgumentException if {@code keys} is empty
70      */
71     public static <K> @NonNull ImmutableMapTemplate<K> unordered(final Collection<K> keys) {
72         switch (keys.size()) {
73             case 0:
74                 throw new IllegalArgumentException("Proposed keyset must not be empty");
75             case 1:
76                 return SharedSingletonMapTemplate.unordered(keys.iterator().next());
77             default:
78                 return ImmutableOffsetMapTemplate.unordered(keys);
79         }
80     }
81
82     /**
83      * Instantiate an immutable map by applying specified {@code transformer} to values of {@code fromMap}.
84      *
85      * @param fromMap Input map
86      * @param keyValueTransformer Transformation to apply to values
87      * @param <T> the type of input values
88      * @param <V> the type of mapped values
89      * @return An immutable map
90      * @throws NullPointerException if any of the arguments is null or if the transformer produces a {@code null} value
91      * @throws IllegalArgumentException if {@code fromMap#keySet()} does not match this template's keys
92      */
93     public abstract <T, V> @NonNull UnmodifiableMapPhase<K, V> instantiateTransformed(Map<K, T> fromMap,
94             BiFunction<K, T, V> keyValueTransformer);
95
96     /**
97      * Instantiate an immutable map by filling values from provided array. The array MUST be ordered to match key order
98      * as returned by {@link #keySet()}.
99      *
100      * @param values Values to use
101      * @param <V> the type of mapped values
102      * @return An immutable map
103      * @throws NullPointerException if {@code values} or any of its elements is null
104      * @throws IllegalArgumentException if {@code values.length} does not match the number of keys in this template
105      */
106     @SuppressWarnings("unchecked")
107     public abstract <V> @NonNull UnmodifiableMapPhase<K, V> instantiateWithValues(V... values);
108
109     /**
110      * Returns the set of keys expected by this template, in the iteration order Maps resulting from instantiation
111      * will have.
112      *
113      * @return This template's key set
114      * @see Map#keySet()
115      */
116     public abstract Set<K> keySet();
117
118     final <T, V> @NonNull V transformValue(final K key, final T input, final BiFunction<K, T, V> transformer) {
119         final V value = transformer.apply(key, input);
120         checkArgument(value != null, "Transformer returned null for input %s at key %s", input, key);
121         return value;
122     }
123 }