]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.utils.datastructures/src/org/simantics/utils/datastructures/hints/AbstractHintObservable.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.utils.datastructures / src / org / simantics / utils / datastructures / hints / AbstractHintObservable.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 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  *******************************************************************************/\r
12 /*\r
13  *\r
14  * @author Toni Kalajainen\r
15  */\r
16 package org.simantics.utils.datastructures.hints;\r
17 \r
18 import gnu.trove.map.hash.THashMap;\r
19 \r
20 import java.lang.reflect.Method;\r
21 import java.util.ArrayList;\r
22 import java.util.Collection;\r
23 import java.util.List;\r
24 import java.util.Map;\r
25 \r
26 import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
27 import org.simantics.utils.threads.Executable;\r
28 import org.simantics.utils.threads.IThreadWorkQueue;\r
29 import org.simantics.utils.threads.SyncListenerList;\r
30 import org.simantics.utils.threads.ThreadUtils;\r
31 \r
32 /**\r
33  * TODO Optimize class queries.\r
34  * \r
35  * @author Toni Kalajainen\r
36  */\r
37 public abstract class AbstractHintObservable implements IHintObservable {\r
38 \r
39         /** Global listeners */\r
40         protected SyncListenerList<IHintListener> listeners = \r
41                 new SyncListenerList<IHintListener>(IHintListener.class);\r
42         \r
43         /** Key specific listeners */\r
44         protected Map<Key, SyncListenerList<IHintListener>> keyListeners =\r
45                 new THashMap<Key, SyncListenerList<IHintListener>>(2);  \r
46         \r
47         private final static SyncListenerList<IHintListener> EMPTY_LIST = \r
48                 new SyncListenerList<IHintListener>(IHintListener.class);\r
49         private final static Runnable NO_ACTION = new Runnable() {\r
50                 @Override\r
51                 public void run() {\r
52                 }};\r
53         \r
54 \r
55         private synchronized SyncListenerList<IHintListener> getOrCreateKeyListeners(Key key)\r
56         {\r
57                 SyncListenerList<IHintListener> result = keyListeners.get(key);\r
58                 if (result==null) {\r
59                         result = new SyncListenerList<IHintListener>(IHintListener.class);\r
60                         keyListeners.put(key, result);\r
61                 }\r
62                 return result;\r
63         }\r
64         \r
65         protected synchronized SyncListenerList<IHintListener> getListenerList(Key forKey)\r
66         {\r
67                 return keyListeners.get(forKey);\r
68         }\r
69         \r
70         private static Method hintChanged = SyncListenerList.getMethod(IHintListener.class, "hintChanged");\r
71         private static Method hintRemoved = SyncListenerList.getMethod(IHintListener.class, "hintRemoved");\r
72 \r
73         protected void fireKeyChanged(IHintObservable sender, Key key, Object oldValue, Object newValue)\r
74         {                               \r
75                 createFireKeyChangedRunnable(sender, key, oldValue, newValue).run();\r
76         }\r
77         \r
78         protected synchronized Executable[] getFireKeyChangedExecutables(IHintObservable sender, Key key, Object oldValue, Object newValue)\r
79         {\r
80                 if (listeners.isEmpty() && keyListeners.isEmpty()) return Executable.EMPTY_ARRAY;\r
81                 List<Executable> list = new ArrayList<Executable>();\r
82                 addFireKeyChangedExecutables(list, sender, key, oldValue, newValue);\r
83                 return list.toArray(new Executable[list.size()]);\r
84         }\r
85         \r
86         protected synchronized Runnable createFireKeyChangedRunnable(final IHintObservable sender, final Key key, final Object oldValue, final Object newValue)\r
87         {\r
88                 SyncListenerList<IHintListener> l1 = listeners;\r
89                 SyncListenerList<IHintListener> l2 = keyListeners.get(key);\r
90                 if (l2==null) l2 = EMPTY_LIST;\r
91                 if (l1.isEmpty() && l2.isEmpty()) return NO_ACTION;\r
92                 if (!l1.executableInCurrentThread() || !l2.executableInCurrentThread()) {\r
93                         final Executable e[] = getFireKeyChangedExecutables(sender, key, oldValue, newValue); \r
94                         return new Runnable() {\r
95                                 @Override\r
96                                 public void run() {\r
97                                         ThreadUtils.multiSyncExec(e);\r
98                                 }};\r
99                 } else {\r
100                         final Map<IThreadWorkQueue, IHintListener[]> list1 = l1.getSnapshot(); \r
101                         final Map<IThreadWorkQueue, IHintListener[]> list2 = l2.getSnapshot();\r
102                         return new Runnable() {\r
103                                 @Override\r
104                                 public void run() {\r
105                                         if (list1!=null)\r
106                                         for (IHintListener[] ll : list1.values())\r
107                                                 for (IHintListener l : ll)\r
108                                                         l.hintChanged(sender, key, oldValue, newValue);\r
109                                         if (list2!=null)\r
110                                         for (IHintListener[] ll : list2.values())\r
111                                                 for (IHintListener l : ll)\r
112                                                         l.hintChanged(sender, key, oldValue, newValue);                                 \r
113                                 }\r
114                         };                      \r
115                 }\r
116         }\r
117 \r
118         protected synchronized void addFireKeyChangedExecutables(Collection<Executable> list, IHintObservable sender, Key key, Object oldValue, Object newValue)\r
119         {\r
120                 // Add Key specific listeners\r
121                 Object[] args = {sender, key, oldValue, newValue};\r
122                 SyncListenerList<IHintListener> keyListeners = getListenerList(key);\r
123                 if (keyListeners!=null) \r
124                         keyListeners.addExecutables(list, hintChanged, args);\r
125                 // Add generic listeners\r
126                 listeners.addExecutables(list, hintChanged, args);\r
127         }\r
128         \r
129         protected void fireKeyRemoved(IHintObservable sender, Key key, Object oldValue)\r
130         {               \r
131                 createFireKeyRemovedRunnable(sender, key, oldValue).run();\r
132         }\r
133         \r
134         protected synchronized Executable[] getFireKeyRemovedExecutables(IHintObservable sender, Key key, Object oldValue)\r
135         {\r
136                 List<Executable> list = new ArrayList<Executable>();\r
137                 addFireKeyRemovedExecutables(list, sender, key, oldValue);\r
138                 return list.toArray(new Executable[list.size()]);\r
139         }\r
140         \r
141         protected synchronized Runnable createFireKeyRemovedRunnable(final IHintObservable sender, final Key key, final Object oldValue)\r
142         {\r
143                 SyncListenerList<IHintListener> l1 = listeners;\r
144                 SyncListenerList<IHintListener> l2 = keyListeners.get(key);\r
145                 if (l2==null) l2 = EMPTY_LIST;\r
146                 if (l1.isEmpty() && l2.isEmpty()) return NO_ACTION;\r
147                 if (!l1.executableInCurrentThread() || !l2.executableInCurrentThread()) {\r
148                         final Executable e[] = getFireKeyRemovedExecutables(sender, key, oldValue); \r
149                         return new Runnable() {\r
150                                 @Override\r
151                                 public void run() {\r
152                                         ThreadUtils.multiSyncExec(e);\r
153                                 }};\r
154                 } else {                        \r
155                         final Map<IThreadWorkQueue, IHintListener[]> list1 = l1.getSnapshot(); \r
156                         final Map<IThreadWorkQueue, IHintListener[]> list2 = l2.getSnapshot();\r
157                         return new Runnable() {\r
158                                 @Override\r
159                                 public void run() {\r
160                                         for (IHintListener[] ll : list1.values())\r
161                                                 for (IHintListener l : ll)\r
162                                                         l.hintRemoved(sender, key, oldValue);                                   \r
163                                         for (IHintListener[] ll : list2.values())\r
164                                                 for (IHintListener l : ll)\r
165                                                         l.hintRemoved(sender, key, oldValue);                                   \r
166                                 }\r
167                         };                      \r
168                 }\r
169         }\r
170         \r
171         protected synchronized void addFireKeyRemovedExecutables(Collection<Executable> list, IHintObservable sender, Key key, Object oldValue)\r
172         {\r
173                 // Add Key specific listeners\r
174                 Object[] args = {sender, key, oldValue};\r
175                 SyncListenerList<IHintListener> keyListeners = getListenerList(key);\r
176                 if (keyListeners!=null) \r
177                         keyListeners.addExecutables(list, hintRemoved, args);\r
178                 // Add generic listeners\r
179                 listeners.addExecutables(list, hintRemoved, args);\r
180         }\r
181 \r
182         protected void fireKeyChanged(Key key, Object oldValue, Object newValue)\r
183         {\r
184                 fireKeyChanged(this, key, oldValue, newValue);\r
185         }\r
186         \r
187         protected void fireKeyRemoved(Key key, Object oldValue)\r
188         {\r
189                 fireKeyRemoved(this, key, oldValue);\r
190         }\r
191         \r
192         public synchronized boolean hasListeners() \r
193         {\r
194                 return !listeners.isEmpty() || !keyListeners.isEmpty();\r
195         }\r
196 \r
197         public void addHintListener(IHintListener listener) {\r
198                 listeners.add(listener);\r
199         }\r
200 \r
201         public void addKeyHintListener(Key key, IHintListener listener) {\r
202                 getOrCreateKeyListeners(key).add(listener);\r
203         }\r
204 \r
205         public void removeHintListener(IHintListener listener) {\r
206                 listeners.remove(listener);\r
207         }\r
208 \r
209         public synchronized void removeKeyHintListener(Key key, IHintListener listener) {\r
210                 SyncListenerList<IHintListener> list = keyListeners.get(key);\r
211                 if (list==null) return;\r
212                 list.remove(listener);\r
213                 if (list.isEmpty()) keyListeners.remove(key);           \r
214         }\r
215                 \r
216         /**\r
217          * Adds hint listener, which gets events for all hint changes\r
218          * \r
219          * @param listener\r
220          */\r
221         public void addHintListener(IThreadWorkQueue threadAccess, IHintListener listener)\r
222         {\r
223                 listeners.add(threadAccess, listener);\r
224         }\r
225         \r
226         /**\r
227          * Removes hint listener\r
228          * \r
229          * @param listener\r
230          */\r
231         public void removeHintListener(IThreadWorkQueue threadAccess, IHintListener listener)\r
232         {\r
233                 listeners.remove(threadAccess, listener);\r
234         }\r
235         \r
236         /**\r
237          * Adds hint listener for a specific key\r
238          * @param key\r
239          * @param listener\r
240          */\r
241         public void addKeyHintListener(IThreadWorkQueue threadAccess, Key key, IHintListener listener)\r
242         {\r
243                 getOrCreateKeyListeners(key).add(threadAccess, listener);               \r
244         }\r
245         \r
246         /**\r
247          * Removes hint listener\r
248          * @param key\r
249          * @param listener\r
250          */\r
251         public void removeKeyHintListener(IThreadWorkQueue threadAccess, Key key, IHintListener listener)\r
252         {\r
253                 SyncListenerList<IHintListener> list = keyListeners.get(key);\r
254                 if (list==null) return;\r
255                 list.remove(threadAccess, listener);\r
256                 if (list.isEmpty()) keyListeners.remove(key);           \r
257         }\r
258                 \r
259 }\r