/******************************************************************************* * Copyright (c) 2002 - 2006 IBM Corporation. * 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package com.ibm.wala.util.collections; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; import com.ibm.wala.util.debug.Assertions; import com.ibm.wala.util.debug.UnimplementedError; /** * A simple implementation of Map; intended for Maps with few elements. Optimized for space, not time -- use with care. */ public class SmallMap<K, V> implements Map<K, V> { private static final boolean DEBUG_USAGE = false; private static final int DEBUG_MAX_SIZE = 20; // this Map contains keysAndValues.length / 2 entries. // in the following array, entries 0 ... keysAndValues.length/2 - 1 are keys. // entries keysAndValues.length/2 .. keysAndValues.length are values. private Object[] keysAndValues; /* */ @Override public int size() { if (keysAndValues == null) { return 0; } else { return keysAndValues.length / 2; } } /** * Use with care. * * @return the ith key */ @SuppressWarnings("unchecked") public K getKey(int i) throws IllegalStateException { if (keysAndValues == null) { throw new IllegalStateException("getKey on empty map"); } try { return (K) keysAndValues[i]; } catch (ArrayIndexOutOfBoundsException e) { throw new IllegalArgumentException("invalid i: " + i); } } /** * Use with care. * * @return the ith key */ public Object getValue(int i) throws IllegalStateException { if (keysAndValues == null) { throw new IllegalStateException("getValue on empty map"); } try { return keysAndValues[size() + i]; } catch (ArrayIndexOutOfBoundsException e) { throw new IllegalArgumentException("illegal i: " + i); } } /* * @see java.util.Map#isEmpty() */ @Override public boolean isEmpty() { return (keysAndValues == null); } /* * @see java.util.Map#containsKey(java.lang.Object) */ @Override public boolean containsKey(Object key) { for (int i = 0; i < size(); i++) { if (keysAndValues[i].equals(key)) { return true; } } return false; } /* * @see java.util.Map#containsValue(java.lang.Object) */ @Override public boolean containsValue(Object value) { if (keysAndValues == null) { return false; } for (int i = size(); i < keysAndValues.length; i++) { Object v = keysAndValues[i]; if (v == null) { if (value == null) { return true; } } else { if (v.equals(value)) { return true; } } } return false; } /* * @see java.util.Map#get(java.lang.Object) */ @Override @SuppressWarnings("unchecked") public V get(Object key) { if (key != null) for (int i = 0; i < size(); i++) { if (keysAndValues[i] != null && keysAndValues[i].equals(key)) { return (V) keysAndValues[size() + i]; } } return null; } private void growByOne() { Object[] old = keysAndValues; int length = (old == null) ? 0 : old.length; keysAndValues = new Object[length + 2]; for (int i = 0; i < length / 2; i++) { keysAndValues[i] = old[i]; } for (int i = 0; i < length / 2; i++) { keysAndValues[i + 1 + length / 2] = old[length / 2 + i]; } } /* * @see java.util.Map#put(java.lang.Object, java.lang.Object) */ @Override @SuppressWarnings({ "unchecked", "unused" }) public V put(Object key, Object value) { if (key == null) { throw new IllegalArgumentException("null key"); } for (int i = 0; i < size(); i++) { if (keysAndValues[i] != null && keysAndValues[i].equals(key)) { V result = (V) keysAndValues[size() + i]; keysAndValues[size() + i] = value; return result; } } if (DEBUG_USAGE && size() >= DEBUG_MAX_SIZE) { Assertions.UNREACHABLE("too many elements in a SmallMap"); } growByOne(); keysAndValues[size() - 1] = key; keysAndValues[keysAndValues.length - 1] = value; return null; } /* * @see java.util.Map#remove(java.lang.Object) */ @Override public V remove(Object key) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } /* * @see java.util.Map#putAll(java.util.Map) */ @Override public void putAll(Map<? extends K, ? extends V> t) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } /* * @see java.util.Map#clear() */ @Override public void clear() { keysAndValues = null; } /* * @see java.util.Map#keySet() */ @Override @SuppressWarnings("unchecked") public Set<K> keySet() { // TODO: use a better set implementation, SOON!! HashSet<K> result = HashSetFactory.make(size()); for (int i = 0; i < size(); i++) { result.add((K) keysAndValues[i]); } return result; } /* * @see java.util.Map#values() */ @Override @SuppressWarnings("unchecked") public Collection<V> values() { int s = size(); if (s == 0) { return Collections.emptySet(); } HashSet<V> result = HashSetFactory.make(s); for (int i = s; i < keysAndValues.length; i++) { result.add((V) keysAndValues[i]); } return result; } /** * @throws UnimplementedError */ @Override public Set<Map.Entry<K, V>> entrySet() throws UnimplementedError { Assertions.UNREACHABLE("must implement entrySet"); return null; } }