com.ibm.icu.impl.Relation Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of icu4j Show documentation
Show all versions of icu4j Show documentation
International Component for Unicode for Java (ICU4J) is a mature, widely used Java library
providing Unicode and Globalization support
// © 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 extends Set> 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 extends Set>)setCreator).getConstructor();
this.setCreator.newInstance(setComparatorParam); // check to make sure compiles
} else {
this.setCreator = ((Class extends Set>)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 extends V> 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 extends K, ? extends V> t) {
for (Map.Entry extends K, ? extends V> entry : t.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
public void putAll(Relation extends K, ? extends V> 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;
}
}