/* * JBoss, Home of Professional Open Source. * * See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing. * * See the AUTHORS.txt file distributed with this work for a full listing of individual contributors. */ package org.teiid.designer.core.util; import java.util.AbstractMap; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * A {@link HashMap} which stores values that also reference their * own keys. More memory efficient than storing separate references * to the keys and values. * * @param <K> * @param <V> * @since 8.0 */ public class KeyInValueHashMap<K, V> extends AbstractMap<K, V> { /** * Adapter interface that clients of the class should implement * and pass to its constructor so that the key (K) can be derived * from the value (V). * * @param <K> * @param <V> */ public interface KeyFromValueAdapter<K, V> { /** * Get the key from the value * * @param value * * @return key (K) from the value (V) */ K getKey(V value); } private class EntryWrapper implements Map.Entry<K, V> { V value; /** * @param value */ public EntryWrapper(V value) { this.value = value; } @Override public K getKey() { return (K) adapter.getKey(value); } @Override public V getValue() { return value; } @Override public V setValue(V value) { throw new UnsupportedOperationException(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((this.value == null) ? 0 : this.value.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; EntryWrapper other = (EntryWrapper)obj; if (!getOuterType().equals(other.getOuterType())) return false; if (this.value == null) { if (other.value != null) return false; } else if (!this.value.equals(other.value)) return false; return true; } private KeyInValueHashMap getOuterType() { return KeyInValueHashMap.this; } } private Set<Map.Entry<K, V>> entrySet = new HashSet<Map.Entry<K, V>>(); private KeyFromValueAdapter adapter; /** * Create a new instance * * @param adapter that can convert from the value into the key */ public KeyInValueHashMap(KeyFromValueAdapter adapter) { this.adapter = adapter; } @Override public V put(K key, V value) { throw new UnsupportedOperationException("Use add rather than put since the key is part of the value"); //$NON-NLS-1$ } /** * Add a value to this map where its key will be derived * by the {@link KeyFromValueAdapter} * * @param value * * @return true if the value was added. */ public boolean add(V value) { EntryWrapper entry = new EntryWrapper(value); return entrySet.add(entry); } /** * Remove a value from this map where the key will * be determined by the {@link KeyFromValueAdapter} * * @param value * * @return removed value or null. */ @Override public V remove(Object value) { EntryWrapper entry = new EntryWrapper((V) value); if (entrySet.remove(entry)) return (V) value; return null; } @Override public Set<Map.Entry<K, V>> entrySet() { return entrySet; } }