]> gerrit.simantics Code Review - simantics/platform.git/blob
2be216d30df840a8e9147501b2b19e42b215b3ba
[simantics/platform.git] /
1 /*******************************************************************************
2  * Copyright (c) 2007, 2015 Association for Decentralized Information Management
3  * in Industry THTH ry.
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
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *     Semantum Oy - added getHintsUnsafe
12  *******************************************************************************/
13 /*
14  *
15  * @author Toni Kalajainen
16  */
17 package org.simantics.utils.datastructures.hints;
18
19 import java.util.ArrayList;
20 import java.util.HashMap;
21 import java.util.HashSet;
22 import java.util.Map;
23 import java.util.Set;
24 import java.util.Map.Entry;
25
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;
33
34
35 public class HintStack extends AbstractHintObservable implements IHintStack {
36
37         /** Stack of hint contexts */
38         PriorityStack<IHintContext> stack = 
39                 new PriorityStack<IHintContext>(IHintContext.class);    
40         
41         IPriorityStackListener<IHintContext> stackListener =
42                 new IPriorityStackListener<IHintContext>() {
43                         @Override
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;
51                                         
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();
56
57                                         int index = stack.indexOf(item);
58                                         
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());
62                                                                                 
63                                         // Iterate all lower layers, and see if any key collides
64                                         for (int i=index-1; i>=0; i--)
65                                         {
66                                                 Map<Key, Object> lowerLevelHints = list[i].getHints();
67                                                 lowerLevelHints.keySet().retainAll(keys);
68                                                 keys.removeAll(lowerLevelHints.keySet());
69                                                 oldValues.putAll(lowerLevelHints);
70                                         }
71                                         
72                                         // Send events for all new hints
73                                         for (Key key : keys)
74                                         {
75                                                 Object newValue         = hints.get(key);
76                                                 addFireKeyChangedExecutables(executables, HintStack.this, key, null, newValue);
77                                         }
78                                         
79                                         // Send events for all hints that were overridden
80                                         for (Entry<Key, Object> hint : oldValues.entrySet())
81                                         {
82                                                 Key key                         = hint.getKey();
83                                                 Object oldValue         = hint.getValue();
84                                                 Object newValue         = hints.get(key);
85                                                 addFireKeyChangedExecutables(executables, HintStack.this, key, oldValue, newValue);
86                                         }
87                                 }
88                                 ThreadUtils.multiSyncExec(executables);
89                         }
90                         @Override
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;
98                                         
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();
103
104                                         int index = stack.indexOf(item);
105                                         
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());
109                                                                                 
110                                         // Iterate all lower layers, and see if any key collides
111                                         for (int i=index-1; i>=0; i--)
112                                         {
113                                                 Map<Key, Object> lowerLevelHints = list[i].getHints();
114                                                 lowerLevelHints.keySet().retainAll(keys);
115                                                 keys.removeAll(lowerLevelHints.keySet());
116                                                 overriddenValues.putAll(lowerLevelHints);
117                                         }
118                                         
119                                         // Send events for all values were removed and were never overridden
120                                         for (Key key : keys)
121                                         {
122                                                 Object oldValue         = hints.get(key);
123                                                 addFireKeyRemovedExecutables(executables, HintStack.this, key, oldValue);
124                                         }
125                                         
126                                         // Send events. overridden hints have become effective
127                                         for (Entry<Key, Object> hint : overriddenValues.entrySet())
128                                         {
129                                                 Key key                         = hint.getKey();
130                                                 Object newValue         = hint.getValue();
131                                                 Object oldValue         = hints.get(key);
132                                                 addFireKeyChangedExecutables(executables, HintStack.this, key, oldValue, newValue);
133                                         }                                       
134                                 }
135                                 ThreadUtils.multiSyncExec(executables);
136                         }
137         };
138         
139         IHintListener ctxListener = new IHintListener() {
140                 @Override
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();
145
146                                 int index = stack.indexOf((IHintContext)sender);
147                                 if (index<0) return;
148                                 
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;
152                                 
153                                 // Check if this hint overrides another hint
154                                 if (oldValue==null) {
155                                         for (int i=index-1; i>=0; i--)
156                                         {
157                                                 oldValue = list[i].getHint(key);
158                                                 if (oldValue!=null) break;
159                                         }
160                                 }
161                                 notifications = createFireKeyChangedRunnable(HintStack.this, key, oldValue, newValue);                          
162                         }
163                         notifications.run();
164                 }
165                 @Override
166                 public void hintRemoved(IHintObservable sender, Key key, Object oldValue) {
167                         Runnable notification;
168                         synchronized(HintStack.this.stack) {
169                                 IHintContext list[] = stack.toArray();
170
171                                 int index = stack.indexOf((IHintContext)sender);
172                                 if (index<0) return;
173                                 
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;
177                                 
178                                 // Check if this hint overrides another hint
179                                 Object newValue = null;
180                                 for (int i=index-1; i>=0; i--)
181                                 {
182                                         newValue = list[i].getHint(key);
183                                         if (newValue!=null) break;
184                                 }
185                                 if (newValue!=null)
186                                         notification = createFireKeyChangedRunnable(HintStack.this, key, oldValue, newValue);                
187                                 else
188                         notification = createFireKeyRemovedRunnable(HintStack.this, key, oldValue);              
189                         }
190                         notification.run();
191                 }
192         };
193         
194         public HintStack() {
195                 stack.addStackListener(stackListener);
196         }
197         
198         @Override
199         public void addHintContext(IHintContext hints, int priority) {
200                 stack.add(hints, priority);
201         }
202
203         @SuppressWarnings("unchecked")
204         @Override
205         public <E> E getHint(Key key) {
206                 IHintContext list [] = stack.toArray();
207                 for (int i=list.length-1; i>=0; i--)
208                 {
209                         IHintContext ctx = list[i];
210                         Object value = ctx.getHint(key);
211                         if (value!=null) return (E) value;
212                 }
213                 return null;
214         }
215
216         @Override
217         public boolean containsHint(Key key) {
218                 IHintContext list [] = stack.toArray();
219                 for (int i=list.length-1; i>=0; i--)
220                 {
221                         IHintContext ctx = list[i];
222                         if (ctx.containsHint(key))
223                                 return true;
224                 }
225                 return false;
226         }
227
228         @Override
229         public boolean removeHintContext(IHintContext hints) {
230                 return stack.remove(hints);
231         }
232
233         @Override
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());
238                 return result;
239         }
240
241         @Override
242         public synchronized Map<Key, Object> getHintsUnsafe() {
243                 return getHints();
244         }
245
246         @Override
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));
251                 return result;
252         }
253         
254         /**
255          * Returns a hint context whose read operations originate from the stack, and
256          * write operations are performed on a local stack
257          * 
258          * @param ctx the hint context to write into
259          * @return write-localized hint context based on this hint stack
260          */
261         public IHintContext createStackRead(final IHintContext ctx)
262         {
263                 return new IHintContext() {
264                         @Override
265                         public void clearWithoutNotification() {
266                                 ctx.clearWithoutNotification();
267                         }
268                         @SuppressWarnings("unchecked")
269                         @Override
270                         public <E> E removeHint(Key key) {
271                                 return (E) ctx.removeHint(key);
272                         }
273                         @Override
274                         public void setHint(Key key, Object value) {
275                                 ctx.setHint(key, value);
276                         }
277                         @Override
278                         public void addHintListener(IHintListener listener) {
279                                 HintStack.this.addHintListener(listener);
280                         }
281                         @Override
282                         public void addHintListener(IThreadWorkQueue threadAccess, IHintListener listener) {
283                                 HintStack.this.addHintListener(threadAccess, listener);
284                         }
285                         @Override
286                         public void addKeyHintListener(Key key, IHintListener listener) {
287                                 HintStack.this.addKeyHintListener(key, listener);
288                         }
289                         @Override
290                         public void addKeyHintListener(IThreadWorkQueue threadAccess, Key key, IHintListener listener) {
291                                 HintStack.this.addKeyHintListener(threadAccess, key, listener);
292                         }
293                         @Override
294                         public boolean containsHint(Key key) {
295                                 return HintStack.this.containsHint(key);
296                         }
297                         @SuppressWarnings("unchecked")
298                         @Override
299                         public <E> E getHint(Key key) {
300                                 return (E) HintStack.this.getHint(key);
301                         }
302                         @Override
303                         public Map<Key, Object> getHints() {
304                                 return HintStack.this.getHints();
305                         }
306                         @Override
307                         public Map<Key, Object> getHintsUnsafe() {
308                                 return HintStack.this.getHintsUnsafe();
309                         }
310                         @Override
311                         public void removeHintListener(IHintListener listener) {
312                                 HintStack.this.removeHintListener(listener);
313                         }
314                         @Override
315                         public void removeHintListener(IThreadWorkQueue threadAccess, IHintListener listener) {
316                                 HintStack.this.removeHintListener(threadAccess, listener);
317                         }
318                         @Override
319                         public void removeKeyHintListener(Key key, IHintListener listener) {
320                                 HintStack.this.removeKeyHintListener(key, listener);
321                         }
322                         @Override
323                         public void removeKeyHintListener(IThreadWorkQueue threadAccess, Key key, IHintListener listener) {
324                                 HintStack.this.removeKeyHintListener(threadAccess, key, listener);
325                         }
326                         @Override
327                         public <E extends Key> Map<E, Object> getHintsOfClass(Class<E> clazz) {
328                                 return HintStack.this.getHintsOfClass(clazz);
329                         }
330                         @Override
331                         public void setHints(Map<Key, Object> hints) {
332                                 ctx.setHints(hints);
333                         }};
334         }
335         
336 }