All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ibm.icu.impl.Relation Maven / Gradle / Ivy

The newest version!
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
 **********************************************************************
 * Copyright (c) 2002-2015, International Business Machines
 * Corporation and others.  All Rights Reserved.
 **********************************************************************
 * Author: Mark Davis
 **********************************************************************
 */
package com.ibm.icu.impl;

import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.ibm.icu.util.Freezable;

/**
 * A Relation is a set of mappings from keys to values.
 * Unlike Map, there is not guaranteed to be a single value per key.
 * The Map-like APIs return collections for values.
 * @author medavis

 */
public class Relation implements Freezable> { // TODO: add , Map>, but requires API changes
    private Map> data;

    Constructor> setCreator;
    Object[] setComparatorParam;

    public static  Relation of(Map> map, Class setCreator) {
        return new Relation<>(map, setCreator);
    }

    public static  Relation of(Map> map, Class setCreator, Comparator setComparator) {
        return new Relation<>(map, setCreator, setComparator);
    }

    public Relation(Map> map, Class setCreator) {
        this(map, setCreator, null);
    }

    @SuppressWarnings("unchecked")
    public Relation(Map> map, Class setCreator, Comparator setComparator) {
        try {
            setComparatorParam = setComparator == null ? null : new Object[]{setComparator};
            if (setComparator == null) {
                this.setCreator = ((Class>)setCreator).getConstructor();
                this.setCreator.newInstance(setComparatorParam); // check to make sure compiles
            } else {
                this.setCreator = ((Class>)setCreator).getConstructor(Comparator.class);
                this.setCreator.newInstance(setComparatorParam); // check to make sure compiles
            }
            data = map == null ? new HashMap>() : map;
        } catch (Exception e) {
            throw (RuntimeException) new IllegalArgumentException("Can't create new set").initCause(e);
        }
    }

    public void clear() {
        data.clear();
    }

    public boolean containsKey(Object key) {
        return data.containsKey(key);
    }

    public boolean containsValue(Object value) {
        for (Set values : data.values()) {
            if (values.contains(value)) {
                return true;
            }
        }
        return false;
    }

    public final Set> entrySet() {
        return keyValueSet();
    }

    public Set>> keyValuesSet() {
        return data.entrySet();
    }

    public Set> keyValueSet() {
        Set> result = new LinkedHashSet<>();
        for (K key : data.keySet()) {
            for (V value : data.get(key)) {
                result.add(new SimpleEntry<>(key, value));
            }
        }
        return result;
    }

    @Override
    public boolean equals(Object o) {
        if (o == null)
            return false;
        if (o.getClass() != this.getClass())
            return false;
        return data.equals(((Relation) o).data);
    }

    //  public V get(Object key) {
    //      Set set = data.get(key);
    //      if (set == null || set.size() == 0)
    //        return null;
    //      return set.iterator().next();
    //  }

    public Set getAll(Object key) {
        return data.get(key);
    }

    public Set get(Object key) {
        return data.get(key);
    }

    @Override
    public int hashCode() {
        return data.hashCode();
    }

    public boolean isEmpty() {
        return data.isEmpty();
    }

    public Set keySet() {
        return data.keySet();
    }

    public V put(K key, V value) {
        Set set = data.get(key);
        if (set == null) {
            data.put(key, set = newSet());
        }
        set.add(value);
        return value;
    }

    public V putAll(K key, Collection values) {
        Set set = data.get(key);
        if (set == null) {
            data.put(key, set = newSet());
        }
        set.addAll(values);
        return values.size() == 0 ? null : values.iterator().next();
    }

    public V putAll(Collection keys, V value) {
        V result = null;
        for (K key : keys) {
            result = put(key, value);
        }
        return result;
    }

    private Set newSet() {
        try {
            return setCreator.newInstance(setComparatorParam);
        } catch (Exception e) {
            throw (RuntimeException) new IllegalArgumentException("Can't create new set").initCause(e);
        }
    }

