]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.utils.datastructures/src/org/simantics/utils/datastructures/hints/HintContext.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.utils.datastructures / src / org / simantics / utils / datastructures / hints / HintContext.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2015 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *     Semantum Oy - added getHintsUnsafe\r
12  *******************************************************************************/\r
13 /*\r
14  *\r
15  * @author Toni Kalajainen\r
16  */\r
17 package org.simantics.utils.datastructures.hints;\r
18 \r
19 import gnu.trove.map.hash.THashMap;\r
20 \r
21 import java.util.ArrayList;\r
22 import java.util.Collection;\r
23 import java.util.HashMap;\r
24 import java.util.HashSet;\r
25 import java.util.List;\r
26 import java.util.Map;\r
27 import java.util.Set;\r
28 import java.util.Map.Entry;\r
29 \r
30 /**\r
31  * \r
32  * @author Toni Kalajainen\r
33  */\r
34 public class HintContext extends AbstractHintObservable implements IHintContext, Cloneable {\r
35 \r
36     protected Map<Key, Object> hints = new THashMap<Key, Object>();\r
37 \r
38     @Override\r
39     public void clearWithoutNotification() {\r
40         synchronized (this) {\r
41             hints.clear();\r
42         }\r
43     }\r
44 \r
45     @Override\r
46     public synchronized boolean containsHint(Key key) {\r
47         return hints.get(key) != null;\r
48     }\r
49 \r
50     @SuppressWarnings("unchecked")\r
51     @Override\r
52     public <E> E getHint(Key key) {\r
53         if (key == null)\r
54             throw new IllegalArgumentException("key is null");\r
55         synchronized (this) {\r
56             return (E) hints.get(key);\r
57         }\r
58     }\r
59 \r
60     @SuppressWarnings("unchecked")\r
61     @Override\r
62     public <E> E removeHint(Key key) {\r
63         if (key == null)\r
64             throw new IllegalArgumentException("key is null");\r
65 \r
66         Runnable notification;\r
67         Object oldValue = null;\r
68         synchronized(this) {\r
69             oldValue = hints.remove(key);\r
70             if (oldValue==null) return null;\r
71             notification = createFireKeyRemovedRunnable(this, key, oldValue);\r
72         }\r
73         notification.run();\r
74         return (E) oldValue;\r
75     }\r
76 \r
77     /**\r
78      * Set a set of hints\r
79      * @param hints\r
80      */\r
81     public void removeHints(Collection<? extends Key> keys) {\r
82         List<Runnable> notifications = new ArrayList<Runnable>(hints.size());\r
83         synchronized (this) {\r
84             // Remove first\r
85             for (Key key : keys) {\r
86                 Object oldValue = this.hints.remove(key);\r
87                 if (oldValue == null)\r
88                     continue;\r
89                 Runnable notification = createFireKeyRemovedRunnable(this, key, oldValue);\r
90                 notifications.add( notification );\r
91             }\r
92         }\r
93 \r
94         // Notify then\r
95         for (Runnable r : notifications)\r
96             r.run();\r
97     }\r
98 \r
99     @Override\r
100     public void setHint(Key key, Object value) {\r
101         if (key == null)\r
102             throw new IllegalArgumentException("key is null");\r
103         if (value == null)\r
104             throw new IllegalArgumentException("value is null");\r
105         if (!key.isValueAccepted(value))\r
106             throw new RuntimeException("Value \""+value+"\" is not accepted with key "+key.getClass().getName());\r
107 \r
108         Runnable notification;\r
109         synchronized(this) {\r
110             Object oldValue = hints.put(key, value);\r
111             notification = createFireKeyChangedRunnable(this, key, oldValue, value);\r
112         }\r
113         notification.run();\r
114     }\r
115 \r
116     /**\r
117      * Set a set of hints\r
118      * @param hints\r
119      */\r
120     @Override\r
121     public void setHints(Map<Key, Object> hints) {\r
122         List<Runnable> notifications = new ArrayList<Runnable>(hints.size());\r
123         synchronized (this) {\r
124             // Insert first\r
125             for (Entry<Key, Object> e : hints.entrySet()) {\r
126                 Key key = e.getKey();\r
127                 Object value = e.getValue();\r
128                 if (value == null)\r
129                     throw new IllegalArgumentException("a value is null for key " + e.getKey());\r
130                 Object oldValue = this.hints.put(key, value);\r
131 \r
132                 Runnable notification = createFireKeyChangedRunnable(this, key,\r
133                         oldValue, value);\r
134                 notifications.add( notification );\r
135             }\r
136         }\r
137 \r
138         // Notify then\r
139         for (Runnable r : notifications)\r
140             r.run();\r
141     }\r
142 \r
143     public Object setHintWithoutNotification(Key key, Object value) {\r
144         if (key == null)\r
145             throw new IllegalArgumentException("key is null");\r
146         if (value == null)\r
147             throw new IllegalArgumentException("value is null");\r
148         if (!key.isValueAccepted(value))\r
149             throw new RuntimeException("Value \""+value+"\" is not accepted with key "+key.getClass().getName());\r
150 \r
151         synchronized(this) {\r
152             return hints.put(key, value);\r
153         }\r
154     }\r
155 \r
156     /**\r
157      * Removes the specified hint without sending notifications about changes.\r
158      * \r
159      * @param <E>\r
160      * @param key\r
161      * @return removed hint value\r
162      */\r
163     @SuppressWarnings("unchecked")\r
164     public <E> E removeHintWithoutNotification(Key key) {\r
165         if (key == null)\r
166             throw new IllegalArgumentException("key is null");\r
167 \r
168         Object oldValue = null;\r
169         synchronized(this) {\r
170             oldValue = hints.remove(key);\r
171         }\r
172         return (E) oldValue;\r
173     }\r
174 \r
175     /**\r
176      * Replace the current hints with the specified set of hints. Hints\r
177      * that were previously included but are not contained by the new map will\r
178      * not be preserved by this operation.\r
179      * \r
180      * @param hints the new hints to set\r
181      */\r
182     public void replaceHintsWithoutNotification(Map<Key, Object> hints) {\r
183         Map<Key, Object> copy = new HashMap<Key, Object>(hints);\r
184         synchronized (this) {\r
185             this.hints = copy;\r
186         }\r
187     }\r
188 \r
189     /**\r
190      * Replace the current set of hints with the new specified set of hints.\r
191      * Notifications are sent for removed and changed hints.\r
192      * \r
193      * @param hints\r
194      */\r
195     public void replaceHints(Map<Key, Object> newHints) {\r
196         List<Runnable> notifications = new ArrayList<Runnable>(Math.max(this.hints.size(), newHints.size()));\r
197         synchronized (this) {\r
198             // Calculate removed keys\r
199             Set<Key> removedKeys = new HashSet<Key>(this.hints.keySet());\r
200             removedKeys.removeAll(newHints.keySet());\r
201 \r
202             // Remove keys\r
203             for (Key key : removedKeys) {\r
204                 Object oldValue = this.hints.remove(key);\r
205                 if (oldValue == null)\r
206                     continue;\r
207 \r
208                 Runnable notification = createFireKeyRemovedRunnable(this, key, oldValue);\r
209                 notifications.add( notification );\r
210             }\r
211 \r
212             // Replace/set existing/new hints\r
213             for (Entry<Key, Object> e : newHints.entrySet()) {\r
214                 Key key = e.getKey();\r
215                 Object value = e.getValue();\r
216                 if (value == null)\r
217                     throw new IllegalArgumentException("a value is null for key " + e.getKey());\r
218                 Object oldValue = this.hints.put(key, value);\r
219                 if (value.equals(oldValue))\r
220                     continue;\r
221 \r
222                 Runnable notification = createFireKeyChangedRunnable(this, key,\r
223                         oldValue, value);\r
224                 notifications.add( notification );\r
225             }\r
226         }\r
227 \r
228         // Notify then\r
229         for (Runnable r : notifications)\r
230             r.run();\r
231     }\r
232 \r
233     /**\r
234      * Set a set of hints without notifying any generic hint or key-specific\r
235      * listeners. This method will only replace the possible previous values of\r
236      * the hints included in the specified map. Hints not included in the map\r
237      * will be preserved as such.\r
238      * \r
239      * @param hints the new hints to set\r
240      */\r
241     public void setHintsWithoutNotification(Map<Key, Object> hints) {\r
242         synchronized (this) {\r
243             for (Entry<Key, Object> e : hints.entrySet()) {\r
244                 Key key = e.getKey();\r
245                 Object value = e.getValue();\r
246                 if (value == null)\r
247                     throw new IllegalArgumentException("a value is null for key " + e.getKey());\r
248                 this.hints.put(key, value);\r
249             }\r
250         }\r
251     }\r
252 \r
253     /**\r
254      * Compares two object for equality.\r
255      * <p>\r
256      * Some times it is annoying to compare two objects if their\r
257      * value may be null.\r
258      * \r
259      * @param o1 obj1\r
260      * @param o2 obj2\r
261      * @return true if equal or both null\r
262      */\r
263     public static boolean objectEquals(Object o1, Object o2) {\r
264         if (o1==o2) return true;\r
265         if (o1==null && o2==null) return true;\r
266         if (o1==null || o2==null) return false;\r
267         return o1.equals(o2);\r
268     }\r
269 \r
270     @Override\r
271     public synchronized Map<Key, Object> getHints() {\r
272         return new HashMap<Key, Object>(hints);\r
273     }\r
274 \r
275     @Override\r
276     public Map<Key, Object> getHintsUnsafe() {\r
277         return hints;\r
278     }\r
279 \r
280     @SuppressWarnings("unchecked")\r
281     @Override\r
282     public synchronized <E extends Key> Map<E, Object> getHintsOfClass(Class<E> clazz) {\r
283         Map<E, Object> result = new HashMap<E, Object>();\r
284         for (Entry<Key, Object> e : hints.entrySet()) {\r
285             Key key = e.getKey();\r
286             if (clazz.isAssignableFrom(key.getClass()))\r
287                 result.put((E)key, e.getValue());\r
288         }\r
289         return result;\r
290     }\r
291 \r
292     public synchronized int size()\r
293     {\r
294         return hints.size();\r
295     }\r
296 \r
297     @Override\r
298     public Object clone() {\r
299         try {\r
300             return super.clone();\r
301         } catch (CloneNotSupportedException e) {\r
302             throw new Error(e);\r
303         }\r
304     }\r
305 \r
306 }\r