1 /*******************************************************************************
2 * Copyright (c) 2007, 2015 Association for Decentralized Information Management
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
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 * Semantum Oy - added getHintsUnsafe
12 *******************************************************************************/
15 * @author Toni Kalajainen
17 package org.simantics.utils.datastructures.hints;
19 import gnu.trove.map.hash.THashMap;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.HashMap;
24 import java.util.HashSet;
25 import java.util.List;
28 import java.util.Map.Entry;
32 * @author Toni Kalajainen
34 public class HintContext extends AbstractHintObservable implements IHintContext, Cloneable {
36 protected Map<Key, Object> hints = new THashMap<Key, Object>();
39 public void clearWithoutNotification() {
46 public synchronized boolean containsHint(Key key) {
47 return hints.get(key) != null;
50 @SuppressWarnings("unchecked")
52 public <E> E getHint(Key key) {
54 throw new IllegalArgumentException("key is null");
56 return (E) hints.get(key);
60 @SuppressWarnings("unchecked")
62 public <E> E removeHint(Key key) {
64 throw new IllegalArgumentException("key is null");
66 Runnable notification;
67 Object oldValue = null;
69 oldValue = hints.remove(key);
70 if (oldValue==null) return null;
71 notification = createFireKeyRemovedRunnable(this, key, oldValue);
81 public void removeHints(Collection<? extends Key> keys) {
82 List<Runnable> notifications = new ArrayList<Runnable>(hints.size());
85 for (Key key : keys) {
86 Object oldValue = this.hints.remove(key);
89 Runnable notification = createFireKeyRemovedRunnable(this, key, oldValue);
90 notifications.add( notification );
95 for (Runnable r : notifications)
100 public void setHint(Key key, Object value) {
102 throw new IllegalArgumentException("key is null");
104 throw new IllegalArgumentException("value is null");
105 if (!key.isValueAccepted(value))
106 throw new RuntimeException("Value \""+value+"\" is not accepted with key "+key.getClass().getName());
108 Runnable notification;
110 Object oldValue = hints.put(key, value);
111 notification = createFireKeyChangedRunnable(this, key, oldValue, value);
121 public void setHints(Map<Key, Object> hints) {
122 List<Runnable> notifications = new ArrayList<Runnable>(hints.size());
123 synchronized (this) {
125 for (Entry<Key, Object> e : hints.entrySet()) {
126 Key key = e.getKey();
127 Object value = e.getValue();
129 throw new IllegalArgumentException("a value is null for key " + e.getKey());
130 Object oldValue = this.hints.put(key, value);
132 Runnable notification = createFireKeyChangedRunnable(this, key,
134 notifications.add( notification );
139 for (Runnable r : notifications)
143 public Object setHintWithoutNotification(Key key, Object value) {
145 throw new IllegalArgumentException("key is null");
147 throw new IllegalArgumentException("value is null");
148 if (!key.isValueAccepted(value))
149 throw new RuntimeException("Value \""+value+"\" is not accepted with key "+key.getClass().getName());
152 return hints.put(key, value);
157 * Removes the specified hint without sending notifications about changes.
161 * @return removed hint value
163 @SuppressWarnings("unchecked")
164 public <E> E removeHintWithoutNotification(Key key) {
166 throw new IllegalArgumentException("key is null");
168 Object oldValue = null;
170 oldValue = hints.remove(key);
176 * Replace the current hints with the specified set of hints. Hints
177 * that were previously included but are not contained by the new map will
178 * not be preserved by this operation.
180 * @param hints the new hints to set
182 public void replaceHintsWithoutNotification(Map<Key, Object> hints) {
183 Map<Key, Object> copy = new HashMap<Key, Object>(hints);
184 synchronized (this) {
190 * Replace the current set of hints with the new specified set of hints.
191 * Notifications are sent for removed and changed hints.
195 public void replaceHints(Map<Key, Object> newHints) {
196 List<Runnable> notifications = new ArrayList<Runnable>(Math.max(this.hints.size(), newHints.size()));
197 synchronized (this) {
198 // Calculate removed keys
199 Set<Key> removedKeys = new HashSet<Key>(this.hints.keySet());
200 removedKeys.removeAll(newHints.keySet());
203 for (Key key : removedKeys) {
204 Object oldValue = this.hints.remove(key);
205 if (oldValue == null)
208 Runnable notification = createFireKeyRemovedRunnable(this, key, oldValue);
209 notifications.add( notification );
212 // Replace/set existing/new hints
213 for (Entry<Key, Object> e : newHints.entrySet()) {
214 Key key = e.getKey();
215 Object value = e.getValue();
217 throw new IllegalArgumentException("a value is null for key " + e.getKey());
218 Object oldValue = this.hints.put(key, value);
219 if (value.equals(oldValue))
222 Runnable notification = createFireKeyChangedRunnable(this, key,
224 notifications.add( notification );
229 for (Runnable r : notifications)
234 * Set a set of hints without notifying any generic hint or key-specific
235 * listeners. This method will only replace the possible previous values of
236 * the hints included in the specified map. Hints not included in the map
237 * will be preserved as such.
239 * @param hints the new hints to set
241 public void setHintsWithoutNotification(Map<Key, Object> hints) {
242 synchronized (this) {
243 for (Entry<Key, Object> e : hints.entrySet()) {
244 Key key = e.getKey();
245 Object value = e.getValue();
247 throw new IllegalArgumentException("a value is null for key " + e.getKey());
248 this.hints.put(key, value);
254 * Compares two object for equality.
256 * Some times it is annoying to compare two objects if their
261 * @return true if equal or both null
263 public static boolean objectEquals(Object o1, Object o2) {
264 if (o1==o2) return true;
265 if (o1==null && o2==null) return true;
266 if (o1==null || o2==null) return false;
267 return o1.equals(o2);
271 public synchronized Map<Key, Object> getHints() {
272 return new HashMap<Key, Object>(hints);
276 public Map<Key, Object> getHintsUnsafe() {
280 @SuppressWarnings("unchecked")
282 public synchronized <E extends Key> Map<E, Object> getHintsOfClass(Class<E> clazz) {
283 Map<E, Object> result = new HashMap<E, Object>();
284 for (Entry<Key, Object> e : hints.entrySet()) {
285 Key key = e.getKey();
286 if (clazz.isAssignableFrom(key.getClass()))
287 result.put((E)key, e.getValue());
292 public synchronized int size()
298 public Object clone() {
300 return super.clone();
301 } catch (CloneNotSupportedException e) {