    public void putAll(Map t) {
        for (Map.Entry entry : t.entrySet()) {
            put(entry.getKey(), entry.getValue());
        }
    }

    public void putAll(Relation t) {
        for (K key : t.keySet()) {
            for (V value : t.getAll(key)) {
                put(key, value);
            }
        }
    }

    public Set removeAll(K key) {
        try {
            return data.remove(key);
        } catch (NullPointerException e) {
            return null; // data doesn't allow null, eg ConcurrentHashMap
        }
    }

    public boolean remove(K key, V value) {
        try {
            Set set = data.get(key);
            if (set == null) {
                return false;
            }
            boolean result = set.remove(value);
            if (set.size() == 0) {
                data.remove(key);
            }
            return result;
        } catch (NullPointerException e) {
            return false; // data doesn't allow null, eg ConcurrentHashMap
        }
    }

    public int size() {
        return data.size();
    }

    public Set values() {
        return values(new LinkedHashSet());
    }

    public > C values(C result) {
        for (Entry> keyValue : data.entrySet()) {
            result.addAll(keyValue.getValue());
        }
        return result;
    }

    @Override
    public String toString() {
        return data.toString();
    }

    static class SimpleEntry implements Entry {
        K key;

        V value;

        public SimpleEntry(K key, V value) {
            this.key = key;
            this.value = value;
        }

        public SimpleEntry(Entry e) {
            this.key = e.getKey();
            this.value = e.getValue();
        }

        @Override
        public K getKey() {
            return key;
        }

        @Override
        public V getValue() {
            return value;
        }

        @Override
        public V setValue(V value) {
            V oldValue = this.value;
            this.value = value;
            return oldValue;
        }
    }

    public Relation addAllInverted(Relation source) {
        for (V value : source.data.keySet()) {
            for (K key : source.data.get(value)) {
                put(key, value);
            }
        }
        return this;
    }

    public Relation addAllInverted(Map source) {
        for (Map.Entry entry : source.entrySet()) {
            put(entry.getValue(), entry.getKey());
        }
        return this;
    }

    volatile boolean frozen = false;

    @Override
    public boolean isFrozen() {
        return frozen;
    }

    @Override
    public Relation freeze() {
        if (!frozen) {
            // does not handle one level down, so we do that on a case-by-case basis
            for (K key : data.keySet()) {
                data.put(key, Collections.unmodifiableSet(data.get(key)));
            }
            // now do top level
            data = Collections.unmodifiableMap(data);
            frozen = true;
        }
        return this;
    }

    @Override
    public Relation cloneAsThawed() {
        // TODO do later
        throw new UnsupportedOperationException();
    }

    public boolean removeAll(Relation toBeRemoved) {
        boolean result = false;
        for (K key : toBeRemoved.keySet()) {
            try {
                Set values = toBeRemoved.getAll(key);
                if (values != null) {
                    result |= removeAll(key, values);
                }
            } catch (NullPointerException e) {
                // data doesn't allow null, eg ConcurrentHashMap
            }
        }
        return result;
    }

    @SafeVarargs
    @SuppressWarnings("varargs")    // Not supported by Eclipse, but we need this for javac
    public final Set removeAll(K... keys) {
        return removeAll(Arrays.asList(keys));
    }

    public boolean removeAll(K key, Iterable toBeRemoved) {
        boolean result = false;
        for (V value : toBeRemoved) {
            result |= remove(key, value);
        }
        return result;
    }

    public Set removeAll(Collection toBeRemoved) {
        Set result = new LinkedHashSet<>();
        for (K key : toBeRemoved) {
            try {
                final Set removals = data.remove(key);
                if (removals != null) {
                    result.addAll(removals);
                }
            } catch (NullPointerException e) {
                // data doesn't allow null, eg ConcurrentHashMap
            }
        }
        return result;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy