Switch MapList to use Java HashMap
[simantics/platform.git] / bundles / org.simantics.utils.datastructures / src / org / simantics / utils / datastructures / MapList.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 /*
13  * 16.8.2006
14  */
15 package org.simantics.utils.datastructures;
16
17 import java.util.ArrayList;
18 import java.util.Collection;
19 import java.util.Collections;
20 import java.util.HashMap;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Map.Entry;
24 import java.util.Set;
25
26 /**
27  * MapList is a data structure with map on left side and arraylist on right side.
28  * <p>
29  * 
30  * @author Toni Kalajainen
31  */
32 public class MapList<L, R> {
33
34     @SuppressWarnings("rawtypes")
35     public static final MapList EMPTY_MAPLIST = new MapList() {
36         private static final String IMMUTABLE_MSG = "Cannot modify immutable empty MapList";
37
38         @Override
39         public void add(Object key) {
40             throw new UnsupportedOperationException(IMMUTABLE_MSG);
41         }
42         @Override
43         public void add(Object key, int index, Object value) {
44             throw new UnsupportedOperationException(IMMUTABLE_MSG);
45         }
46         @Override
47         public void add(Object key, Object value) {
48             throw new UnsupportedOperationException(IMMUTABLE_MSG);
49         }
50         @Override
51         public void addAll(Object key, Collection values) {
52             throw new UnsupportedOperationException(IMMUTABLE_MSG);
53         }
54         @Override
55         public void clear() {
56             throw new UnsupportedOperationException(IMMUTABLE_MSG);
57         }
58         @Override
59         public boolean remove(Object key) {
60             throw new UnsupportedOperationException(IMMUTABLE_MSG);
61         }
62         public boolean remove(Object key, Object value) {
63             throw new UnsupportedOperationException(IMMUTABLE_MSG);
64         }
65     };
66
67     @SuppressWarnings("unchecked")
68     public static <L, R> MapList<L, R> emptyMapList() {
69         return EMPTY_MAPLIST;
70     }
71
72     protected Map<L, List<R>> lists;
73
74     public MapList() {
75         lists = new HashMap<L, List<R>>();
76     }
77     
78     @SuppressWarnings("unchecked")
79         public MapList( Class<?> mapClass ) {
80         try {
81                         lists = (Map<L, List<R>>) mapClass.newInstance();
82                 } catch (InstantiationException e) {
83                         throw new RuntimeException( e );
84                 } catch (IllegalAccessException e) {
85                         throw new RuntimeException( e );
86                 }
87     }
88
89     public MapList(MapList<L, R> copyFrom) {
90         for (Entry<L, List<R>> e : copyFrom.lists.entrySet())
91             lists.put( e.getKey(), new ArrayList<R>(e.getValue()) );
92     }
93
94         public static <L, R> MapList<L, R> use( Map<L, List<R>> map ) {
95                 MapList<L, R> result = new MapList<L, R>();
96                 result.lists = map;
97                 return result;
98         }
99     
100     public void add(L key) {
101         getOrCreateList(key);
102     }
103
104     public void add(L key, R value)
105     {
106         List<R> list = getOrCreateList(key);
107         list.add(value);
108     }
109
110     public void add(L key, int index, R value)
111     {
112         ArrayList<R> list = getOrCreateList(key);
113         list.add(index, value);
114     }
115
116         public void addAll(L key, Collection<R> values) {
117                 ArrayList<R> list = getOrCreateList(key);
118                 list.addAll(values);
119         }
120     
121     private ArrayList<R> getOrCreateList(L key)
122     {
123         ArrayList<R> list = (ArrayList<R>) lists.get(key);
124         if (list==null) {
125             list = new ArrayList<R>(1);
126             lists.put(key, list);
127         }
128         return list;
129     }
130
131     private List<R> getList(L key)
132     {
133         return lists.get(key);
134     }
135
136     public boolean remove(L key, R value)
137     {
138         List<R> list = getList(key);
139         if (list==null) return false;
140         boolean result = list.remove(value);
141         if (list.size()==0)
142             lists.remove(key);
143         return result;
144     }
145
146     public boolean remove(L key)
147     {
148         List<R> list = getList(key);
149         if (list==null) return false;
150         lists.remove(key);
151         return true;
152     }
153
154     public void clear()
155     {
156         lists.clear();
157     }
158
159     public L[] getKeys(L[] list)
160     {
161         return lists.keySet().toArray(list);
162     }
163
164     public Set<L> getKeys()
165     {
166         return lists.keySet();
167     }
168
169     public int getKeySize() {
170         return lists.size();
171     }
172
173     public boolean containsKey(L key)
174     {
175         return lists.containsKey(key);
176     }
177
178     public boolean contains(L key, R obj)
179     {
180         List<R> l = lists.get(key);
181         if (l==null) return false;
182         return l.contains(obj);
183
184     }
185
186     public R[] getValues(L key, R[] list)
187     {
188         List<R> l = lists.get(key);
189         if (l==null) return null;
190         return l.toArray(list);
191     }
192
193     /**
194      * @param key
195      *            the key to get values for
196      * @param list
197      *            the list to fill with existing values for specified key. Fills
198      *            this array with at maximum as many values as there is room for
199      *            in the array even if there are more values available in the
200      *            maplist for the specified key.
201      * @return the amount of values existing for the key. May be smaller or
202      *         larger than the size of the provided list. If smaller, only the
203      *         first array indexes will be filled with data and if larger, all
204      *         array indexes will be filled with data.
205      */
206     public int getAtMostValues(L key, R[] list)
207     {
208         List<R> l = lists.get(key);
209         if (l==null) return 0;
210         int valueCount = l.size();
211         int size = Math.min(valueCount, list.length);
212         for (int i = 0; i < size; ++i)
213             list[i] = l.get(i);
214         return valueCount;
215     }
216
217     /**
218      * Returns a the internal list values for the specified key. The list is
219      * valid as long as it contains elements. The list should not be modified
220      * but the return value of this method does not enforce it like
221      * {@link #getValues(Object)} does. Use this method when you know you will
222      * not risk a 3rd party modifying the returned list and you want to avoid
223      * the cost of extra memory allocation through
224      * {@link Collections#unmodifiableList(List)}.
225      * 
226      * @param key
227      *            the key to look values for
228      * @return empty unmodifiable list if there is no list with the specified
229      *         key, otherwise an unmodifiable version of the stored list
230      */
231     public List<R> getValuesUnsafe(L key)
232     {
233         List<R> l = lists.get(key);
234         return l != null ? l : Collections.<R>emptyList();
235     }
236
237     /**
238      * Returns a read-only reference to the values. The list is valid as long as
239      * it contains elements.
240      * 
241      * @param key
242      * @return empty unmodifiable list if there is no list with the specified key,
243      *         otherwise an unmodifiable version of the stored list
244      */
245     public List<R> getValues(L key)
246     {
247         List<R> l = lists.get(key);
248         if (l==null) return Collections.emptyList();
249         return Collections.unmodifiableList(l);
250     }
251
252     /**
253      * Returns a copy of the values
254      * 
255      * @param key
256      * @return empty unmodifiable list if there is no list with the specified key,
257      *         otherwise a copy of the stored list
258      */
259     public List<R> getValuesSnapshot(L key)
260     {
261         List<R> l = lists.get(key);
262         if (l==null) return Collections.emptyList();
263         return new ArrayList<R>(l);
264     }
265     
266     public List<R> getAllValuesSnapshot() 
267     {
268         return getAllValuesSnapshot(null);
269     }
270
271     public List<R> getAllValuesSnapshot(List<R> result) 
272     {
273         if (result == null)
274             result = new ArrayList<R>();
275         for (List<R> right : lists.values()) {
276             result.addAll(right);
277         }
278         return result;
279     }
280
281     public boolean isEmpty() {
282         return lists.isEmpty();
283     }
284
285     /**
286      * Makes _this_ maplist immutable.
287      */
288     public void makeImmutable() {
289         for (Entry<L, List<R>> e : lists.entrySet())
290             lists.put(e.getKey(), Collections.unmodifiableList(e.getValue()));
291         lists = Collections.unmodifiableMap(lists);
292     }
293
294 }