/******************************************************************************* * Copyright (c) 2008, 2015 Matthew Hall 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: * Matthew Hall - initial API and implementation (bug 215531) * Matthew Hall - bug 228125 * (through ViewerElementMap.java) * Matthew Hall - bugs 262269, 303847 * Stefan Xenos <sxenos@gmail.com> - Bug 335792 ******************************************************************************/ package org.eclipse.core.internal.databinding.identity; import java.lang.reflect.Array; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.eclipse.core.internal.databinding.observable.Util; import org.eclipse.core.runtime.Assert; /** * A {@link Map} whose keys are added, removed and compared by identity. The * keys in the map are compared using <code>==</code> instead of * {@link #equals(Object)}. * <p> * This class is <i>not</i> a strict implementation the {@link Map} interface. * It intentionally violates the {@link Map} contract, which requires the use of * {@link #equals(Object)} when comparing keys. * * @param <K> * the type of the keys in the map * @param <V> * the type of the values in the map * @since 1.2 */ public class IdentityMap<K, V> implements Map<K, V> { private Map<IdentityWrapper<K>, V> wrappedMap; /** * Constructs an IdentityMap. */ public IdentityMap() { this.wrappedMap = new HashMap<>(); } /** * Constructs an IdentityMap containing all the entries in the specified * map. * * @param map * the map whose entries are to be added to this map. */ public IdentityMap(Map<? extends K, ? extends V> map) { this(); Assert.isNotNull(map); putAll(map); } @Override public void clear() { wrappedMap.clear(); } @Override public boolean containsKey(Object key) { return wrappedMap.containsKey(IdentityWrapper.wrap(key)); } @Override public boolean containsValue(Object value) { return wrappedMap.containsValue(value); } @Override public Set<Map.Entry<K, V>> entrySet() { final Set<Map.Entry<IdentityWrapper<K>, V>> wrappedEntrySet = wrappedMap .entrySet(); return new Set<Map.Entry<K, V>>() { @Override public boolean add(Map.Entry<K, V> o) { throw new UnsupportedOperationException(); } @Override public boolean addAll(Collection<? extends Map.Entry<K, V>> c) { throw new UnsupportedOperationException(); } @Override public void clear() { wrappedEntrySet.clear(); } @Override public boolean contains(Object o) { for (Entry<K, V> entry : this) if (entry.equals(o)) return true; return false; } @Override public boolean containsAll(Collection<?> c) { for (Object element : c) if (!contains(element)) return false; return true; } @Override public boolean isEmpty() { return wrappedEntrySet.isEmpty(); } @Override public Iterator<Map.Entry<K, V>> iterator() { final Iterator<Map.Entry<IdentityWrapper<K>, V>> wrappedIterator = wrappedEntrySet.iterator(); return new Iterator<Map.Entry<K, V>>() { @Override public boolean hasNext() { return wrappedIterator.hasNext(); } @Override public Map.Entry<K, V> next() { final Map.Entry<IdentityWrapper<K>, V> wrappedEntry = wrappedIterator .next(); return new Map.Entry<K, V>() { @Override public K getKey() { return wrappedEntry.getKey().unwrap(); } @Override public V getValue() { return wrappedEntry.getValue(); } @Override public V setValue(V value) { return wrappedEntry.setValue(value); } @Override public boolean equals(Object obj) { if (obj == this) return true; if (obj == null || !(obj instanceof Map.Entry)) return false; Map.Entry<?, ?> that = (Map.Entry<?, ?>) obj; return this.getKey() == that.getKey() && Util.equals(this.getValue(), that.getValue()); } @Override public int hashCode() { return wrappedEntry.hashCode(); } }; } @Override public void remove() { wrappedIterator.remove(); } }; } @Override public boolean remove(Object o) { final Map.Entry<?, ?> unwrappedEntry = (Map.Entry<?, ?>) o; Object key = unwrappedEntry.getKey(); final IdentityWrapper<Object> wrappedKey = IdentityWrapper .wrap(key); Map.Entry<IdentityWrapper<Object>, Object> wrappedEntry = new Map.Entry<IdentityWrapper<Object>, Object>() { @Override public IdentityWrapper<Object> getKey() { return wrappedKey; } @Override public Object getValue() { return unwrappedEntry.getValue(); } @Override public Object setValue(Object value) { throw new UnsupportedOperationException(); } @Override public boolean equals(Object obj) { if (obj == this) return true; if (obj == null || !(obj instanceof Map.Entry)) return false; Map.Entry<?, ?> that = (Map.Entry<?, ?>) obj; return Util.equals(wrappedKey, that.getKey()) && Util.equals(this.getValue(), that.getValue()); } @Override public int hashCode() { return wrappedKey.hashCode() ^ (getValue() == null ? 0 : getValue() .hashCode()); } }; return wrappedEntrySet.remove(wrappedEntry); } @Override public boolean removeAll(Collection<?> c) { boolean changed = false; for (Object element : c) changed |= remove(element); return changed; } @Override public boolean retainAll(Collection<?> c) { boolean changed = false; Object[] toRetain = c.toArray(); outer: for (Iterator<?> iterator = iterator(); iterator .hasNext();) { Object entry = iterator.next(); for (int i = 0; i < toRetain.length; i++) if (entry.equals(toRetain[i])) continue outer; iterator.remove(); changed = true; } return changed; } @Override public int size() { return wrappedEntrySet.size(); } @Override public Object[] toArray() { return toArray(new Object[size()]); } @SuppressWarnings("unchecked") @Override public <T> T[] toArray(T[] a) { int size = size(); if (a.length < size) { a = (T[]) Array.newInstance(a.getClass().getComponentType(), size); } int i = 0; for (Entry<K, V> entry : this) { a[i++] = (T) entry; } return a; } @Override public boolean equals(Object obj) { if (obj == this) return true; if (obj == null || !(obj instanceof Set)) return false; Set<?> that = (Set<?>) obj; return this.size() == that.size() && containsAll(that); } @Override public int hashCode() { return wrappedEntrySet.hashCode(); } }; } @Override public V get(Object key) { return wrappedMap.get(IdentityWrapper.wrap(key)); } @Override public boolean isEmpty() { return wrappedMap.isEmpty(); } @Override public Set<K> keySet() { final Set<IdentityWrapper<K>> wrappedKeySet = wrappedMap.keySet(); return new Set<K>() { @Override public boolean add(K o) { throw new UnsupportedOperationException(); } @Override public boolean addAll(Collection<? extends K> c) { throw new UnsupportedOperationException(); } @Override public void clear() { wrappedKeySet.clear(); } @Override public boolean contains(Object o) { return wrappedKeySet.contains(IdentityWrapper.wrap(o)); } @Override public boolean containsAll(Collection<?> c) { for (Object element : c) if (!wrappedKeySet.contains(IdentityWrapper.wrap(element))) return false; return true; } @Override public boolean isEmpty() { return wrappedKeySet.isEmpty(); } @Override public Iterator<K> iterator() { final Iterator<IdentityWrapper<K>> wrappedIterator = wrappedKeySet .iterator(); return new Iterator<K>() { @Override public boolean hasNext() { return wrappedIterator.hasNext(); } @Override public K next() { return wrappedIterator.next().unwrap(); } @Override public void remove() { wrappedIterator.remove(); } }; } @Override public boolean remove(Object o) { return wrappedKeySet.remove(IdentityWrapper.wrap(o)); } @Override public boolean removeAll(Collection<?> c) { boolean changed = false; for (Object element : c) changed |= wrappedKeySet.remove(IdentityWrapper .wrap(element)); return changed; } @Override public boolean retainAll(Collection<?> c) { boolean changed = false; Object[] toRetain = c.toArray(); outer: for (Object element : this) { for (int i = 0; i < toRetain.length; i++) if (element == toRetain[i]) continue outer; // element not contained in collection, remove. remove(element); changed = true; } return changed; } @Override public int size() { return wrappedKeySet.size(); } @Override public Object[] toArray() { return toArray(new Object[wrappedKeySet.size()]); } @SuppressWarnings("unchecked") @Override public <T> T[] toArray(T[] a) { int size = wrappedKeySet.size(); T[] result = a; if (a.length < size) { result = (T[]) Array.newInstance(a.getClass().getComponentType(), size); } int i = 0; for (IdentityWrapper<K> wrapper : wrappedKeySet) { result[i++] = (T) wrapper.unwrap(); } return result; } @Override public boolean equals(Object obj) { if (obj == this) return true; if (obj == null || !(obj instanceof Set)) return false; Set<?> that = (Set<?>) obj; return this.size() == that.size() && containsAll(that); } @Override public int hashCode() { return wrappedKeySet.hashCode(); } }; } @Override public V put(K key, V value) { return wrappedMap.put(IdentityWrapper.wrap(key), value); } @Override public void putAll(Map<? extends K, ? extends V> other) { for (Map.Entry<? extends K, ? extends V> entry : other.entrySet()) { K key = entry.getKey(); V value = entry.getValue(); wrappedMap.put(IdentityWrapper.wrap(key), value); } } @Override public V remove(Object key) { return wrappedMap.remove(IdentityWrapper.wrap(key)); } @Override public int size() { return wrappedMap.size(); } @Override public Collection<V> values() { return wrappedMap.values(); } @Override public boolean equals(Object obj) { if (obj == this) return true; if (obj == null || !(obj instanceof Map)) return false; Map<?, ?> that = (Map<?, ?>) obj; return this.entrySet().equals(that.entrySet()); } @Override public int hashCode() { return wrappedMap.hashCode(); } }