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
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
14 * @author Toni Kalajainen
\r
16 package org.simantics.utils.datastructures.hints;
\r
18 import gnu.trove.map.hash.THashMap;
\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
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
33 * TODO Optimize class queries.
\r
35 * @author Toni Kalajainen
\r
37 public abstract class AbstractHintObservable implements IHintObservable {
\r
39 /** Global listeners */
\r
40 protected SyncListenerList<IHintListener> listeners =
\r
41 new SyncListenerList<IHintListener>(IHintListener.class);
\r
43 /** Key specific listeners */
\r
44 protected Map<Key, SyncListenerList<IHintListener>> keyListeners =
\r
45 new THashMap<Key, SyncListenerList<IHintListener>>(2);
\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
55 private synchronized SyncListenerList<IHintListener> getOrCreateKeyListeners(Key key)
\r
57 SyncListenerList<IHintListener> result = keyListeners.get(key);
\r
59 result = new SyncListenerList<IHintListener>(IHintListener.class);
\r
60 keyListeners.put(key, result);
\r
65 protected synchronized SyncListenerList<IHintListener> getListenerList(Key forKey)
\r
67 return keyListeners.get(forKey);
\r
70 private static Method hintChanged = SyncListenerList.getMethod(IHintListener.class, "hintChanged");
\r
71 private static Method hintRemoved = SyncListenerList.getMethod(IHintListener.class, "hintRemoved");
\r
73 protected void fireKeyChanged(IHintObservable sender, Key key, Object oldValue, Object newValue)
\r
75 createFireKeyChangedRunnable(sender, key, oldValue, newValue).run();
\r
78 protected synchronized Executable[] getFireKeyChangedExecutables(IHintObservable sender, Key key, Object oldValue, Object newValue)
\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
86 protected synchronized Runnable createFireKeyChangedRunnable(final IHintObservable sender, final Key key, final Object oldValue, final Object newValue)
\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
97 ThreadUtils.multiSyncExec(e);
\r
100 final Map<IThreadWorkQueue, IHintListener[]> list1 = l1.getSnapshot();
\r
101 final Map<IThreadWorkQueue, IHintListener[]> list2 = l2.getSnapshot();
\r
102 return new Runnable() {
\r
104 public void run() {
\r
106 for (IHintListener[] ll : list1.values())
\r
107 for (IHintListener l : ll)
\r
108 l.hintChanged(sender, key, oldValue, newValue);
\r
110 for (IHintListener[] ll : list2.values())
\r
111 for (IHintListener l : ll)
\r
112 l.hintChanged(sender, key, oldValue, newValue);
\r
118 protected synchronized void addFireKeyChangedExecutables(Collection<Executable> list, IHintObservable sender, Key key, Object oldValue, Object newValue)
\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
129 protected void fireKeyRemoved(IHintObservable sender, Key key, Object oldValue)
\r
131 createFireKeyRemovedRunnable(sender, key, oldValue).run();
\r
134 protected synchronized Executable[] getFireKeyRemovedExecutables(IHintObservable sender, Key key, Object oldValue)
\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
141 protected synchronized Runnable createFireKeyRemovedRunnable(final IHintObservable sender, final Key key, final Object oldValue)
\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
151 public void run() {
\r
152 ThreadUtils.multiSyncExec(e);
\r
155 final Map<IThreadWorkQueue, IHintListener[]> list1 = l1.getSnapshot();
\r
156 final Map<IThreadWorkQueue, IHintListener[]> list2 = l2.getSnapshot();
\r
157 return new Runnable() {
\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
171 protected synchronized void addFireKeyRemovedExecutables(Collection<Executable> list, IHintObservable sender, Key key, Object oldValue)
\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
182 protected void fireKeyChanged(Key key, Object oldValue, Object newValue)
\r
184 fireKeyChanged(this, key, oldValue, newValue);
\r
187 protected void fireKeyRemoved(Key key, Object oldValue)
\r
189 fireKeyRemoved(this, key, oldValue);
\r
192 public synchronized boolean hasListeners()
\r
194 return !listeners.isEmpty() || !keyListeners.isEmpty();
\r
197 public void addHintListener(IHintListener listener) {
\r
198 listeners.add(listener);
\r
201 public void addKeyHintListener(Key key, IHintListener listener) {
\r
202 getOrCreateKeyListeners(key).add(listener);
\r
205 public void removeHintListener(IHintListener listener) {
\r
206 listeners.remove(listener);
\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
217 * Adds hint listener, which gets events for all hint changes
\r
221 public void addHintListener(IThreadWorkQueue threadAccess, IHintListener listener)
\r
223 listeners.add(threadAccess, listener);
\r
227 * Removes hint listener
\r
231 public void removeHintListener(IThreadWorkQueue threadAccess, IHintListener listener)
\r
233 listeners.remove(threadAccess, listener);
\r
237 * Adds hint listener for a specific key
\r
241 public void addKeyHintListener(IThreadWorkQueue threadAccess, Key key, IHintListener listener)
\r
243 getOrCreateKeyListeners(key).add(threadAccess, listener);
\r
247 * Removes hint listener
\r
251 public void removeKeyHintListener(IThreadWorkQueue threadAccess, Key key, IHintListener listener)
\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