]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.utils.datastructures/src/org/simantics/utils/datastructures/hints/HintStack.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.utils.datastructures / src / org / simantics / utils / datastructures / hints / HintStack.java
diff --git a/bundles/org.simantics.utils.datastructures/src/org/simantics/utils/datastructures/hints/HintStack.java b/bundles/org.simantics.utils.datastructures/src/org/simantics/utils/datastructures/hints/HintStack.java
new file mode 100644 (file)
index 0000000..0aad098
--- /dev/null
@@ -0,0 +1,336 @@
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2015 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
+ *     Semantum Oy - added getHintsUnsafe\r
+ *******************************************************************************/\r
+/*\r
+ *\r
+ * @author Toni Kalajainen\r
+ */\r
+package org.simantics.utils.datastructures.hints;\r
+\r
+import java.util.ArrayList;\r
+import java.util.HashMap;\r
+import java.util.HashSet;\r
+import java.util.Map;\r
+import java.util.Set;\r
+import java.util.Map.Entry;\r
+\r
+import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
+import org.simantics.utils.datastructures.prioritystack.IPriorityStack;\r
+import org.simantics.utils.datastructures.prioritystack.IPriorityStackListener;\r
+import org.simantics.utils.datastructures.prioritystack.PriorityStack;\r
+import org.simantics.utils.threads.Executable;\r
+import org.simantics.utils.threads.IThreadWorkQueue;\r
+import org.simantics.utils.threads.ThreadUtils;\r
+\r
+\r
+public class HintStack extends AbstractHintObservable implements IHintStack {\r
+\r
+       /** Stack of hint contexts */\r
+       PriorityStack<IHintContext> stack = \r
+               new PriorityStack<IHintContext>(IHintContext.class);    \r
+       \r
+       IPriorityStackListener<IHintContext> stackListener =\r
+               new IPriorityStackListener<IHintContext>() {\r
+                       @Override\r
+                       public void itemAdded(IPriorityStack<IHintContext> sender, IHintContext item) {\r
+                               item.addHintListener(ctxListener);\r
+                               ArrayList<Executable> executables = new ArrayList<Executable>();\r
+                               synchronized(HintStack.this.stack) {\r
+                                       if (!hasListeners()) return;\r
+                                       Map<Key, Object> hints = item.getHints();\r
+                                       if (hints.size()==0) return;\r
+                                       \r
+                                       Set<Key> keys = new HashSet<Key>(hints.keySet());\r
+                                       Map<Key, Object> oldValues = new HashMap<Key, Object>();\r
+                                       // Check if there is a change to the stack \r
+                                       IHintContext list[] = stack.toArray();\r
+\r
+                                       int index = stack.indexOf(item);\r
+                                       \r
+                                       // Remove all keys that are overridden by higher priority keys\r
+                                       for (int i=index+1; i<list.length; i++)\r
+                                               keys.removeAll(list[i].getHints().keySet());\r
+                                                                               \r
+                                       // Iterate all lower layers, and see if any key collides\r
+                                       for (int i=index-1; i>=0; i--)\r
+                                       {\r
+                                               Map<Key, Object> lowerLevelHints = list[i].getHints();\r
+                                               lowerLevelHints.keySet().retainAll(keys);\r
+                                               keys.removeAll(lowerLevelHints.keySet());\r
+                                               oldValues.putAll(lowerLevelHints);\r
+                                       }\r
+                                       \r
+                                       // Send events for all new hints\r
+                                       for (Key key : keys)\r
+                                       {\r
+                                               Object newValue         = hints.get(key);\r
+                                               addFireKeyChangedExecutables(executables, HintStack.this, key, null, newValue);\r
+                                       }\r
+                                       \r
+                                       // Send events for all hints that were overridden\r
+                                       for (Entry<Key, Object> hint : oldValues.entrySet())\r
+                                       {\r
+                                               Key key                         = hint.getKey();\r
+                                               Object oldValue         = hint.getValue();\r
+                                               Object newValue         = hints.get(key);\r
+                                               addFireKeyChangedExecutables(executables, HintStack.this, key, oldValue, newValue);\r
+                                       }\r
+                               }\r
+                               ThreadUtils.multiSyncExec(executables);\r
+                       }\r
+                       @Override\r
+                       public void itemRemoved(IPriorityStack<IHintContext> sender, IHintContext item) {\r
+                               item.removeHintListener(ctxListener);\r
+                               ArrayList<Executable> executables = new ArrayList<Executable>();\r
+                               synchronized(HintStack.this.stack) {\r
+                                       if (!hasListeners()) return;\r
+                                       Map<Key, Object> hints = item.getHints();\r
+                                       if (hints.size()==0) return;\r
+                                       \r
+                                       Set<Key> keys = new HashSet<Key>(hints.keySet());\r
+                                       Map<Key, Object> overriddenValues = new HashMap<Key, Object>();\r
+                                       // Check if there is a change to the stack \r
+                                       IHintContext list[] = stack.toArray();\r
+\r
+                                       int index = stack.indexOf(item);\r
+                                       \r
+                                       // Remove all keys that are overridden by higher priority keys\r
+                                       for (int i=index+1; i<list.length; i++)\r
+                                               keys.removeAll(list[i].getHints().keySet());\r
+                                                                               \r
+                                       // Iterate all lower layers, and see if any key collides\r
+                                       for (int i=index-1; i>=0; i--)\r
+                                       {\r
+                                               Map<Key, Object> lowerLevelHints = list[i].getHints();\r
+                                               lowerLevelHints.keySet().retainAll(keys);\r
+                                               keys.removeAll(lowerLevelHints.keySet());\r
+                                               overriddenValues.putAll(lowerLevelHints);\r
+                                       }\r
+                                       \r
+                                       // Send events for all values were removed and were never overridden\r
+                                       for (Key key : keys)\r
+                                       {\r
+                                               Object oldValue         = hints.get(key);\r
+                                               addFireKeyRemovedExecutables(executables, HintStack.this, key, oldValue);\r
+                                       }\r
+                                       \r
+                                       // Send events. overridden hints have become effective\r
+                                       for (Entry<Key, Object> hint : overriddenValues.entrySet())\r
+                                       {\r
+                                               Key key                         = hint.getKey();\r
+                                               Object newValue         = hint.getValue();\r
+                                               Object oldValue         = hints.get(key);\r
+                                               addFireKeyChangedExecutables(executables, HintStack.this, key, oldValue, newValue);\r
+                                       }                                       \r
+                               }\r
+                               ThreadUtils.multiSyncExec(executables);\r
+                       }\r
+       };\r
+       \r
+       IHintListener ctxListener = new IHintListener() {\r
+               @Override\r
+               public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {\r
+                       Runnable notifications;\r
+                       synchronized(HintStack.this.stack) {\r
+                               IHintContext list[] = stack.toArray();\r
+\r
+                               int index = stack.indexOf((IHintContext)sender);\r
+                               if (index<0) return;\r
+                               \r
+                               // Check whether the key is overridden\r
+                               for (int i=index+1; i<list.length; i++)\r
+                                       if (list[i].getHint(key)!=null) return;\r
+                               \r
+                               // Check if this hint overrides another hint\r
+                               if (oldValue==null) {\r
+                                       for (int i=index-1; i>=0; i--)\r
+                                       {\r
+                                               oldValue = list[i].getHint(key);\r
+                                               if (oldValue!=null) break;\r
+                                       }\r
+                               }\r
+                               notifications = createFireKeyChangedRunnable(HintStack.this, key, oldValue, newValue);                          \r
+                       }\r
+                       notifications.run();\r
+               }\r
+               @Override\r
+               public void hintRemoved(IHintObservable sender, Key key, Object oldValue) {\r
+                       Runnable notification;\r
+                       synchronized(HintStack.this.stack) {\r
+                               IHintContext list[] = stack.toArray();\r
+\r
+                               int index = stack.indexOf((IHintContext)sender);\r
+                               if (index<0) return;\r
+                               \r
+                               // Check whether the key was overridden\r
+                               for (int i=index+1; i<list.length; i++)\r
+                                       if (list[i].getHint(key)!=null) return;\r
+                               \r
+                               // Check if this hint overrides another hint\r
+                               Object newValue = null;\r
+                               for (int i=index-1; i>=0; i--)\r
+                               {\r
+                                       newValue = list[i].getHint(key);\r
+                                       if (newValue!=null) break;\r
+                               }\r
+                               if (newValue!=null)\r
+                                       notification = createFireKeyChangedRunnable(HintStack.this, key, oldValue, newValue);                \r
+                               else\r
+                       notification = createFireKeyRemovedRunnable(HintStack.this, key, oldValue);              \r
+                       }\r
+                       notification.run();\r
+               }\r
+       };\r
+       \r
+       public HintStack() {\r
+               stack.addStackListener(stackListener);\r
+       }\r
+       \r
+       @Override\r
+       public void addHintContext(IHintContext hints, int priority) {\r
+               stack.add(hints, priority);\r
+       }\r
+\r
+       @SuppressWarnings("unchecked")\r
+       @Override\r
+       public <E> E getHint(Key key) {\r
+               IHintContext list [] = stack.toArray();\r
+               for (int i=list.length-1; i>=0; i--)\r
+               {\r
+                       IHintContext ctx = list[i];\r
+                       Object value = ctx.getHint(key);\r
+                       if (value!=null) return (E) value;\r
+               }\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       public boolean containsHint(Key key) {\r
+               IHintContext list [] = stack.toArray();\r
+               for (int i=list.length-1; i>=0; i--)\r
+               {\r
+                       IHintContext ctx = list[i];\r
+                       if (ctx.containsHint(key))\r
+                               return true;\r
+               }\r
+               return false;\r
+       }\r
+\r
+       @Override\r
+       public boolean removeHintContext(IHintContext hints) {\r
+               return stack.remove(hints);\r
+       }\r
+\r
+       @Override\r
+       public synchronized Map<Key, Object> getHints() {\r
+               Map<Key, Object> result = new HashMap<Key, Object>();\r
+               for (IHintContext ctx : stack.toArray())\r
+                       result.putAll(ctx.getHints());\r
+               return result;\r
+       }\r
+\r
+       @Override\r
+       public synchronized Map<Key, Object> getHintsUnsafe() {\r
+               return getHints();\r
+       }\r
+\r
+       @Override\r
+       public <E extends Key> Map<E, Object> getHintsOfClass(Class<E> clazz) {\r
+               Map<E, Object> result = new HashMap<E, Object>();\r
+               for (IHintContext ctx : stack.toArray())\r
+                       result.putAll(ctx.getHintsOfClass(clazz));\r
+               return result;\r
+       }\r
+       \r
+       /**\r
+        * Returns a hint context whose read operations originate from the stack, and\r
+        * write operations are performed on a local stack\r
+        * \r
+        * @param ctx the hint context to write into\r
+        * @return write-localized hint context based on this hint stack\r
+        */\r
+       public IHintContext createStackRead(final IHintContext ctx)\r
+       {\r
+               return new IHintContext() {\r
+                       @Override\r
+                       public void clearWithoutNotification() {\r
+                               ctx.clearWithoutNotification();\r
+                       }\r
+                       @SuppressWarnings("unchecked")\r
+                       @Override\r
+                       public <E> E removeHint(Key key) {\r
+                               return (E) ctx.removeHint(key);\r
+                       }\r
+                       @Override\r
+                       public void setHint(Key key, Object value) {\r
+                               ctx.setHint(key, value);\r
+                       }\r
+                       @Override\r
+                       public void addHintListener(IHintListener listener) {\r
+                               HintStack.this.addHintListener(listener);\r
+                       }\r
+                       @Override\r
+                       public void addHintListener(IThreadWorkQueue threadAccess, IHintListener listener) {\r
+                               HintStack.this.addHintListener(threadAccess, listener);\r
+                       }\r
+                       @Override\r
+                       public void addKeyHintListener(Key key, IHintListener listener) {\r
+                               HintStack.this.addKeyHintListener(key, listener);\r
+                       }\r
+                       @Override\r
+                       public void addKeyHintListener(IThreadWorkQueue threadAccess, Key key, IHintListener listener) {\r
+                               HintStack.this.addKeyHintListener(threadAccess, key, listener);\r
+                       }\r
+                       @Override\r
+                       public boolean containsHint(Key key) {\r
+                               return HintStack.this.containsHint(key);\r
+                       }\r
+                       @SuppressWarnings("unchecked")\r
+                       @Override\r
+                       public <E> E getHint(Key key) {\r
+                               return (E) HintStack.this.getHint(key);\r
+                       }\r
+                       @Override\r
+                       public Map<Key, Object> getHints() {\r
+                               return HintStack.this.getHints();\r
+                       }\r
+                       @Override\r
+                       public Map<Key, Object> getHintsUnsafe() {\r
+                               return HintStack.this.getHintsUnsafe();\r
+                       }\r
+                       @Override\r
+                       public void removeHintListener(IHintListener listener) {\r
+                               HintStack.this.removeHintListener(listener);\r
+                       }\r
+                       @Override\r
+                       public void removeHintListener(IThreadWorkQueue threadAccess, IHintListener listener) {\r
+                               HintStack.this.removeHintListener(threadAccess, listener);\r
+                       }\r
+                       @Override\r
+                       public void removeKeyHintListener(Key key, IHintListener listener) {\r
+                               HintStack.this.removeKeyHintListener(key, listener);\r
+                       }\r
+                       @Override\r
+                       public void removeKeyHintListener(IThreadWorkQueue threadAccess, Key key, IHintListener listener) {\r
+                               HintStack.this.removeKeyHintListener(threadAccess, key, listener);\r
+                       }\r
+                       @Override\r
+                       public <E extends Key> Map<E, Object> getHintsOfClass(Class<E> clazz) {\r
+                               return HintStack.this.getHintsOfClass(clazz);\r
+                       }\r
+                       @Override\r
+                       public void setHints(Map<Key, Object> hints) {\r
+                               ctx.setHints(hints);\r
+                       }};\r
+       }\r
+       \r
+}\r