/*******************************************************************************
* Copyright (c) 2007, 2010 Association for Decentralized Information Management
* in Industry THTH ry.
* 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:
* VTT Technical Research Centre of Finland - initial API and implementation
*******************************************************************************/
/*
* 16.8.2006
*/
package org.simantics.utils.datastructures;
import gnu.trove.map.hash.THashMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* MapList is a data structure with map on left side and arraylist on right side.
*
*
* @author Toni Kalajainen
*/
public class MapList {
@SuppressWarnings("rawtypes")
public static final MapList EMPTY_MAPLIST = new MapList() {
private static final String IMMUTABLE_MSG = "Cannot modify immutable empty MapList";
@Override
public void add(Object key) {
throw new UnsupportedOperationException(IMMUTABLE_MSG);
}
@Override
public void add(Object key, int index, Object value) {
throw new UnsupportedOperationException(IMMUTABLE_MSG);
}
@Override
public void add(Object key, Object value) {
throw new UnsupportedOperationException(IMMUTABLE_MSG);
}
@Override
public void addAll(Object key, Collection values) {
throw new UnsupportedOperationException(IMMUTABLE_MSG);
}
@Override
public void clear() {
throw new UnsupportedOperationException(IMMUTABLE_MSG);
}
@Override
public boolean remove(Object key) {
throw new UnsupportedOperationException(IMMUTABLE_MSG);
}
public boolean remove(Object key, Object value) {
throw new UnsupportedOperationException(IMMUTABLE_MSG);
}
};
@SuppressWarnings("unchecked")
public static MapList emptyMapList() {
return EMPTY_MAPLIST;
}
protected Map> lists;
public MapList() {
lists = new THashMap>();
}
@SuppressWarnings("unchecked")
public MapList( Class> mapClass ) {
try {
lists = (Map>) mapClass.newInstance();
} catch (InstantiationException e) {
throw new RuntimeException( e );
} catch (IllegalAccessException e) {
throw new RuntimeException( e );
}
}
public MapList(MapList copyFrom) {
for (Entry> e : copyFrom.lists.entrySet())
lists.put( e.getKey(), new ArrayList(e.getValue()) );
}
public static MapList use( Map> map ) {
MapList result = new MapList();
result.lists = map;
return result;
}
public void add(L key) {
getOrCreateList(key);
}
public void add(L key, R value)
{
List list = getOrCreateList(key);
list.add(value);
}
public void add(L key, int index, R value)
{
ArrayList list = getOrCreateList(key);
list.add(index, value);
}
public void addAll(L key, Collection values) {
ArrayList list = getOrCreateList(key);
list.addAll(values);
}
private ArrayList getOrCreateList(L key)
{
ArrayList list = (ArrayList) lists.get(key);
if (list==null) {
list = new ArrayList(1);
lists.put(key, list);
}
return list;
}
private List getList(L key)
{
return lists.get(key);
}
public boolean remove(L key, R value)
{
List list = getList(key);
if (list==null) return false;
boolean result = list.remove(value);
if (list.size()==0)
lists.remove(key);
return result;
}
public boolean remove(L key)
{
List list = getList(key);
if (list==null) return false;
lists.remove(key);
return true;
}
public void clear()
{
lists.clear();
}
public L[] getKeys(L[] list)
{
return lists.keySet().toArray(list);
}
public Set getKeys()
{
return lists.keySet();
}
public int getKeySize() {
return lists.size();
}
public boolean containsKey(L key)
{
return lists.containsKey(key);
}
public boolean contains(L key, R obj)
{
List l = lists.get(key);
if (l==null) return false;
return l.contains(obj);
}
public R[] getValues(L key, R[] list)
{
List l = lists.get(key);
if (l==null) return null;
return l.toArray(list);
}
/**
* @param key
* the key to get values for
* @param list
* the list to fill with existing values for specified key. Fills
* this array with at maximum as many values as there is room for
* in the array even if there are more values available in the
* maplist for the specified key.
* @return the amount of values existing for the key. May be smaller or
* larger than the size of the provided list. If smaller, only the
* first array indexes will be filled with data and if larger, all
* array indexes will be filled with data.
*/
public int getAtMostValues(L key, R[] list)
{
List l = lists.get(key);
if (l==null) return 0;
int valueCount = l.size();
int size = Math.min(valueCount, list.length);
for (int i = 0; i < size; ++i)
list[i] = l.get(i);
return valueCount;
}
/**
* Returns a the internal list values for the specified key. The list is
* valid as long as it contains elements. The list should not be modified
* but the return value of this method does not enforce it like
* {@link #getValues(Object)} does. Use this method when you know you will
* not risk a 3rd party modifying the returned list and you want to avoid
* the cost of extra memory allocation through
* {@link Collections#unmodifiableList(List)}.
*
* @param key
* the key to look values for
* @return empty unmodifiable list if there is no list with the specified
* key, otherwise an unmodifiable version of the stored list
*/
public List getValuesUnsafe(L key)
{
List l = lists.get(key);
return l != null ? l : Collections.emptyList();
}
/**
* Returns a read-only reference to the values. The list is valid as long as
* it contains elements.
*
* @param key
* @return empty unmodifiable list if there is no list with the specified key,
* otherwise an unmodifiable version of the stored list
*/
public List getValues(L key)
{
List l = lists.get(key);
if (l==null) return Collections.emptyList();
return Collections.unmodifiableList(l);
}
/**
* Returns a copy of the values
*
* @param key
* @return empty unmodifiable list if there is no list with the specified key,
* otherwise a copy of the stored list
*/
public List getValuesSnapshot(L key)
{
List l = lists.get(key);
if (l==null) return Collections.emptyList();
return new ArrayList(l);
}
public List getAllValuesSnapshot()
{
return getAllValuesSnapshot(null);
}
public List getAllValuesSnapshot(List result)
{
if (result == null)
result = new ArrayList();
for (List right : lists.values()) {
result.addAll(right);
}
return result;
}
public boolean isEmpty() {
return lists.isEmpty();
}
/**
* Makes _this_ maplist immutable.
*/
public void makeImmutable() {
for (Entry> e : lists.entrySet())
lists.put(e.getKey(), Collections.unmodifiableList(e.getValue()));
lists = Collections.unmodifiableMap(lists);
}
}