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
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 * Semantum Oy - added getHintsUnsafe
\r
12 *******************************************************************************/
\r
15 * @author Toni Kalajainen
\r
17 package org.simantics.utils.datastructures.hints;
\r
19 import gnu.trove.map.hash.THashMap;
\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
32 * @author Toni Kalajainen
\r
34 public class HintContext extends AbstractHintObservable implements IHintContext, Cloneable {
\r
36 protected Map<Key, Object> hints = new THashMap<Key, Object>();
\r
39 public void clearWithoutNotification() {
\r
40 synchronized (this) {
\r
46 public synchronized boolean containsHint(Key key) {
\r
47 return hints.get(key) != null;
\r
50 @SuppressWarnings("unchecked")
\r
52 public <E> E getHint(Key key) {
\r
54 throw new IllegalArgumentException("key is null");
\r
55 synchronized (this) {
\r
56 return (E) hints.get(key);
\r
60 @SuppressWarnings("unchecked")
\r
62 public <E> E removeHint(Key key) {
\r
64 throw new IllegalArgumentException("key is null");
\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
74 return (E) oldValue;
\r
78 * Set a set of hints
\r
81 public void removeHints(Collection<? extends Key> keys) {
\r
82 List<Runnable> notifications = new ArrayList<Runnable>(hints.size());
\r
83 synchronized (this) {
\r
85 for (Key key : keys) {
\r
86 Object oldValue = this.hints.remove(key);
\r
87 if (oldValue == null)
\r
89 Runnable notification = createFireKeyRemovedRunnable(this, key, oldValue);
\r
90 notifications.add( notification );
\r
95 for (Runnable r : notifications)
\r
100 public void setHint(Key key, Object value) {
\r
102 throw new IllegalArgumentException("key is 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
108 Runnable notification;
\r
109 synchronized(this) {
\r
110 Object oldValue = hints.put(key, value);
\r
111 notification = createFireKeyChangedRunnable(this, key, oldValue, value);
\r
113 notification.run();
\r
117 * Set a set of hints
\r
121 public void setHints(Map<Key, Object> hints) {
\r
122 List<Runnable> notifications = new ArrayList<Runnable>(hints.size());
\r
123 synchronized (this) {
\r
125 for (Entry<Key, Object> e : hints.entrySet()) {
\r
126 Key key = e.getKey();
\r
127 Object value = e.getValue();
\r
129 throw new IllegalArgumentException("a value is null for key " + e.getKey());
\r
130 Object oldValue = this.hints.put(key, value);
\r
132 Runnable notification = createFireKeyChangedRunnable(this, key,
\r
134 notifications.add( notification );
\r
139 for (Runnable r : notifications)
\r
143 public Object setHintWithoutNotification(Key key, Object value) {
\r
145 throw new IllegalArgumentException("key is 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
151 synchronized(this) {
\r
152 return hints.put(key, value);
\r
157 * Removes the specified hint without sending notifications about changes.
\r
161 * @return removed hint value
\r
163 @SuppressWarnings("unchecked")
\r
164 public <E> E removeHintWithoutNotification(Key key) {
\r
166 throw new IllegalArgumentException("key is null");
\r
168 Object oldValue = null;
\r
169 synchronized(this) {
\r
170 oldValue = hints.remove(key);
\r
172 return (E) oldValue;
\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
180 * @param hints the new hints to set
\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
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
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
203 for (Key key : removedKeys) {
\r
204 Object oldValue = this.hints.remove(key);
\r
205 if (oldValue == null)
\r
208 Runnable notification = createFireKeyRemovedRunnable(this, key, oldValue);
\r
209 notifications.add( notification );
\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
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
222 Runnable notification = createFireKeyChangedRunnable(this, key,
\r
224 notifications.add( notification );
\r
229 for (Runnable r : notifications)
\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
239 * @param hints the new hints to set
\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
247 throw new IllegalArgumentException("a value is null for key " + e.getKey());
\r
248 this.hints.put(key, value);
\r
254 * Compares two object for equality.
\r
256 * Some times it is annoying to compare two objects if their
\r
257 * value may be null.
\r
261 * @return true if equal or both null
\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
271 public synchronized Map<Key, Object> getHints() {
\r
272 return new HashMap<Key, Object>(hints);
\r
276 public Map<Key, Object> getHintsUnsafe() {
\r
280 @SuppressWarnings("unchecked")
\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
292 public synchronized int size()
\r
294 return hints.size();
\r
298 public Object clone() {
\r
300 return super.clone();
\r
301 } catch (CloneNotSupportedException e) {
\r
302 throw new Error(e);
\r