/* * Copyright (c) 2015 Eike Stepper (Berlin, 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<Object, Object>()); } public final Class<K> getKeyClass() { return keyClass; } public final Class<V> getValueClass() { return valueClass; } public final Map<V, K> invert() { return new BidiMap<V, K>(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; } }; } }