2 * Copyright (c) 2018 Pantheon Technologies, s.r.o. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.util;
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
13 import com.google.common.annotations.Beta;
14 import com.google.common.base.MoreObjects;
15 import com.google.common.collect.ImmutableMap;
16 import java.util.Arrays;
17 import java.util.Collection;
18 import java.util.Iterator;
20 import java.util.Map.Entry;
21 import java.util.Objects;
23 import java.util.function.BiFunction;
24 import org.eclipse.jdt.annotation.NonNull;
25 import org.opendaylight.yangtools.concepts.Immutable;
28 * Template for instantiating {@link UnmodifiableMapPhase} instances with a fixed set of keys. The template can then be
29 * used as a factory for instances via using {@link #instantiateTransformed(Map, BiFunction)} or, more efficiently,
30 * using {@link #instantiateWithValues(Object[])} where the argument array has values ordered corresponding to the key
31 * order defined by {@link #keySet()}.
33 * @param <K> the type of keys maintained by this template
36 public abstract class ImmutableMapTemplate<K> implements Immutable {
37 private abstract static class AbstractMultiple<K> extends ImmutableMapTemplate<K> {
38 private final @NonNull ImmutableMap<K, Integer> offsets;
40 AbstractMultiple(final ImmutableMap<K, Integer> offsets) {
41 this.offsets = requireNonNull(offsets);
45 public final Set<K> keySet() {
46 return offsets.keySet();
50 public final <T, V> @NonNull ImmutableOffsetMap<K, V> instantiateTransformed(final Map<K, T> fromMap,
51 final BiFunction<K, T, V> valueTransformer) {
52 final int size = offsets.size();
53 checkArgument(fromMap.size() == size);
55 @SuppressWarnings("unchecked")
56 final V[] objects = (V[]) new Object[size];
57 for (Entry<K, T> entry : fromMap.entrySet()) {
58 final K key = requireNonNull(entry.getKey());
59 final Integer offset = offsets.get(key);
60 checkArgument(offset != null, "Key %s present in input, but not in offsets %s", key, offsets);
62 objects[offset.intValue()] = transformValue(key, entry.getValue(), valueTransformer);
65 return createMap(offsets, objects);
70 public final <V> @NonNull UnmodifiableMapPhase<K, V> instantiateWithValues(final V... values) {
71 checkArgument(values.length == offsets.size());
72 final V[] copy = values.clone();
73 Arrays.stream(copy).forEach(Objects::requireNonNull);
74 return createMap(offsets, values);
78 public final String toString() {
79 return MoreObjects.toStringHelper(this).add("offsets", offsets).toString();
82 abstract <V> @NonNull ImmutableOffsetMap<K, V> createMap(ImmutableMap<K, Integer> offsets, V[] objects);
85 private static final class Ordered<K> extends AbstractMultiple<K> {
86 Ordered(final Collection<K> keys) {
87 super(OffsetMapCache.orderedOffsets(keys));
91 <V> @NonNull ImmutableOffsetMap<K, V> createMap(final ImmutableMap<K, Integer> offsets, final V[] objects) {
92 return new ImmutableOffsetMap.Ordered<>(offsets, objects);
96 private static final class Unordered<K> extends AbstractMultiple<K> {
97 Unordered(final Collection<K> keys) {
98 super(OffsetMapCache.unorderedOffsets(keys));
102 <V> @NonNull ImmutableOffsetMap<K, V> createMap(final ImmutableMap<K, Integer> offsets, final V[] objects) {
103 return new ImmutableOffsetMap.Unordered<>(offsets, objects);
107 private abstract static class AbstractSingle<K> extends ImmutableMapTemplate<K> {
108 private final @NonNull SingletonSet<K> keySet;
110 AbstractSingle(final K key) {
111 this.keySet = SharedSingletonMap.cachedSet(key);
115 public Set<K> keySet() {
120 public final <T, V> @NonNull SharedSingletonMap<K, V> instantiateTransformed(final Map<K, T> fromMap,
121 final BiFunction<K, T, V> valueTransformer) {
122 final Iterator<Entry<K, T>> it = fromMap.entrySet().iterator();
123 checkArgument(it.hasNext(), "Input is empty while expecting 1 item");
125 final Entry<K, T> entry = it.next();
126 final K expected = keySet.getElement();
127 final K actual = entry.getKey();
128 checkArgument(expected.equals(actual), "Unexpected key %s, expecting %s", actual, expected);
130 final V value = transformValue(actual, entry.getValue(), valueTransformer);
131 checkArgument(!it.hasNext(), "Input has more than one item");
133 return createMap(keySet, value);
138 public final <V> @NonNull UnmodifiableMapPhase<K, V> instantiateWithValues(final V... values) {
139 checkArgument(values.length == 1);
140 return createMap(keySet, values[0]);
144 public final String toString() {
145 return MoreObjects.toStringHelper(this).add("keySet", keySet).toString();
148 abstract <V> @NonNull SharedSingletonMap<K, V> createMap(SingletonSet<K> keySet, V value);
151 private static final class SingleOrdered<K> extends AbstractSingle<K> {
152 SingleOrdered(final K key) {
157 <V> @NonNull SharedSingletonMap<K, V> createMap(final SingletonSet<K> keySet, final V value) {
158 return new SharedSingletonMap.Ordered<>(keySet, value);
162 private static final class SingleUnordered<K> extends AbstractSingle<K> {
163 SingleUnordered(final K key) {
168 <V> @NonNull SharedSingletonMap<K, V> createMap(final SingletonSet<K> keySet, final V value) {
169 return new SharedSingletonMap.Unordered<>(keySet, value);
173 ImmutableMapTemplate() {
178 * Create a template which produces Maps with specified keys, with iteration order matching the iteration order
179 * of {@code keys}. {@link #keySet()} will return these keys in exactly the same order. The resulting map will
180 * retain insertion order through {@link UnmodifiableMapPhase#toModifiableMap()} transformations.
182 * @param keys Keys in requested iteration order.
183 * @param <K> the type of keys maintained by resulting template
184 * @return A template object.
185 * @throws NullPointerException if {@code keys} or any of its elements is null
186 * @throws IllegalArgumentException if {@code keys} is empty
188 public static <K> @NonNull ImmutableMapTemplate<K> ordered(final Collection<K> keys) {
189 switch (keys.size()) {
191 throw new IllegalArgumentException("Proposed keyset must not be empty");
193 return new SingleOrdered<>(keys.iterator().next());
195 return new Ordered<>(keys);
200 * Create a template which produces Maps with specified keys, with unconstrained iteration order. Produced maps
201 * will have the iteration order matching the order returned by {@link #keySet()}. The resulting map will
202 * NOT retain ordering through {@link UnmodifiableMapPhase#toModifiableMap()} transformations.
204 * @param keys Keys in any iteration order.
205 * @param <K> the type of keys maintained by resulting template
206 * @return A template object.
207 * @throws NullPointerException if {@code keys} or any of its elements is null
208 * @throws IllegalArgumentException if {@code keys} is empty
210 public static <K> @NonNull ImmutableMapTemplate<K> unordered(final Collection<K> keys) {
211 switch (keys.size()) {
213 throw new IllegalArgumentException("Proposed keyset must not be empty");
215 return new SingleUnordered<>(keys.iterator().next());
217 return new Unordered<>(keys);
222 * Instantiate an immutable map by applying specified {@code transformer} to values of {@code fromMap}.
224 * @param fromMap Input map
225 * @param keyValueTransformer Transformation to apply to values
226 * @param <T> the type of input values
227 * @param <V> the type of mapped values
228 * @return An immutable map
229 * @throws NullPointerException if any of the arguments is null or if the transformer produces a {@code null} value
230 * @throws IllegalArgumentException if {@code fromMap#keySet()} does not match this template's keys
232 public abstract <T, V> @NonNull UnmodifiableMapPhase<K, V> instantiateTransformed(Map<K, T> fromMap,
233 BiFunction<K, T, V> keyValueTransformer);
236 * Instantiate an immutable map by filling values from provided array. The array MUST be ordered to match key order
237 * as returned by {@link #keySet()}.
239 * @param values Values to use
240 * @param <V> the type of mapped values
241 * @return An immutable map
242 * @throws NullPointerException if {@code values} or any of its elements is null
243 * @throws IllegalArgumentException if {@code values.lenght} does not match the number of keys in this template
245 @SuppressWarnings("unchecked")
246 public abstract <V> @NonNull UnmodifiableMapPhase<K, V> instantiateWithValues(V... values);
249 * Returns the set of keys expected by this template, in the iteration order Maps resulting from instantiation
252 * @return This template's key set
255 public abstract Set<K> keySet();
257 final <T, V> @NonNull V transformValue(final K key, final T input, final BiFunction<K, T, V> transformer) {
258 final V value = transformer.apply(key, input);
259 checkArgument(value != null, "Transformer returned null for input %s at key %s", input, key);