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