view 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 source

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