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