-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
- * in Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- * VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-/*\r
- *\r
- * @author Toni Kalajainen\r
- */\r
-package org.simantics.utils.datastructures.hints;\r
-\r
-import gnu.trove.map.hash.THashMap;\r
-\r
-import java.lang.reflect.Method;\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.List;\r
-import java.util.Map;\r
-\r
-import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
-import org.simantics.utils.threads.Executable;\r
-import org.simantics.utils.threads.IThreadWorkQueue;\r
-import org.simantics.utils.threads.SyncListenerList;\r
-import org.simantics.utils.threads.ThreadUtils;\r
-\r
-/**\r
- * TODO Optimize class queries.\r
- * \r
- * @author Toni Kalajainen\r
- */\r
-public abstract class AbstractHintObservable implements IHintObservable {\r
-\r
- /** Global listeners */\r
- protected SyncListenerList<IHintListener> listeners = \r
- new SyncListenerList<IHintListener>(IHintListener.class);\r
- \r
- /** Key specific listeners */\r
- protected Map<Key, SyncListenerList<IHintListener>> keyListeners =\r
- new THashMap<Key, SyncListenerList<IHintListener>>(2); \r
- \r
- private final static SyncListenerList<IHintListener> EMPTY_LIST = \r
- new SyncListenerList<IHintListener>(IHintListener.class);\r
- private final static Runnable NO_ACTION = new Runnable() {\r
- @Override\r
- public void run() {\r
- }};\r
- \r
-\r
- private synchronized SyncListenerList<IHintListener> getOrCreateKeyListeners(Key key)\r
- {\r
- SyncListenerList<IHintListener> result = keyListeners.get(key);\r
- if (result==null) {\r
- result = new SyncListenerList<IHintListener>(IHintListener.class);\r
- keyListeners.put(key, result);\r
- }\r
- return result;\r
- }\r
- \r
- protected synchronized SyncListenerList<IHintListener> getListenerList(Key forKey)\r
- {\r
- return keyListeners.get(forKey);\r
- }\r
- \r
- private static Method hintChanged = SyncListenerList.getMethod(IHintListener.class, "hintChanged");\r
- private static Method hintRemoved = SyncListenerList.getMethod(IHintListener.class, "hintRemoved");\r
-\r
- protected void fireKeyChanged(IHintObservable sender, Key key, Object oldValue, Object newValue)\r
- { \r
- createFireKeyChangedRunnable(sender, key, oldValue, newValue).run();\r
- }\r
- \r
- protected synchronized Executable[] getFireKeyChangedExecutables(IHintObservable sender, Key key, Object oldValue, Object newValue)\r
- {\r
- if (listeners.isEmpty() && keyListeners.isEmpty()) return Executable.EMPTY_ARRAY;\r
- List<Executable> list = new ArrayList<Executable>();\r
- addFireKeyChangedExecutables(list, sender, key, oldValue, newValue);\r
- return list.toArray(new Executable[list.size()]);\r
- }\r
- \r
- protected synchronized Runnable createFireKeyChangedRunnable(final IHintObservable sender, final Key key, final Object oldValue, final Object newValue)\r
- {\r
- SyncListenerList<IHintListener> l1 = listeners;\r
- SyncListenerList<IHintListener> l2 = keyListeners.get(key);\r
- if (l2==null) l2 = EMPTY_LIST;\r
- if (l1.isEmpty() && l2.isEmpty()) return NO_ACTION;\r
- if (!l1.executableInCurrentThread() || !l2.executableInCurrentThread()) {\r
- final Executable e[] = getFireKeyChangedExecutables(sender, key, oldValue, newValue); \r
- return new Runnable() {\r
- @Override\r
- public void run() {\r
- ThreadUtils.multiSyncExec(e);\r
- }};\r
- } else {\r
- final Map<IThreadWorkQueue, IHintListener[]> list1 = l1.getSnapshot(); \r
- final Map<IThreadWorkQueue, IHintListener[]> list2 = l2.getSnapshot();\r
- return new Runnable() {\r
- @Override\r
- public void run() {\r
- if (list1!=null)\r
- for (IHintListener[] ll : list1.values())\r
- for (IHintListener l : ll)\r
- l.hintChanged(sender, key, oldValue, newValue);\r
- if (list2!=null)\r
- for (IHintListener[] ll : list2.values())\r
- for (IHintListener l : ll)\r
- l.hintChanged(sender, key, oldValue, newValue); \r
- }\r
- }; \r
- }\r
- }\r
-\r
- protected synchronized void addFireKeyChangedExecutables(Collection<Executable> list, IHintObservable sender, Key key, Object oldValue, Object newValue)\r
- {\r
- // Add Key specific listeners\r
- Object[] args = {sender, key, oldValue, newValue};\r
- SyncListenerList<IHintListener> keyListeners = getListenerList(key);\r
- if (keyListeners!=null) \r
- keyListeners.addExecutables(list, hintChanged, args);\r
- // Add generic listeners\r
- listeners.addExecutables(list, hintChanged, args);\r
- }\r
- \r
- protected void fireKeyRemoved(IHintObservable sender, Key key, Object oldValue)\r
- { \r
- createFireKeyRemovedRunnable(sender, key, oldValue).run();\r
- }\r
- \r
- protected synchronized Executable[] getFireKeyRemovedExecutables(IHintObservable sender, Key key, Object oldValue)\r
- {\r
- List<Executable> list = new ArrayList<Executable>();\r
- addFireKeyRemovedExecutables(list, sender, key, oldValue);\r
- return list.toArray(new Executable[list.size()]);\r
- }\r
- \r
- protected synchronized Runnable createFireKeyRemovedRunnable(final IHintObservable sender, final Key key, final Object oldValue)\r
- {\r
- SyncListenerList<IHintListener> l1 = listeners;\r
- SyncListenerList<IHintListener> l2 = keyListeners.get(key);\r
- if (l2==null) l2 = EMPTY_LIST;\r
- if (l1.isEmpty() && l2.isEmpty()) return NO_ACTION;\r
- if (!l1.executableInCurrentThread() || !l2.executableInCurrentThread()) {\r
- final Executable e[] = getFireKeyRemovedExecutables(sender, key, oldValue); \r
- return new Runnable() {\r
- @Override\r
- public void run() {\r
- ThreadUtils.multiSyncExec(e);\r
- }};\r
- } else { \r
- final Map<IThreadWorkQueue, IHintListener[]> list1 = l1.getSnapshot(); \r
- final Map<IThreadWorkQueue, IHintListener[]> list2 = l2.getSnapshot();\r
- return new Runnable() {\r
- @Override\r
- public void run() {\r
- for (IHintListener[] ll : list1.values())\r
- for (IHintListener l : ll)\r
- l.hintRemoved(sender, key, oldValue); \r
- for (IHintListener[] ll : list2.values())\r
- for (IHintListener l : ll)\r
- l.hintRemoved(sender, key, oldValue); \r
- }\r
- }; \r
- }\r
- }\r
- \r
- protected synchronized void addFireKeyRemovedExecutables(Collection<Executable> list, IHintObservable sender, Key key, Object oldValue)\r
- {\r
- // Add Key specific listeners\r
- Object[] args = {sender, key, oldValue};\r
- SyncListenerList<IHintListener> keyListeners = getListenerList(key);\r
- if (keyListeners!=null) \r
- keyListeners.addExecutables(list, hintRemoved, args);\r
- // Add generic listeners\r
- listeners.addExecutables(list, hintRemoved, args);\r
- }\r
-\r
- protected void fireKeyChanged(Key key, Object oldValue, Object newValue)\r
- {\r
- fireKeyChanged(this, key, oldValue, newValue);\r
- }\r
- \r
- protected void fireKeyRemoved(Key key, Object oldValue)\r
- {\r
- fireKeyRemoved(this, key, oldValue);\r
- }\r
- \r
- public synchronized boolean hasListeners() \r
- {\r
- return !listeners.isEmpty() || !keyListeners.isEmpty();\r
- }\r
-\r
- public void addHintListener(IHintListener listener) {\r
- listeners.add(listener);\r
- }\r
-\r
- public void addKeyHintListener(Key key, IHintListener listener) {\r
- getOrCreateKeyListeners(key).add(listener);\r
- }\r
-\r
- public void removeHintListener(IHintListener listener) {\r
- listeners.remove(listener);\r
- }\r
-\r
- public synchronized void removeKeyHintListener(Key key, IHintListener listener) {\r
- SyncListenerList<IHintListener> list = keyListeners.get(key);\r
- if (list==null) return;\r
- list.remove(listener);\r
- if (list.isEmpty()) keyListeners.remove(key); \r
- }\r
- \r
- /**\r
- * Adds hint listener, which gets events for all hint changes\r
- * \r
- * @param listener\r
- */\r
- public void addHintListener(IThreadWorkQueue threadAccess, IHintListener listener)\r
- {\r
- listeners.add(threadAccess, listener);\r
- }\r
- \r
- /**\r
- * Removes hint listener\r
- * \r
- * @param listener\r
- */\r
- public void removeHintListener(IThreadWorkQueue threadAccess, IHintListener listener)\r
- {\r
- listeners.remove(threadAccess, listener);\r
- }\r
- \r
- /**\r
- * Adds hint listener for a specific key\r
- * @param key\r
- * @param listener\r
- */\r
- public void addKeyHintListener(IThreadWorkQueue threadAccess, Key key, IHintListener listener)\r
- {\r
- getOrCreateKeyListeners(key).add(threadAccess, listener); \r
- }\r
- \r
- /**\r
- * Removes hint listener\r
- * @param key\r
- * @param listener\r
- */\r
- public void removeKeyHintListener(IThreadWorkQueue threadAccess, Key key, IHintListener listener)\r
- {\r
- SyncListenerList<IHintListener> list = keyListeners.get(key);\r
- if (list==null) return;\r
- list.remove(threadAccess, listener);\r
- if (list.isEmpty()) keyListeners.remove(key); \r
- }\r
- \r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+/*
+ *
+ * @author Toni Kalajainen
+ */
+package org.simantics.utils.datastructures.hints;
+
+import gnu.trove.map.hash.THashMap;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.simantics.utils.datastructures.hints.IHintContext.Key;
+import org.simantics.utils.threads.Executable;
+import org.simantics.utils.threads.IThreadWorkQueue;
+import org.simantics.utils.threads.SyncListenerList;
+import org.simantics.utils.threads.ThreadUtils;
+
+/**
+ * TODO Optimize class queries.
+ *
+ * @author Toni Kalajainen
+ */
+public abstract class AbstractHintObservable implements IHintObservable {
+
+ /** Global listeners */
+ protected SyncListenerList<IHintListener> listeners =
+ new SyncListenerList<IHintListener>(IHintListener.class);
+
+ /** Key specific listeners */
+ protected Map<Key, SyncListenerList<IHintListener>> keyListeners =
+ new THashMap<Key, SyncListenerList<IHintListener>>(2);
+
+ private final static SyncListenerList<IHintListener> EMPTY_LIST =
+ new SyncListenerList<IHintListener>(IHintListener.class);
+ private final static Runnable NO_ACTION = new Runnable() {
+ @Override
+ public void run() {
+ }};
+
+
+ private synchronized SyncListenerList<IHintListener> getOrCreateKeyListeners(Key key)
+ {
+ SyncListenerList<IHintListener> result = keyListeners.get(key);
+ if (result==null) {
+ result = new SyncListenerList<IHintListener>(IHintListener.class);
+ keyListeners.put(key, result);
+ }
+ return result;
+ }
+
+ protected synchronized SyncListenerList<IHintListener> getListenerList(Key forKey)
+ {
+ return keyListeners.get(forKey);
+ }
+
+ private static Method hintChanged = SyncListenerList.getMethod(IHintListener.class, "hintChanged");
+ private static Method hintRemoved = SyncListenerList.getMethod(IHintListener.class, "hintRemoved");
+
+ protected void fireKeyChanged(IHintObservable sender, Key key, Object oldValue, Object newValue)
+ {
+ createFireKeyChangedRunnable(sender, key, oldValue, newValue).run();
+ }
+
+ protected synchronized Executable[] getFireKeyChangedExecutables(IHintObservable sender, Key key, Object oldValue, Object newValue)
+ {
+ if (listeners.isEmpty() && keyListeners.isEmpty()) return Executable.EMPTY_ARRAY;
+ List<Executable> list = new ArrayList<Executable>();
+ addFireKeyChangedExecutables(list, sender, key, oldValue, newValue);
+ return list.toArray(new Executable[list.size()]);
+ }
+
+ protected synchronized Runnable createFireKeyChangedRunnable(final IHintObservable sender, final Key key, final Object oldValue, final Object newValue)
+ {
+ SyncListenerList<IHintListener> l1 = listeners;
+ SyncListenerList<IHintListener> l2 = keyListeners.get(key);
+ if (l2==null) l2 = EMPTY_LIST;
+ if (l1.isEmpty() && l2.isEmpty()) return NO_ACTION;
+ if (!l1.executableInCurrentThread() || !l2.executableInCurrentThread()) {
+ final Executable e[] = getFireKeyChangedExecutables(sender, key, oldValue, newValue);
+ return new Runnable() {
+ @Override
+ public void run() {
+ ThreadUtils.multiSyncExec(e);
+ }};
+ } else {
+ final Map<IThreadWorkQueue, IHintListener[]> list1 = l1.getSnapshot();
+ final Map<IThreadWorkQueue, IHintListener[]> list2 = l2.getSnapshot();
+ return new Runnable() {
+ @Override
+ public void run() {
+ if (list1!=null)
+ for (IHintListener[] ll : list1.values())
+ for (IHintListener l : ll)
+ l.hintChanged(sender, key, oldValue, newValue);
+ if (list2!=null)
+ for (IHintListener[] ll : list2.values())
+ for (IHintListener l : ll)
+ l.hintChanged(sender, key, oldValue, newValue);
+ }
+ };
+ }
+ }
+
+ protected synchronized void addFireKeyChangedExecutables(Collection<Executable> list, IHintObservable sender, Key key, Object oldValue, Object newValue)
+ {
+ // Add Key specific listeners
+ Object[] args = {sender, key, oldValue, newValue};
+ SyncListenerList<IHintListener> keyListeners = getListenerList(key);
+ if (keyListeners!=null)
+ keyListeners.addExecutables(list, hintChanged, args);
+ // Add generic listeners
+ listeners.addExecutables(list, hintChanged, args);
+ }
+
+ protected void fireKeyRemoved(IHintObservable sender, Key key, Object oldValue)
+ {
+ createFireKeyRemovedRunnable(sender, key, oldValue).run();
+ }
+
+ protected synchronized Executable[] getFireKeyRemovedExecutables(IHintObservable sender, Key key, Object oldValue)
+ {
+ List<Executable> list = new ArrayList<Executable>();
+ addFireKeyRemovedExecutables(list, sender, key, oldValue);
+ return list.toArray(new Executable[list.size()]);
+ }
+
+ protected synchronized Runnable createFireKeyRemovedRunnable(final IHintObservable sender, final Key key, final Object oldValue)
+ {
+ SyncListenerList<IHintListener> l1 = listeners;
+ SyncListenerList<IHintListener> l2 = keyListeners.get(key);
+ if (l2==null) l2 = EMPTY_LIST;
+ if (l1.isEmpty() && l2.isEmpty()) return NO_ACTION;
+ if (!l1.executableInCurrentThread() || !l2.executableInCurrentThread()) {
+ final Executable e[] = getFireKeyRemovedExecutables(sender, key, oldValue);
+ return new Runnable() {
+ @Override
+ public void run() {
+ ThreadUtils.multiSyncExec(e);
+ }};
+ } else {
+ final Map<IThreadWorkQueue, IHintListener[]> list1 = l1.getSnapshot();
+ final Map<IThreadWorkQueue, IHintListener[]> list2 = l2.getSnapshot();
+ return new Runnable() {
+ @Override
+ public void run() {
+ for (IHintListener[] ll : list1.values())
+ for (IHintListener l : ll)
+ l.hintRemoved(sender, key, oldValue);
+ for (IHintListener[] ll : list2.values())
+ for (IHintListener l : ll)
+ l.hintRemoved(sender, key, oldValue);
+ }
+ };
+ }
+ }
+
+ protected synchronized void addFireKeyRemovedExecutables(Collection<Executable> list, IHintObservable sender, Key key, Object oldValue)
+ {
+ // Add Key specific listeners
+ Object[] args = {sender, key, oldValue};
+ SyncListenerList<IHintListener> keyListeners = getListenerList(key);
+ if (keyListeners!=null)
+ keyListeners.addExecutables(list, hintRemoved, args);
+ // Add generic listeners
+ listeners.addExecutables(list, hintRemoved, args);
+ }
+
+ protected void fireKeyChanged(Key key, Object oldValue, Object newValue)
+ {
+ fireKeyChanged(this, key, oldValue, newValue);
+ }
+
+ protected void fireKeyRemoved(Key key, Object oldValue)
+ {
+ fireKeyRemoved(this, key, oldValue);
+ }
+
+ public synchronized boolean hasListeners()
+ {
+ return !listeners.isEmpty() || !keyListeners.isEmpty();
+ }
+
+ public void addHintListener(IHintListener listener) {
+ listeners.add(listener);
+ }
+
+ public void addKeyHintListener(Key key, IHintListener listener) {
+ getOrCreateKeyListeners(key).add(listener);
+ }
+
+ public void removeHintListener(IHintListener listener) {
+ listeners.remove(listener);
+ }
+
+ public synchronized void removeKeyHintListener(Key key, IHintListener listener) {
+ SyncListenerList<IHintListener> list = keyListeners.get(key);
+ if (list==null) return;
+ list.remove(listener);
+ if (list.isEmpty()) keyListeners.remove(key);
+ }
+
+ /**
+ * Adds hint listener, which gets events for all hint changes
+ *
+ * @param listener
+ */
+ public void addHintListener(IThreadWorkQueue threadAccess, IHintListener listener)
+ {
+ listeners.add(threadAccess, listener);
+ }
+
+ /**
+ * Removes hint listener
+ *
+ * @param listener
+ */
+ public void removeHintListener(IThreadWorkQueue threadAccess, IHintListener listener)
+ {
+ listeners.remove(threadAccess, listener);
+ }
+
+ /**
+ * Adds hint listener for a specific key
+ * @param key
+ * @param listener
+ */
+ public void addKeyHintListener(IThreadWorkQueue threadAccess, Key key, IHintListener listener)
+ {
+ getOrCreateKeyListeners(key).add(threadAccess, listener);
+ }
+
+ /**
+ * Removes hint listener
+ * @param key
+ * @param listener
+ */
+ public void removeKeyHintListener(IThreadWorkQueue threadAccess, Key key, IHintListener listener)
+ {
+ SyncListenerList<IHintListener> list = keyListeners.get(key);
+ if (list==null) return;
+ list.remove(threadAccess, listener);
+ if (list.isEmpty()) keyListeners.remove(key);
+ }
+
+}