/******************************************************************************* * 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 java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; 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 HashMap>(); } @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); } }