Merge branch 'master' of ../controller
[yangtools.git] / common / util / src / main / java / org / opendaylight / yangtools / util / SharedSingletonMap.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. 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.annotations.Beta;
14 import com.google.common.cache.CacheBuilder;
15 import com.google.common.cache.CacheLoader;
16 import com.google.common.cache.LoadingCache;
17 import java.io.Serializable;
18 import java.util.AbstractMap.SimpleImmutableEntry;
19 import java.util.Iterator;
20 import java.util.Map;
21 import org.eclipse.jdt.annotation.NonNull;
22
23 /**
24  * Implementation of the {@link Map} interface which stores a single mapping. The key set is shared among all instances
25  * which contain the same key. This implementation does not support null keys or values.
26  *
27  * <p>
28  * In case the set of keys is statically known, you can use {@link SharedSingletonMapTemplate} to efficiently create
29  * {@link SharedSingletonMap} instances.
30  *
31  * @param <K> the type of keys maintained by this map
32  * @param <V> the type of mapped values
33  */
34 @Beta
35 public abstract class SharedSingletonMap<K, V> implements Serializable, UnmodifiableMapPhase<K, V> {
36     static final class Ordered<K, V> extends SharedSingletonMap<K, V> {
37         private static final long serialVersionUID = 1L;
38
39         Ordered(final K key, final V value) {
40             super(key, value);
41         }
42
43         Ordered(final SingletonSet<K> keySet, final V value) {
44             super(keySet, value);
45         }
46
47         @Override
48         public @NonNull ModifiableMapPhase<K, V> toModifiableMap() {
49             return MutableOffsetMap.orderedCopyOf(this);
50         }
51     }
52
53     static final class Unordered<K, V> extends SharedSingletonMap<K, V> {
54         private static final long serialVersionUID = 1L;
55
56         Unordered(final K key, final V value) {
57             super(key, value);
58         }
59
60         Unordered(final SingletonSet<K> keySet, final V value) {
61             super(keySet, value);
62         }
63
64         @Override
65         public @NonNull ModifiableMapPhase<K, V> toModifiableMap() {
66             return MutableOffsetMap.unorderedCopyOf(this);
67         }
68     }
69
70     private static final long serialVersionUID = 1L;
71     private static final LoadingCache<Object, SingletonSet<Object>> CACHE = CacheBuilder.newBuilder().weakValues()
72             .build(new CacheLoader<Object, SingletonSet<Object>>() {
73                 @Override
74                 public SingletonSet<Object> load(final Object key) {
75                     return SingletonSet.of(key);
76                 }
77             });
78
79     private final @NonNull SingletonSet<K> keySet;
80     private final @NonNull V value;
81     private int hashCode;
82
83     SharedSingletonMap(final SingletonSet<K> keySet, final V value) {
84         this.keySet = requireNonNull(keySet);
85         this.value = requireNonNull(value);
86     }
87
88     SharedSingletonMap(final K key, final V value) {
89         this(cachedSet(key), value);
90     }
91
92     /**
93      * Create a {@link SharedSingletonMap} of specified {@code key} and {@code value}, which retains insertion order
94      * when transformed via {@link #toModifiableMap()}.
95      *
96      * @param key key
97      * @param value value
98      * @return A SharedSingletonMap
99      * @throws NullPointerException if any of the arguments is null
100      */
101     public static <K, V> @NonNull SharedSingletonMap<K, V> orderedOf(final K key, final V value) {
102         return new Ordered<>(key, value);
103     }
104
105     /**
106      * Create a {@link SharedSingletonMap} of specified {@code key} and {@code value}, which does not retain insertion
107      * order when transformed via {@link #toModifiableMap()}.
108      *
109      * @param key key
110      * @param value value
111      * @return A SharedSingletonMap
112      * @throws NullPointerException if any of the arguments is null
113      */
114     public static <K, V> @NonNull SharedSingletonMap<K, V> unorderedOf(final K key, final V value) {
115         return new Unordered<>(key, value);
116     }
117
118     /**
119      * Create a {@link SharedSingletonMap} of specified {@code key} and {@code value}, which retains insertion order
120      * when transformed via {@link #toModifiableMap()}.
121      *
122      * @param map input map
123      * @return A SharedSingletonMap
124      * @throws NullPointerException if {@code map} is null
125      * @throws IllegalArgumentException if {@code map} does not have exactly one entry
126      */
127     public static <K, V> @NonNull SharedSingletonMap<K, V> orderedCopyOf(final Map<K, V> map) {
128         final Entry<K, V> e = singleEntry(map);
129         return new Ordered<>(e.getKey(), e.getValue());
130     }
131
132     /**
133      * Create a {@link SharedSingletonMap} from specified single-element map, which does not retain insertion order when
134      * transformed via {@link #toModifiableMap()}.
135      *
136      * @param map input map
137      * @return A SharedSingletonMap
138      * @throws NullPointerException if {@code map} is null
139      * @throws IllegalArgumentException if {@code map} does not have exactly one entry
140      */
141     public static <K, V> @NonNull SharedSingletonMap<K, V> unorderedCopyOf(final Map<K, V> map) {
142         final Entry<K, V> e = singleEntry(map);
143         return new Unordered<>(e.getKey(), e.getValue());
144     }
145
146     public final Entry<K, V> getEntry() {
147         return new SimpleImmutableEntry<>(keySet.getElement(), value);
148     }
149
150     @Override
151     public final @NonNull SingletonSet<Entry<K, V>> entrySet() {
152         return SingletonSet.of(getEntry());
153     }
154
155     @Override
156     public final @NonNull SingletonSet<K> keySet() {
157         return keySet;
158     }
159
160     @Override
161     public final @NonNull SingletonSet<V> values() {
162         return SingletonSet.of(value);
163     }
164
165     @Override
166     public final boolean containsKey(final Object key) {
167         return keySet.contains(key);
168     }
169
170     @Override
171     @SuppressWarnings("checkstyle:hiddenField")
172     public final boolean containsValue(final Object value) {
173         return this.value.equals(value);
174     }
175
176     @Override
177     public final V get(final Object key) {
178         return keySet.contains(key) ? value : null;
179     }
180
181     @Override
182     public final int size() {
183         return 1;
184     }
185
186     @Override
187     public final boolean isEmpty() {
188         return false;
189     }
190
191     @Override
192     @SuppressWarnings("checkstyle:hiddenField")
193     public final V put(final K key, final V value) {
194         throw new UnsupportedOperationException();
195     }
196
197     @Override
198     public final V remove(final Object key) {
199         throw new UnsupportedOperationException();
200     }
201
202     @Override
203     @SuppressWarnings("checkstyle:parameterName")
204     public final void putAll(final Map<? extends K, ? extends V> m) {
205         throw new UnsupportedOperationException();
206     }
207
208     @Override
209     public final void clear() {
210         throw new UnsupportedOperationException();
211     }
212
213     @Override
214     public final int hashCode() {
215         if (hashCode == 0) {
216             hashCode = keySet.getElement().hashCode() ^ value.hashCode();
217         }
218         return hashCode;
219     }
220
221     @Override
222     public final boolean equals(final Object obj) {
223         if (this == obj) {
224             return true;
225         }
226         if (!(obj instanceof Map)) {
227             return false;
228         }
229
230         final Map<?, ?> m = (Map<?, ?>)obj;
231         return m.size() == 1 && value.equals(m.get(keySet.getElement()));
232     }
233
234     @Override
235     public final String toString() {
236         return "{" + keySet.getElement() + '=' + value + '}';
237     }
238
239     @SuppressWarnings("unchecked")
240     static <K> @NonNull SingletonSet<K> cachedSet(final K key) {
241         return (SingletonSet<K>) CACHE.getUnchecked(key);
242     }
243
244     private static <K, V> Entry<K, V> singleEntry(final Map<K, V> map) {
245         final Iterator<Entry<K, V>> it = map.entrySet().iterator();
246         checkArgument(it.hasNext(), "Input map is empty");
247         final Entry<K, V> ret = it.next();
248         checkArgument(!it.hasNext(), "Input map has more than one entry");
249         return ret;
250     }
251 }