Home | History | Annotate | Download | only in impl
      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 
     39     public static <K, V> Relation<K, V> of(Map<K, Set<V>> map, Class<?> setCreator) {
     40         return new Relation<K, V>(map, setCreator);
     41     }
     42 
     43     public static <K,V> Relation<K, V> of(Map<K, Set<V>> map, Class<?> setCreator, Comparator<V> setComparator) {
     44         return new Relation<K, V>(map, setCreator, setComparator);
     45     }
     46 
     47     public Relation(Map<K, Set<V>> map, Class<?> setCreator) {
     48         this(map, setCreator, null);
     49     }
     50 
     51     @SuppressWarnings("unchecked")
     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 
     68     public void clear() {
     69         data.clear();
     70     }
     71 
     72     public boolean containsKey(Object key) {
     73         return data.containsKey(key);
     74     }
     75 
     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 
     85     public final Set<Entry<K, V>> entrySet() {
     86         return keyValueSet();
     87     }
     88 
     89     public Set<Entry<K, Set<V>>> keyValuesSet() {
     90         return data.entrySet();
     91     }
     92 
     93     public Set<Entry<K, V>> keyValueSet() {
     94         Set<Entry<K, V>> result = new LinkedHashSet<Entry<K, V>>();
     95         for (K key : data.keySet()) {
     96             for (V value : data.get(key)) {
     97                 result.add(new SimpleEntry<K, V>(key, value));
     98             }
     99         }
    100         return result;
    101     }
    102 
    103     @Override
    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 
    119     public Set<V> getAll(Object key) {
    120         return data.get(key);
    121     }
    122 
    123     public Set<V> get(Object key) {
    124         return data.get(key);
    125     }
    126 
    127     @Override
    128     public int hashCode() {
    129         return data.hashCode();
    130     }
    131 
    132     public boolean isEmpty() {
    133         return data.isEmpty();
    134     }
    135 
    136     public Set<K> keySet() {
    137         return data.keySet();
    138     }
    139 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    212     public int size() {
    213         return data.size();
    214     }
    215 
    216     public Set<V> values() {
    217         return values(new LinkedHashSet<V>());
    218     }
    219 
    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
    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 
    237         public SimpleEntry(K key, V value) {
    238             this.key = key;
    239             this.value = value;
    240         }
    241 
    242         public SimpleEntry(Entry<K, V> e) {
    243             this.key = e.getKey();
    244             this.value = e.getValue();
    245         }
    246 
    247         @Override
    248         public K getKey() {
    249             return key;
    250         }
    251 
    252         @Override
    253         public V getValue() {
    254             return value;
    255         }
    256 
    257         @Override
    258         public V setValue(V value) {
    259             V oldValue = this.value;
    260             this.value = value;
    261             return oldValue;
    262         }
    263     }
    264 
    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 
    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
    284     public boolean isFrozen() {
    285         return frozen;
    286     }
    287 
    288     @Override
    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
    303     public Relation<K, V> cloneAsThawed() {
    304         // TODO do later
    305         throw new UnsupportedOperationException();
    306     }
    307 
    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     public Set<V> removeAll(K... keys) {
    324         return removeAll(Arrays.asList(keys));
    325     }
    326 
    327     public boolean removeAll(K key, Iterable<V> toBeRemoved) {
    328         boolean result = false;
    329         for (V value : toBeRemoved) {
    330             result |= remove(key, value);
    331         }
    332         return result;
    333     }
    334 
    335     public Set<V> removeAll(Collection<K> toBeRemoved) {
    336         Set<V> result = new LinkedHashSet<V>();
    337         for (K key : toBeRemoved) {
    338             try {
    339                 final Set<V> removals = data.remove(key);
    340                 if (removals != null) {
    341                     result.addAll(removals);
    342                 }
    343             } catch (NullPointerException e) {
    344                 // data doesn't allow null, eg ConcurrentHashMap
    345             }
    346         }
    347         return result;
    348     }
    349 }
    350