blob: fb3ce2708b7813adfe1c16f5711bbe601124bd90 [file] [log] [blame]
/*
* Copyright (c) 2015, 2019 Eike Stepper (Loehne, Germany) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Eike Stepper - initial API and implementation
*/
package org.eclipse.net4j.util.collection;
import org.eclipse.net4j.util.CheckUtil;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* @author Eike Stepper
* @since 3.6
*/
public class BidiMap<K, V> extends AbstractMap<K, V>
{
private final Map<Object, Object> map;
private final Class<K> keyClass;
private final Class<V> valueClass;
private BidiMap(Class<K> keyClass, Class<V> valueClass, Map<Object, Object> map)
{
CheckUtil.checkArg(keyClass != valueClass, "Key and value class are the same");
CheckUtil.checkArg(!keyClass.isAssignableFrom(valueClass), "Key class is assignable from value class");
CheckUtil.checkArg(!valueClass.isAssignableFrom(keyClass), "Value class is assignable from key class");
this.keyClass = keyClass;
this.valueClass = valueClass;
this.map = map;
}
public BidiMap(Class<K> keyClass, Class<V> valueClass)
{
this(keyClass, valueClass, new HashMap<>());
}
public final Class<K> getKeyClass()
{
return keyClass;
}
public final Class<V> getValueClass()
{
return valueClass;
}
public final Map<V, K> invert()
{
return new BidiMap<>(valueClass, keyClass, map);
}
@Override
public V put(K key, V value)
{
CheckUtil.checkArg(key, "Key is null");
CheckUtil.checkArg(value, "Value is null");
@SuppressWarnings("unchecked")
V oldValue = (V)map.put(key, value);
map.put(value, key);
return oldValue;
}
@Override
public V remove(Object key)
{
if (keyClass.isInstance(key))
{
@SuppressWarnings("unchecked")
V oldValue = (V)map.remove(key);
if (oldValue != null)
{
map.remove(oldValue);
}
return oldValue;
}
return null;
}
@Override
public void clear()
{
map.clear();
}
@Override
public int size()
{
return map.size() / 2;
}
@Override
public boolean isEmpty()
{
return map.isEmpty();
}
@Override
public boolean containsKey(Object key)
{
return keyClass.isInstance(key) && map.containsKey(key);
}
@Override
public boolean containsValue(Object value)
{
return invert().containsKey(value);
}
@Override
public V get(Object key)
{
if (keyClass.isInstance(key))
{
@SuppressWarnings("unchecked")
V value = (V)map.get(key);
return value;
}
return null;
}
@Override
public Set<Map.Entry<K, V>> entrySet()
{
return new AbstractSet<Map.Entry<K, V>>()
{
@Override
public Iterator<Map.Entry<K, V>> iterator()
{
final Iterator<Map.Entry<Object, Object>> delegate = map.entrySet().iterator();
return new AbstractIterator<Map.Entry<K, V>>()
{
@Override
protected Object computeNextElement()
{
while (delegate.hasNext())
{
Map.Entry<Object, Object> element = delegate.next();
if (keyClass.isInstance(element.getKey()))
{
return element;
}
}
return END_OF_DATA;
}
};
}
@Override
public int size()
{
return BidiMap.this.size() / 2;
}
};
}
}