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