2 **********************************************************************
\r
3 * Copyright (c) 2002-2009, International Business Machines
\r
4 * Corporation and others. All Rights Reserved.
\r
5 **********************************************************************
\r
7 **********************************************************************
\r
9 package com.ibm.icu.dev.test.util;
\r
11 import java.lang.reflect.Constructor;
\r
12 import java.util.Collection;
\r
13 import java.util.Collections;
\r
14 import java.util.Comparator;
\r
15 import java.util.HashMap;
\r
16 import java.util.LinkedHashSet;
\r
17 import java.util.Map;
\r
18 import java.util.Set;
\r
19 import java.util.Map.Entry;
\r
21 import com.ibm.icu.util.Freezable;
\r
24 * A Relation is a set of mappings from keys to values.
\r
25 * Unlike Map, there is not guaranteed to be a single value per key.
\r
26 * The Map-like APIs return collections for values.
\r
30 public class Relation<K, V> implements Freezable {
\r
31 private Map<K, Set<V>> data;
\r
33 Constructor<Set<V>> setCreator;
\r
34 Object[] setComparatorParam;
\r
36 public Relation(Map<K, Set<V>> map, Class<Set<V>> setCreator) {
\r
37 this(map, setCreator, null);
\r
40 public Relation(Map<K, Set<V>> map, Class<Set<V>> setCreator, Comparator<V> setComparator) {
\r
42 setComparatorParam = setComparator == null ? null : new Object[]{setComparator};
\r
43 if (setComparator == null) {
\r
44 this.setCreator = setCreator.getConstructor();
\r
45 this.setCreator.newInstance(setComparatorParam); // check to make sure compiles
\r
47 this.setCreator = setCreator.getConstructor(Comparator.class);
\r
48 this.setCreator.newInstance(setComparatorParam); // check to make sure compiles
\r
50 data = map == null ? new HashMap() : map;
\r
51 } catch (Exception e) {
\r
52 throw (RuntimeException) new IllegalArgumentException("Can't create new set").initCause(e);
\r
57 public void clear() {
\r
61 public boolean containsKey(Object key) {
\r
62 return data.containsKey(key);
\r
65 public boolean containsValue(Object value) {
\r
66 for (Set<V> values : data.values()) {
\r
67 if (values.contains(value))
\r
73 public Set<Entry<K, V>> entrySet() {
\r
74 Set<Entry<K, V>> result = new LinkedHashSet();
\r
75 for (K key : data.keySet()) {
\r
76 for (V value : data.get(key)) {
\r
77 result.add(new SimpleEntry(key, value));
\r
83 public boolean equals(Object o) {
\r
86 if (o.getClass() != this.getClass())
\r
88 return data.equals(((Relation) o).data);
\r
91 // public V get(Object key) {
\r
92 // Set<V> set = data.get(key);
\r
93 // if (set == null || set.size() == 0)
\r
95 // return set.iterator().next();
\r
98 public Set<V> getAll(Object key) {
\r
99 return data.get(key);
\r
102 public int hashCode() {
\r
103 return data.hashCode();
\r
106 public boolean isEmpty() {
\r
107 return data.isEmpty();
\r
110 public Set<K> keySet() {
\r
111 return data.keySet();
\r
114 public V put(K key, V value) {
\r
115 Set<V> set = data.get(key);
\r
117 data.put(key, set = newSet());
\r
123 public V putAll(K key, Collection<V> value) {
\r
124 Set<V> set = data.get(key);
\r
126 data.put(key, set = newSet());
\r
129 return value.size() == 0 ? null : value.iterator().next();
\r
132 public V putAll(Collection<K> keys, V value) {
\r
134 for (K key : keys) {
\r
135 result = put(key, value);
\r
140 private Set<V> newSet() {
\r
142 return (Set<V>) setCreator.newInstance(setComparatorParam);
\r
143 } catch (Exception e) {
\r
144 throw (RuntimeException) new IllegalArgumentException("Can't create new set").initCause(e);
\r
148 public void putAll(Map<? extends K, ? extends V> t) {
\r
149 for (K key : t.keySet()) {
\r
150 put(key, t.get(key));
\r
154 public void putAll(Relation<? extends K, ? extends V> t) {
\r
155 for (K key : t.keySet()) {
\r
156 for (V value : t.getAll(key)) {
\r
162 public Set<V> removeAll(K key) {
\r
163 return data.remove(key);
\r
166 public boolean remove(K key, V value) {
\r
167 Set<V> set = data.get(key);
\r
168 if (set == null) return false;
\r
169 boolean result = set.remove(value);
\r
170 if (set.size() == 0) {
\r
176 public int size() {
\r
177 return data.size();
\r
180 public Collection<V> values() {
\r
181 Set<V> result = newSet();
\r
182 for (K key : data.keySet()) {
\r
183 result.addAll(data.get(key));
\r
188 public String toString() {
\r
189 return data.toString();
\r
192 static class SimpleEntry<K, V> implements Entry<K, V> {
\r
197 public SimpleEntry(K key, V value) {
\r
199 this.value = value;
\r
202 public SimpleEntry(Entry<K, V> e) {
\r
203 this.key = e.getKey();
\r
204 this.value = e.getValue();
\r
207 public K getKey() {
\r
211 public V getValue() {
\r
215 public V setValue(V value) {
\r
216 V oldValue = this.value;
\r
217 this.value = value;
\r
222 public Relation<K,V> addAllInverted(Relation<V,K> source) {
\r
223 for (V value : source.data.keySet()) {
\r
224 for (K key : source.data.get(value)) {
\r
231 public Relation<K,V> addAllInverted(Map<V,K> source) {
\r
232 for (V value : source.keySet()) {
\r
233 put(source.get(value), value);
\r
238 boolean frozen = false;
\r
240 public boolean isFrozen() {
\r
244 public Object freeze() {
\r
247 // does not handle one level down, so we do that on a case-by-case basis
\r
248 for (K key : data.keySet()) {
\r
249 data.put(key, Collections.unmodifiableSet(data.get(key)));
\r
251 // now do top level
\r
252 data = Collections.unmodifiableMap(data);
\r
257 public Object cloneAsThawed() {
\r
259 throw new UnsupportedOperationException();
\r
262 public boolean removeAll(Relation<K, V> toBeRemoved) {
\r
263 boolean result = false;
\r
264 for (K key : toBeRemoved.keySet()) {
\r
265 Set<V> values = toBeRemoved.getAll(key);
\r
266 if (values != null) {
\r
267 result |= removeAll(key, values);
\r
273 public boolean removeAll(K key, Iterable<V> all) {
\r
274 boolean result = false;
\r
275 for (V value : all) {
\r
276 result |= remove(key, value);
\r
281 public Set<V> removeAll(Collection<K> toBeRemoved) {
\r
282 Set<V> result = new LinkedHashSet();
\r
283 for (K key : toBeRemoved) {
\r
284 final Set<V> removals = data.remove(key);
\r
285 if (removals != null) {
\r
286 result.addAll(removals);
\r