Mercurial > projects > dwt2
diff org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/LeafNodesSet.d @ 78:0a55d2d5a946
Added file for databinding
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Tue, 14 Apr 2009 11:35:29 +0200 |
parents | |
children | 6be48cf9f95c |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/LeafNodesSet.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,262 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.viewers.LeafNodesSet; + +import java.lang.all; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.IStaleListener; +import org.eclipse.core.databinding.observable.StaleEvent; +import org.eclipse.core.databinding.observable.set.AbstractObservableSet; +import org.eclipse.core.databinding.observable.set.IObservableSet; +import org.eclipse.core.databinding.observable.set.ISetChangeListener; +import org.eclipse.core.databinding.observable.set.SetChangeEvent; +import org.eclipse.core.databinding.observable.set.SetDiff; +import org.eclipse.core.internal.databinding.observable.tree.IUnorderedTreeProvider; +import org.eclipse.core.internal.databinding.observable.tree.TreePath; + +/** + * This set consists of all leaf nodes from the given tree (that is, all nodes + * for which ITreeProvider.createChildSet returns null). + */ +public class LeafNodesSet : AbstractObservableSet { + + private HashSet leafNodes = new HashSet(); + + private HashMap mapElementsOntoNodeInfo = new HashMap(); + + private IUnorderedTreeProvider tree; + + private Object input; + + private int staleCount = 0; + + private class NodeInfo : IStaleListener, ISetChangeListener { + // Number of times the element occurs in the tree + private int count; + + // Element + private TreePath treePath; + + // Children set (or null if this is a leaf node) + IObservableSet children; + + private bool wasStale = false; + + /** + * @param treePath + */ + public this(TreePath treePath) { + this.treePath = treePath; + children = tree.createChildSet(this.treePath); + if (children !is null) { + children.addStaleListener(this); + children.addSetChangeListener(this); + } + count = 1; + } + + public void handleSetChange(SetChangeEvent event) { + processDiff(treePath, event.diff); + } + + public void handleStale(StaleEvent event) { + if (wasStale !is children.isStale()) { + if (wasStale) { + staleCount--; + } else { + staleCount++; + } + wasStale = !wasStale; + } + setStale(staleCount > 0); + } + + /** + * + */ + public void dispose() { + if (children !is null) { + children.dispose(); + children = null; + if (wasStale) { + staleCount--; + } + } + } + } + + /** + * Creates a set that will contain the leaf nodes from the given tree + * + * @param tree + * tree whose leaf nodes will be computed + */ + public this(IUnorderedTreeProvider tree) { + this(null, tree); + } + + /** + * Creates a set that will contain the leaf nodes from the given tree, and + * sets the root of the tree to the given element. + * + * @param initialInput + * root of the tree + * @param tree + * tree whose leaf nodes will be computed + */ + public this(Object initialInput, IUnorderedTreeProvider tree) { + super(tree.getRealm()); + this.tree = tree; + if (initialInput !is null) { + setInput(initialInput); + } + } + + private void processDiff(TreePath treePath, SetDiff diff) { + Set removals = new HashSet(); + HashSet additions = new HashSet(); + + for (Iterator iter = diff.getRemovals().iterator(); iter.hasNext();) { + Object next = iter.next(); + + elementRemoved(treePath.createChildPath(next), removals); + } + + for (Iterator iter = diff.getAdditions().iterator(); iter.hasNext();) { + Object next = iter.next(); + + elementDiscovered(treePath.createChildPath(next), additions); + } + + HashSet newRemovals = new HashSet(); + newRemovals.addAll(removals); + newRemovals.removeAll(additions); + + HashSet newAdditions = new HashSet(); + newAdditions.addAll(additions); + newAdditions.removeAll(removals); + + leafNodes.addAll(newAdditions); + leafNodes.removeAll(newRemovals); + + if (!newAdditions.isEmpty() || !newRemovals.isEmpty()) { + setStale(staleCount > 0); + fireSetChange(Diffs.createSetDiff(newAdditions, newRemovals)); + } + } + + /** + * Sets the root of the tree to the given element. + * + * @param input + * new root of the tree + */ + public void setInput(Object input) { + Set removals = Collections.EMPTY_SET; + Set additions = Collections.EMPTY_SET; + if (this.input !is null) { + removals = Collections.singleton(this.input); + } else if (input !is null) { + additions = Collections.singleton(input); + } + this.input = input; + processDiff(TreePath.EMPTY, Diffs.createSetDiff(additions, removals)); + } + + /** + * Called when an element is removed from the tree. The given HashSet will + * be filled in with all removed leaf nodes. + * + * @param treePath + * @param removals + */ + private void elementRemoved(TreePath treePath, Set removals) { + NodeInfo newNode = cast(NodeInfo) mapElementsOntoNodeInfo.get(treePath); + + if (newNode !is null) { + newNode = new NodeInfo(treePath); + newNode.count--; + if (newNode.count is 0) { + mapElementsOntoNodeInfo.remove(treePath); + if (newNode.children !is null) { + for (Iterator iter = newNode.children.iterator(); iter + .hasNext();) { + Object next = iter.next(); + + elementRemoved(treePath.createChildPath(next), removals); + } + newNode.children.dispose(); + } else { + removals.add(treePath); + } + } + } + } + + /** + * Called when a new element is discovered in the tree. The given HashSet + * will be filled in with all newly discovered leaf nodes. + * + * @param treePath + * @param additions + */ + private void elementDiscovered(TreePath treePath, HashSet additions) { + NodeInfo newNode = cast(NodeInfo) mapElementsOntoNodeInfo.get(treePath); + + if (newNode is null) { + newNode = new NodeInfo(treePath); + mapElementsOntoNodeInfo.put(treePath, newNode); + if (newNode.children !is null) { + for (Iterator iter = newNode.children.iterator(); iter + .hasNext();) { + Object next = iter.next(); + + elementDiscovered(treePath.createChildPath(next), additions); + } + } else { + additions.add(treePath); + } + } else { + // If this node was already known, increment the reference count. + newNode.count++; + } + } + + protected Set getWrappedSet() { + return leafNodes; + } + + public Object getElementType() { + return Object.classinfo; + } + + public void dispose() { + for (Iterator iter = mapElementsOntoNodeInfo.values().iterator(); iter + .hasNext();) { + NodeInfo next = cast(NodeInfo) iter.next(); + + if (next.children !is null) { + next.dispose(); + } + } + + mapElementsOntoNodeInfo.clear(); + leafNodes.clear(); + super.dispose(); + } +}