/******************************************************************************* * 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); }}; } }