c171a82f7e62e5650a0bef8cc258809876c919d6
[yangtools.git] / common / util / src / main / java / org / opendaylight / yangtools / util / ImmutableOffsetMapTemplate.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 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.base.MoreObjects;
14 import com.google.common.collect.ImmutableMap;
15 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
16 import java.util.Arrays;
17 import java.util.Collection;
18 import java.util.Map;
19 import java.util.Map.Entry;
20 import java.util.Objects;
21 import java.util.Set;
22 import java.util.function.BiFunction;
23 import org.eclipse.jdt.annotation.NonNull;
24
25 /**
26  * Template for instantiating {@link ImmutableOffsetMap} instances with a fixed set of keys. The template can then be
27  * used as a factory for instances via using {@link #instantiateTransformed(Map, BiFunction)} or, more efficiently,
28  * using {@link #instantiateWithValues(Object[])} where the argument array has values ordered corresponding to the key
29  * order defined by {@link #keySet()}.
30  *
31  * @param <K> the type of keys maintained by this template
32  */
33 public abstract class ImmutableOffsetMapTemplate<K> extends ImmutableMapTemplate<K> {
34     private static final class Ordered<K> extends ImmutableOffsetMapTemplate<K> {
35         Ordered(final Collection<K> keys) {
36             super(OffsetMapCache.orderedOffsets(keys));
37         }
38
39         @Override
40         <V> @NonNull ImmutableOffsetMap<K, V> createMap(final ImmutableMap<K, Integer> offsets, final V[] objects) {
41             return new ImmutableOffsetMap.Ordered<>(offsets, objects);
42         }
43     }
44
45     private static final class Unordered<K> extends ImmutableOffsetMapTemplate<K> {
46         Unordered(final Collection<K> keys) {
47             super(OffsetMapCache.unorderedOffsets(keys));
48         }
49
50         @Override
51         <V> @NonNull ImmutableOffsetMap<K, V> createMap(final ImmutableMap<K, Integer> offsets, final V[] objects) {
52             return new ImmutableOffsetMap.Unordered<>(offsets, objects);
53         }
54     }
55
56     private final @NonNull ImmutableMap<K, Integer> offsets;
57
58     ImmutableOffsetMapTemplate(final ImmutableMap<K, Integer> offsets) {
59         this.offsets = requireNonNull(offsets);
60     }
61
62     /**
63      * Create a template which produces Maps with specified keys, with iteration order matching the iteration order
64      * of {@code keys}. {@link #keySet()} will return these keys in exactly the same order. The resulting map will
65      * retain insertion order through {@link UnmodifiableMapPhase#toModifiableMap()} transformations.
66      *
67      * @param keys Keys in requested iteration order.
68      * @param <K> the type of keys maintained by resulting template
69      * @return A template object.
70      * @throws NullPointerException if {@code keys} or any of its elements is null
71      * @throws IllegalArgumentException if {@code keys} is does not have at least two keys
72      */
73     public static <K> @NonNull ImmutableOffsetMapTemplate<K> ordered(final Collection<K> keys) {
74         checkArgument(keys.size() > 1);
75         return new Ordered<>(keys);
76     }
77
78     /**
79      * Create a template which produces Maps with specified keys, with unconstrained iteration order. Produced maps
80      * will have the iteration order matching the order returned by {@link #keySet()}.  The resulting map will
81      * NOT retain ordering through {@link UnmodifiableMapPhase#toModifiableMap()} transformations.
82      *
83      * @param keys Keys in any iteration order.
84      * @param <K> the type of keys maintained by resulting template
85      * @return A template object.
86      * @throws NullPointerException if {@code keys} or any of its elements is null
87      * @throws IllegalArgumentException if {@code keys} is does not have at least two keys
88      */
89     public static <K> @NonNull ImmutableOffsetMapTemplate<K> unordered(final Collection<K> keys) {
90         checkArgument(keys.size() > 1);
91         return new Unordered<>(keys);
92     }
93
94     @Override
95     public final Set<K> keySet() {
96         return offsets.keySet();
97     }
98
99     @Override
100     public final <T, V> @NonNull ImmutableOffsetMap<K, V> instantiateTransformed(final Map<K, T> fromMap,
101             final BiFunction<K, T, V> valueTransformer) {
102         final int size = offsets.size();
103         checkArgument(fromMap.size() == size);
104
105         @SuppressWarnings("unchecked")
106         final V[] objects = (V[]) new Object[size];
107         for (Entry<K, T> entry : fromMap.entrySet()) {
108             final K key = requireNonNull(entry.getKey());
109             objects[offsetOf(key)] = transformValue(key, entry.getValue(), valueTransformer);
110         }
111
112         return createMap(offsets, objects);
113     }
114
115     @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE",
116         justification = "SpotBugs does not grok checkArgument()")
117     private int offsetOf(final K key) {
118         final Integer offset = offsets.get(key);
119         checkArgument(offset != null, "Key %s present in input, but not in offsets %s", key, offsets);
120         return offset;
121     }
122
123     @Override
124     @SafeVarargs
125     public final <V> @NonNull ImmutableOffsetMap<K, V> instantiateWithValues(final V... values) {
126         checkArgument(values.length == offsets.size());
127         final V[] copy = values.clone();
128         Arrays.stream(copy).forEach(Objects::requireNonNull);
129         return createMap(offsets, values);
130     }
131
132     @Override
133     public final String toString() {
134         return MoreObjects.toStringHelper(this).add("offsets", offsets).toString();
135     }
136
137     abstract <V> @NonNull ImmutableOffsetMap<K, V> createMap(ImmutableMap<K, Integer> offsets, V[] objects);
138 }