1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html#License
3 /*
4  **********************************************************************
5  * Copyright (c) 2002-2015, International Business Machines
6  * Corporation and others.  All Rights Reserved.
7  **********************************************************************
8  * Author: Mark Davis
9  **********************************************************************
10  */
11 package com.ibm.icu.impl;
12 
13 import java.lang.reflect.Constructor;
14 import java.util.Arrays;
15 import java.util.Collection;
16 import java.util.Collections;
17 import java.util.Comparator;
18 import java.util.HashMap;
19 import java.util.LinkedHashSet;
20 import java.util.Map;
21 import java.util.Map.Entry;
22 import java.util.Set;
23 
24 import com.ibm.icu.util.Freezable;
25 
26 /**
27  * A Relation is a set of mappings from keys to values.
28  * Unlike Map, there is not guaranteed to be a single value per key.
29  * The Map-like APIs return collections for values.
30  * @author medavis
31 
32  */
33 public class Relation<K, V> implements Freezable<Relation<K,V>> { // TODO: add , Map<K, Collection<V>>, but requires API changes
34     private Map<K, Set<V>> data;
35 
36     Constructor<? extends Set<V>> setCreator;
37     Object[] setComparatorParam;
38 
of(Map<K, Set<V>> map, Class<?> setCreator)39     public static <K, V> Relation<K, V> of(Map<K, Set<V>> map, Class<?> setCreator) {
40         return new Relation<>(map, setCreator);
41     }
42 
of(Map<K, Set<V>> map, Class<?> setCreator, Comparator<V> setComparator)43     public static <K,V> Relation<K, V> of(Map<K, Set<V>> map, Class<?> setCreator, Comparator<V> setComparator) {
44         return new Relation<>(map, setCreator, setComparator);
45     }
46 
Relation(Map<K, Set<V>> map, Class<?> setCreator)47     public Relation(Map<K, Set<V>> map, Class<?> setCreator) {
48         this(map, setCreator, null);
49     }
50 
51     @SuppressWarnings("unchecked")
Relation(Map<K, Set<V>> map, Class<?> setCreator, Comparator<V> setComparator)52     public Relation(Map<K, Set<V>> map, Class<?> setCreator, Comparator<V> setComparator) {
53         try {
54             setComparatorParam = setComparator == null ? null : new Object[]{setComparator};
55             if (setComparator == null) {
56                 this.setCreator = ((Class<? extends Set<V>>)setCreator).getConstructor();
57                 this.setCreator.newInstance(setComparatorParam); // check to make sure compiles
58             } else {
59                 this.setCreator = ((Class<? extends Set<V>>)setCreator).getConstructor(Comparator.class);
60                 this.setCreator.newInstance(setComparatorParam); // check to make sure compiles
61             }
62             data = map == null ? new HashMap<K, Set<V>>() : map;
63         } catch (Exception e) {
64             throw (RuntimeException) new IllegalArgumentException("Can't create new set").initCause(e);
65         }
66     }
67 
clear()68     public void clear() {
69         data.clear();
70     }
71 
containsKey(Object key)72     public boolean containsKey(Object key) {
73         return data.containsKey(key);
74     }
75 
containsValue(Object value)76     public boolean containsValue(Object value) {
77         for (Set<V> values : data.values()) {
78             if (values.contains(value)) {
79                 return true;
80             }
81         }
82         return false;
83     }
84 
entrySet()85     public final Set<Entry<K, V>> entrySet() {
86         return keyValueSet();
87     }
88 
keyValuesSet()89     public Set<Entry<K, Set<V>>> keyValuesSet() {
90         return data.entrySet();
91     }
92 
keyValueSet()93     public Set<Entry<K, V>> keyValueSet() {
94         Set<Entry<K, V>> result = new LinkedHashSet<>();
95         for (K key : data.keySet()) {
96             for (V value : data.get(key)) {
97                 result.add(new SimpleEntry<>(key, value));
98             }
99         }
100         return result;
101     }
102 
103     @Override
equals(Object o)104     public boolean equals(Object o) {
105         if (o == null)
106             return false;
107         if (o.getClass() != this.getClass())
108             return false;
109         return data.equals(((Relation<?, ?>) o).data);
110     }
111 
112     //  public V get(Object key) {
113     //      Set<V> set = data.get(key);
114     //      if (set == null || set.size() == 0)
115     //        return null;
116     //      return set.iterator().next();
117     //  }
118 
getAll(Object key)119     public Set<V> getAll(Object key) {
120         return data.get(key);
121     }
122 
get(Object key)123     public Set<V> get(Object key) {
124         return data.get(key);
125     }
126 
127     @Override
hashCode()128     public int hashCode() {
129         return data.hashCode();
130     }
131 
isEmpty()132     public boolean isEmpty() {
133         return data.isEmpty();
134     }
135 
keySet()136     public Set<K> keySet() {
137         return data.keySet();
138     }
139 
put(K key, V value)140     public V put(K key, V value) {
141         Set<V> set = data.get(key);
142         if (set == null) {
143             data.put(key, set = newSet());
144         }
145         set.add(value);
146         return value;
147     }
148 
putAll(K key, Collection<? extends V> values)149     public V putAll(K key, Collection<? extends V> values) {
150         Set<V> set = data.get(key);
151         if (set == null) {
152             data.put(key, set = newSet());
153         }
154         set.addAll(values);
155         return values.size() == 0 ? null : values.iterator().next();
156     }
157 
putAll(Collection<K> keys, V value)158     public V putAll(Collection<K> keys, V value) {
159         V result = null;
160         for (K key : keys) {
161             result = put(key, value);
162         }
163         return result;
164     }
165 
newSet()166     private Set<V> newSet() {
167         try {
168             return setCreator.newInstance(setComparatorParam);
169         } catch (Exception e) {
170             throw (RuntimeException) new IllegalArgumentException("Can't create new set").initCause(e);
171         }
172     }
173 
putAll(Map<? extends K, ? extends V> t)174     public void putAll(Map<? extends K, ? extends V> t) {
175         for (Map.Entry<? extends K, ? extends V> entry : t.entrySet()) {
176             put(entry.getKey(), entry.getValue());
177         }
178     }
179 
putAll(Relation<? extends K, ? extends V> t)180     public void putAll(Relation<? extends K, ? extends V> t) {
181         for (K key : t.keySet()) {
182             for (V value : t.getAll(key)) {
183                 put(key, value);
184             }
185         }
186     }
187 
removeAll(K key)188     public Set<V> removeAll(K key) {
189         try {
190             return data.remove(key);
191         } catch (NullPointerException e) {
192             return null; // data doesn't allow null, eg ConcurrentHashMap
193         }
194     }
195 
remove(K key, V value)196     public boolean remove(K key, V value) {
197         try {
198             Set<V> set = data.get(key);
199             if (set == null) {
200                 return false;
201             }
202             boolean result = set.remove(value);
203             if (set.size() == 0) {
204                 data.remove(key);
205             }
206             return result;
207         } catch (NullPointerException e) {
208             return false; // data doesn't allow null, eg ConcurrentHashMap
209         }
210     }
211 
size()212     public int size() {
213         return data.size();
214     }
215 
values()216     public Set<V> values() {
217         return values(new LinkedHashSet<V>());
218     }
219 
values(C result)220     public <C extends Collection<V>> C values(C result) {
221         for (Entry<K, Set<V>> keyValue : data.entrySet()) {
222             result.addAll(keyValue.getValue());
223         }
224         return result;
225     }
226 
227     @Override
toString()228     public String toString() {
229         return data.toString();
230     }
231 
232     static class SimpleEntry<K, V> implements Entry<K, V> {
233         K key;
234 
235         V value;
236 
SimpleEntry(K key, V value)237         public SimpleEntry(K key, V value) {
238             this.key = key;
239             this.value = value;
240         }
241 
SimpleEntry(Entry<K, V> e)242         public SimpleEntry(Entry<K, V> e) {
243             this.key = e.getKey();
244             this.value = e.getValue();
245         }
246 
247         @Override
getKey()248         public K getKey() {
249             return key;
250         }
251 
252         @Override
getValue()253         public V getValue() {
254             return value;
255         }
256 
257         @Override
setValue(V value)258         public V setValue(V value) {
259             V oldValue = this.value;
260             this.value = value;
261             return oldValue;
262         }
263     }
264 
addAllInverted(Relation<V,K> source)265     public Relation<K,V> addAllInverted(Relation<V,K> source) {
266         for (V value : source.data.keySet()) {
267             for (K key : source.data.get(value)) {
268                 put(key, value);
269             }
270         }
271         return this;
272     }
273 
addAllInverted(Map<V,K> source)274     public Relation<K,V> addAllInverted(Map<V,K> source) {
275         for (Map.Entry<V,K> entry : source.entrySet()) {
276             put(entry.getValue(), entry.getKey());
277         }
278         return this;
279     }
280 
281     volatile boolean frozen = false;
282 
283     @Override
isFrozen()284     public boolean isFrozen() {
285         return frozen;
286     }
287 
288     @Override
freeze()289     public Relation<K, V> freeze() {
290         if (!frozen) {
291             // does not handle one level down, so we do that on a case-by-case basis
292             for (K key : data.keySet()) {
293                 data.put(key, Collections.unmodifiableSet(data.get(key)));
294             }
295             // now do top level
296             data = Collections.unmodifiableMap(data);
297             frozen = true;
298         }
299         return this;
300     }
301 
302     @Override
cloneAsThawed()303     public Relation<K, V> cloneAsThawed() {
304         // TODO do later
305         throw new UnsupportedOperationException();
306     }
307 
removeAll(Relation<K, V> toBeRemoved)308     public boolean removeAll(Relation<K, V> toBeRemoved) {
309         boolean result = false;
310         for (K key : toBeRemoved.keySet()) {
311             try {
312                 Set<V> values = toBeRemoved.getAll(key);
313                 if (values != null) {
314                     result |= removeAll(key, values);
315                 }
316             } catch (NullPointerException e) {
317                 // data doesn't allow null, eg ConcurrentHashMap
318             }
319         }
320         return result;
321     }
322 
323     @SafeVarargs
324     @SuppressWarnings("varargs")    // Not supported by Eclipse, but we need this for javac
removeAll(K... keys)325     public final Set<V> removeAll(K... keys) {
326         return removeAll(Arrays.asList(keys));
327     }
328 
removeAll(K key, Iterable<V> toBeRemoved)329     public boolean removeAll(K key, Iterable<V> toBeRemoved) {
330         boolean result = false;
331         for (V value : toBeRemoved) {
332             result |= remove(key, value);
333         }
334         return result;
335     }
336 
removeAll(Collection<K> toBeRemoved)337     public Set<V> removeAll(Collection<K> toBeRemoved) {
338         Set<V> result = new LinkedHashSet<>();
339         for (K key : toBeRemoved) {
340             try {
341                 final Set<V> removals = data.remove(key);
342                 if (removals != null) {
343                     result.addAll(removals);
344                 }
345             } catch (NullPointerException e) {
346                 // data doesn't allow null, eg ConcurrentHashMap
347             }
348         }
349         return result;
350     }
351 }
352