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