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 java.util.ArrayList;
20 import java.util.HashMap;
21 import java.util.HashSet;
24 import java.util.Map.Entry;
26 import org.simantics.utils.datastructures.hints.IHintContext.Key;
27 import org.simantics.utils.datastructures.prioritystack.IPriorityStack;
28 import org.simantics.utils.datastructures.prioritystack.IPriorityStackListener;
29 import org.simantics.utils.datastructures.prioritystack.PriorityStack;
30 import org.simantics.utils.threads.Executable;
31 import org.simantics.utils.threads.IThreadWorkQueue;
32 import org.simantics.utils.threads.ThreadUtils;
35 public class HintStack extends AbstractHintObservable implements IHintStack {
37 /** Stack of hint contexts */
38 PriorityStack<IHintContext> stack =
39 new PriorityStack<IHintContext>(IHintContext.class);
41 IPriorityStackListener<IHintContext> stackListener =
42 new IPriorityStackListener<IHintContext>() {
44 public void itemAdded(IPriorityStack<IHintContext> sender, IHintContext item) {
45 item.addHintListener(ctxListener);
46 ArrayList<Executable> executables = new ArrayList<Executable>();
47 synchronized(HintStack.this.stack) {
48 if (!hasListeners()) return;
49 Map<Key, Object> hints = item.getHints();
50 if (hints.size()==0) return;
52 Set<Key> keys = new HashSet<Key>(hints.keySet());
53 Map<Key, Object> oldValues = new HashMap<Key, Object>();
54 // Check if there is a change to the stack
55 IHintContext list[] = stack.toArray();
57 int index = stack.indexOf(item);
59 // Remove all keys that are overridden by higher priority keys
60 for (int i=index+1; i<list.length; i++)
61 keys.removeAll(list[i].getHints().keySet());
63 // Iterate all lower layers, and see if any key collides
64 for (int i=index-1; i>=0; i--)
66 Map<Key, Object> lowerLevelHints = list[i].getHints();
67 lowerLevelHints.keySet().retainAll(keys);
68 keys.removeAll(lowerLevelHints.keySet());
69 oldValues.putAll(lowerLevelHints);
72 // Send events for all new hints
75 Object newValue = hints.get(key);
76 addFireKeyChangedExecutables(executables, HintStack.this, key, null, newValue);
79 // Send events for all hints that were overridden
80 for (Entry<Key, Object> hint : oldValues.entrySet())
82 Key key = hint.getKey();
83 Object oldValue = hint.getValue();
84 Object newValue = hints.get(key);
85 addFireKeyChangedExecutables(executables, HintStack.this, key, oldValue, newValue);
88 ThreadUtils.multiSyncExec(executables);
91 public void itemRemoved(IPriorityStack<IHintContext> sender, IHintContext item) {
92 item.removeHintListener(ctxListener);
93 ArrayList<Executable> executables = new ArrayList<Executable>();
94 synchronized(HintStack.this.stack) {
95 if (!hasListeners()) return;
96 Map<Key, Object> hints = item.getHints();
97 if (hints.size()==0) return;
99 Set<Key> keys = new HashSet<Key>(hints.keySet());
100 Map<Key, Object> overriddenValues = new HashMap<Key, Object>();
101 // Check if there is a change to the stack
102 IHintContext list[] = stack.toArray();
104 int index = stack.indexOf(item);
106 // Remove all keys that are overridden by higher priority keys
107 for (int i=index+1; i<list.length; i++)
108 keys.removeAll(list[i].getHints().keySet());
110 // Iterate all lower layers, and see if any key collides
111 for (int i=index-1; i>=0; i--)
113 Map<Key, Object> lowerLevelHints = list[i].getHints();
114 lowerLevelHints.keySet().retainAll(keys);
115 keys.removeAll(lowerLevelHints.keySet());
116 overriddenValues.putAll(lowerLevelHints);
119 // Send events for all values were removed and were never overridden
122 Object oldValue = hints.get(key);
123 addFireKeyRemovedExecutables(executables, HintStack.this, key, oldValue);
126 // Send events. overridden hints have become effective
127 for (Entry<Key, Object> hint : overriddenValues.entrySet())
129 Key key = hint.getKey();
130 Object newValue = hint.getValue();
131 Object oldValue = hints.get(key);
132 addFireKeyChangedExecutables(executables, HintStack.this, key, oldValue, newValue);
135 ThreadUtils.multiSyncExec(executables);
139 IHintListener ctxListener = new IHintListener() {
141 public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {
142 Runnable notifications;
143 synchronized(HintStack.this.stack) {
144 IHintContext list[] = stack.toArray();
146 int index = stack.indexOf((IHintContext)sender);
149 // Check whether the key is overridden
150 for (int i=index+1; i<list.length; i++)
151 if (list[i].getHint(key)!=null) return;
153 // Check if this hint overrides another hint
154 if (oldValue==null) {
155 for (int i=index-1; i>=0; i--)
157 oldValue = list[i].getHint(key);
158 if (oldValue!=null) break;
161 notifications = createFireKeyChangedRunnable(HintStack.this, key, oldValue, newValue);
166 public void hintRemoved(IHintObservable sender, Key key, Object oldValue) {
167 Runnable notification;
168 synchronized(HintStack.this.stack) {
169 IHintContext list[] = stack.toArray();
171 int index = stack.indexOf((IHintContext)sender);
174 // Check whether the key was overridden
175 for (int i=index+1; i<list.length; i++)
176 if (list[i].getHint(key)!=null) return;
178 // Check if this hint overrides another hint
179 Object newValue = null;
180 for (int i=index-1; i>=0; i--)
182 newValue = list[i].getHint(key);
183 if (newValue!=null) break;
186 notification = createFireKeyChangedRunnable(HintStack.this, key, oldValue, newValue);
188 notification = createFireKeyRemovedRunnable(HintStack.this, key, oldValue);
195 stack.addStackListener(stackListener);
199 public void addHintContext(IHintContext hints, int priority) {
200 stack.add(hints, priority);
203 @SuppressWarnings("unchecked")
205 public <E> E getHint(Key key) {
206 IHintContext list [] = stack.toArray();
207 for (int i=list.length-1; i>=0; i--)
209 IHintContext ctx = list[i];
210 Object value = ctx.getHint(key);
211 if (value!=null) return (E) value;
217 public boolean containsHint(Key key) {
218 IHintContext list [] = stack.toArray();
219 for (int i=list.length-1; i>=0; i--)
221 IHintContext ctx = list[i];
222 if (ctx.containsHint(key))
229 public boolean removeHintContext(IHintContext hints) {
230 return stack.remove(hints);
234 public synchronized Map<Key, Object> getHints() {
235 Map<Key, Object> result = new HashMap<Key, Object>();
236 for (IHintContext ctx : stack.toArray())
237 result.putAll(ctx.getHints());
242 public synchronized Map<Key, Object> getHintsUnsafe() {
247 public <E extends Key> Map<E, Object> getHintsOfClass(Class<E> clazz) {
248 Map<E, Object> result = new HashMap<E, Object>();
249 for (IHintContext ctx : stack.toArray())
250 result.putAll(ctx.getHintsOfClass(clazz));
255 * Returns a hint context whose read operations originate from the stack, and
256 * write operations are performed on a local stack
258 * @param ctx the hint context to write into
259 * @return write-localized hint context based on this hint stack
261 public IHintContext createStackRead(final IHintContext ctx)
263 return new IHintContext() {
265 public void clearWithoutNotification() {
266 ctx.clearWithoutNotification();
268 @SuppressWarnings("unchecked")
270 public <E> E removeHint(Key key) {
271 return (E) ctx.removeHint(key);
274 public void setHint(Key key, Object value) {
275 ctx.setHint(key, value);
278 public void addHintListener(IHintListener listener) {
279 HintStack.this.addHintListener(listener);
282 public void addHintListener(IThreadWorkQueue threadAccess, IHintListener listener) {
283 HintStack.this.addHintListener(threadAccess, listener);
286 public void addKeyHintListener(Key key, IHintListener listener) {
287 HintStack.this.addKeyHintListener(key, listener);
290 public void addKeyHintListener(IThreadWorkQueue threadAccess, Key key, IHintListener listener) {
291 HintStack.this.addKeyHintListener(threadAccess, key, listener);
294 public boolean containsHint(Key key) {
295 return HintStack.this.containsHint(key);
297 @SuppressWarnings("unchecked")
299 public <E> E getHint(Key key) {
300 return (E) HintStack.this.getHint(key);
303 public Map<Key, Object> getHints() {
304 return HintStack.this.getHints();
307 public Map<Key, Object> getHintsUnsafe() {
308 return HintStack.this.getHintsUnsafe();
311 public void removeHintListener(IHintListener listener) {
312 HintStack.this.removeHintListener(listener);
315 public void removeHintListener(IThreadWorkQueue threadAccess, IHintListener listener) {
316 HintStack.this.removeHintListener(threadAccess, listener);
319 public void removeKeyHintListener(Key key, IHintListener listener) {
320 HintStack.this.removeKeyHintListener(key, listener);
323 public void removeKeyHintListener(IThreadWorkQueue threadAccess, Key key, IHintListener listener) {
324 HintStack.this.removeKeyHintListener(threadAccess, key, listener);
327 public <E extends Key> Map<E, Object> getHintsOfClass(Class<E> clazz) {
328 return HintStack.this.getHintsOfClass(clazz);
331 public void setHints(Map<Key, Object> hints) {