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