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();
+    }
+}