]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - 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
diff --git a/bundles/org.simantics.utils.datastructures/src/org/simantics/utils/datastructures/hints/AbstractHintObservable.java b/bundles/org.simantics.utils.datastructures/src/org/simantics/utils/datastructures/hints/AbstractHintObservable.java
new file mode 100644 (file)
index 0000000..b2f32ac
--- /dev/null
@@ -0,0 +1,259 @@
+/*******************************************************************************\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