| /******************************************************************************* |
| * Copyright (c) 2010, 2016 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.osgi.internal.serviceregistry; |
| |
| import java.util.*; |
| |
| public class ShrinkableValueCollectionMap<K, V> extends AbstractMap<K, Collection<V>> implements Map<K, Collection<V>> { |
| final Map<? extends K, ? extends Set<? extends Map.Entry<?, ? extends V>>> map; |
| Map<Object, Collection<V>> values; |
| |
| public ShrinkableValueCollectionMap(Map<? extends K, ? extends Set<? extends Map.Entry<?, ? extends V>>> m) { |
| if (m == null) { |
| throw new NullPointerException(); |
| } |
| map = m; |
| values = null; |
| } |
| |
| public void clear() { |
| map.clear(); |
| if (values != null) { |
| values.clear(); |
| } |
| } |
| |
| public boolean containsKey(Object key) { |
| return map.containsKey(key); |
| } |
| |
| public boolean containsValue(Object value) { |
| /* Since values are collections and the collection has identity equality, |
| * there is no way any value could be contained in this map unless that |
| * value has already been gotten from this map. |
| */ |
| if (values == null) { |
| return false; |
| } |
| return values.containsValue(value); |
| } |
| |
| public Set<Map.Entry<K, Collection<V>>> entrySet() { |
| return new EntrySet(); |
| } |
| |
| public Collection<V> get(Object key) { |
| Collection<V> value = null; |
| if (values != null) { |
| value = values.get(key); |
| } |
| if (value == null) { |
| Set<? extends Map.Entry<?, ? extends V>> entrySet = map.get(key); |
| if (entrySet == null) { |
| return null; |
| } |
| value = new ShrinkableEntrySetValueCollection<>(entrySet); |
| if (values == null) { |
| values = new HashMap<>(map.size()); |
| } |
| values.put(key, value); |
| } |
| return value; |
| } |
| |
| public boolean isEmpty() { |
| return map.isEmpty(); |
| } |
| |
| public Collection<V> remove(Object key) { |
| Set<? extends Map.Entry<?, ? extends V>> entrySet = map.remove(key); |
| Collection<V> value = null; |
| if (values != null) { |
| value = values.remove(key); |
| } |
| if ((value == null) && (entrySet != null)) { |
| value = new ShrinkableEntrySetValueCollection<>(entrySet); |
| } |
| return value; |
| } |
| |
| public int size() { |
| return map.size(); |
| } |
| |
| /** |
| * Set class used for entry sets. |
| */ |
| private final class EntrySet extends AbstractSet<Map.Entry<K, Collection<V>>> { |
| EntrySet() { |
| super(); |
| } |
| |
| public Iterator<Map.Entry<K, Collection<V>>> iterator() { |
| return new EntryIterator(); |
| } |
| |
| public int size() { |
| return ShrinkableValueCollectionMap.this.size(); |
| } |
| } |
| |
| /** |
| * Iterator class used for entry sets. |
| */ |
| private final class EntryIterator implements Iterator<Map.Entry<K, Collection<V>>> { |
| private final Iterator<? extends K> iter; |
| private K last; |
| |
| EntryIterator() { |
| iter = map.keySet().iterator(); |
| } |
| |
| public boolean hasNext() { |
| return iter.hasNext(); |
| } |
| |
| public Map.Entry<K, Collection<V>> next() { |
| last = iter.next(); |
| return new Entry(last); |
| } |
| |
| public void remove() { |
| iter.remove(); |
| if (values != null) { |
| values.remove(last); |
| } |
| } |
| } |
| |
| /** |
| * This class represents the entry in this map. |
| */ |
| private final class Entry implements Map.Entry<K, Collection<V>> { |
| private final K key; |
| private Collection<V> value; |
| |
| Entry(final K k) { |
| key = k; |
| } |
| |
| public K getKey() { |
| return key; |
| } |
| |
| public Collection<V> getValue() { |
| if (value == null) { |
| value = ShrinkableValueCollectionMap.this.get(key); |
| } |
| return value; |
| } |
| |
| public Collection<V> setValue(Collection<V> value) { |
| throw new UnsupportedOperationException(); // entries cannot be modified. |
| } |
| |
| public String toString() { |
| return getKey() + "=" + getValue(); //$NON-NLS-1$ |
| } |
| |
| public int hashCode() { |
| return hash(getKey()) ^ hash(getValue()); |
| } |
| |
| public boolean equals(Object obj) { |
| if (obj == this) { |
| return true; |
| } |
| if (!(obj instanceof Map.Entry)) { |
| return false; |
| } |
| Map.Entry<?, ?> other = (Map.Entry<?, ?>) obj; |
| return equality(getKey(), other.getKey()) && equality(getValue(), other.getValue()); |
| } |
| } |
| |
| static int hash(Object one) { |
| return (one == null) ? 0 : one.hashCode(); |
| } |
| |
| static boolean equality(Object one, Object two) { |
| return (one == null) ? (two == null) : one.equals(two); |
| } |
| } |