/*******************************************************************************
* 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;
}
}