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