X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.utils.datastructures%2Fsrc%2Forg%2Fsimantics%2Futils%2Fdatastructures%2Fhints%2FHintStack.java;fp=bundles%2Forg.simantics.utils.datastructures%2Fsrc%2Forg%2Fsimantics%2Futils%2Fdatastructures%2Fhints%2FHintStack.java;h=0aad098d7a556c1c091ff3ba8236245b1b702305;hb=969bd23cab98a79ca9101af33334000879fb60c5;hp=0000000000000000000000000000000000000000;hpb=866dba5cd5a3929bbeae85991796acb212338a08;p=simantics%2Fplatform.git 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 index 000000000..0aad098d7 --- /dev/null +++ b/bundles/org.simantics.utils.datastructures/src/org/simantics/utils/datastructures/hints/HintStack.java @@ -0,0 +1,336 @@ +/******************************************************************************* + * 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 stack = + new PriorityStack(IHintContext.class); + + IPriorityStackListener stackListener = + new IPriorityStackListener() { + @Override + public void itemAdded(IPriorityStack sender, IHintContext item) { + item.addHintListener(ctxListener); + ArrayList executables = new ArrayList(); + synchronized(HintStack.this.stack) { + if (!hasListeners()) return; + Map hints = item.getHints(); + if (hints.size()==0) return; + + Set keys = new HashSet(hints.keySet()); + Map oldValues = new HashMap(); + // 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=0; i--) + { + Map 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 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 sender, IHintContext item) { + item.removeHintListener(ctxListener); + ArrayList executables = new ArrayList(); + synchronized(HintStack.this.stack) { + if (!hasListeners()) return; + Map hints = item.getHints(); + if (hints.size()==0) return; + + Set keys = new HashSet(hints.keySet()); + Map overriddenValues = new HashMap(); + // 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=0; i--) + { + Map 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 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=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=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 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 getHints() { + Map result = new HashMap(); + for (IHintContext ctx : stack.toArray()) + result.putAll(ctx.getHints()); + return result; + } + + @Override + public synchronized Map getHintsUnsafe() { + return getHints(); + } + + @Override + public Map getHintsOfClass(Class clazz) { + Map result = new HashMap(); + 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 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 getHint(Key key) { + return (E) HintStack.this.getHint(key); + } + @Override + public Map getHints() { + return HintStack.this.getHints(); + } + @Override + public Map 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 Map getHintsOfClass(Class clazz) { + return HintStack.this.getHintsOfClass(clazz); + } + @Override + public void setHints(Map hints) { + ctx.setHints(hints); + }}; + } + +}