Mercurial > projects > dwt2
diff org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/viewers/UnorderedTreeContentProvider.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/provisional/viewers/UnorderedTreeContentProvider.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,533 @@ +/******************************************************************************* + * 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 + * Stefan Xenos, IBM - initial API and implementation + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.provisional.viewers.UnorderedTreeContentProvider; + +import java.lang.all; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.set.AbstractObservableSet; +import org.eclipse.core.databinding.observable.set.IObservableSet; +import org.eclipse.core.databinding.observable.set.SetDiff; +import org.eclipse.core.internal.databinding.observable.tree.IUnorderedTreeProvider; +import org.eclipse.jface.databinding.viewers.ObservableListTreeContentProvider; +import org.eclipse.jface.databinding.viewers.ObservableSetTreeContentProvider; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.ITreePathContentProvider; +import org.eclipse.jface.viewers.ITreeViewerListener; +import org.eclipse.jface.viewers.TreeExpansionEvent; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.Viewer; + +/** + * NON-API - Generic tree content provider to be used with an AbstractTreeViewer based on a IUnorderedTreeProvider. + * @since 1.1 + * @deprecated Use {@link ObservableSetTreeContentProvider} or + * {@link ObservableListTreeContentProvider} instead. + */ +public class UnorderedTreeContentProvider : ITreeContentProvider, ITreePathContentProvider { + + private HashMap mapElementToTreeNode = new HashMap(); + private LinkedList enqueuedPrefetches = new LinkedList(); + private IParentProvider rootParentProvider = null; + private bool useTreePaths = false; + + class KnownElementsSet : AbstractObservableSet { + + protected this() { + super(); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.internal.databinding.provisional.observable.set.AbstractObservableSet#getWrappedSet() + */ + protected Set getWrappedSet() { + return mapElementToTreeNode.keySet(); + } + + void doFireDiff(Set added, Set removed) { + fireSetChange(Diffs.createSetDiff(added, removed)); + } + + public void fireSetChange(SetDiff diff) { + super.fireSetChange(diff); + } + + void doFireStale(bool isStale) { + if (isStale) { + fireStale(); + } else { + fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET, Collections.EMPTY_SET)); + } + } + + /* (non-Javadoc) + * @see org.eclipse.jface.internal.databinding.provisional.observable.set.IObservableSet#getElementType() + */ + public Object getElementType() { + return new Object(); + } + } + + KnownElementsSet elements = new KnownElementsSet(); + + private ITreeViewerListener expandListener = new class() ITreeViewerListener { + public void treeCollapsed(TreeExpansionEvent event) { + } + + public void treeExpanded(TreeExpansionEvent event) { + } + }; + + private IUnorderedTreeProvider provider; + private Object pendingNode; + + private int avoidViewerUpdates; + + private TreeViewer treeViewer; + + private int staleCount = 0; + private bool useRefresh; + private int maxPrefetches = 0; + + /** + * Constructs a content provider that will render the given tree in a TreeViewer. + * + * @param provider IObservableTree that provides the contents of the tree + * @param pendingNode element to insert whenever a node is being fetched in the background + * @param useRefresh true = notify the viewer of changes by calling refresh(...), false = + * notify the viewer of changes by calling add(...) and remove(...). Using false + * is more efficient, but may not work with TreeViewer subclasses. + */ + public this(IUnorderedTreeProvider provider, + Object pendingNode, bool useRefresh) { + this.provider = provider; + this.pendingNode = pendingNode; + this.useRefresh = useRefresh; + } + + /** + * Sets whether this content provider should add/remove elements using + * TreePaths (true) or elements (false). + * + * <p></p> + * <p>When using elements:</p> + * + * <ul> + * <li>Cycles are permitted (elements can be their own ancestor)</li> + * <li>Addition, removal, and refresh are slightly faster</li> + * <li>It is not possible to have more than one content provider per tree</li> + * <li>The setRootPath(...) method is ignored</li> + * </ul> + * + * <p></p> + * <p>When using TreePaths:</p> + * + * <ul> + * <li>Cycles are not permitted (elements cannot be their own parent)</li> + * <li>Addition, removal, and refresh are slightly slower</li> + * <li>It is possible to use more than one content provider in the same tree</li> + * <li>The setRootPath(...) method can be used to direct the output to a particular + * subtree</li> + * </ul> + * + * @param usePaths + */ + public void useTreePaths(bool usePaths) { + this.useTreePaths = usePaths; + } + + /** + * @param rootParentProvider + */ + public void setRootPath(IParentProvider rootParentProvider) { + this.rootParentProvider = rootParentProvider; + } + + /** + * @param maxPrefetches + */ + public void setMaxPrefetches(int maxPrefetches) { + this.maxPrefetches = maxPrefetches; + } + + /* package */ IObservableSet createChildSet(Object element) { + return provider.createChildSet(element); + } + + /* package */ void remove(Object element, Set removals, bool lastElement) { + if (removals.isEmpty()) { + return; + } + if (avoidViewerUpdates is 0) { + if (lastElement || useRefresh) { + doRefresh(element); + } else { + if (useTreePaths) { + List toRemove = new ArrayList(); + TreePath[] parents = getParents(element); + for (int i = 0; i < parents.length; i++) { + TreePath parent = parents[i]; + + for (Iterator iter = removals.iterator(); iter.hasNext();) { + Object elementToRemove = iter.next(); + + toRemove.add(parent.createChildPath(element).createChildPath(elementToRemove)); + } + } + + treeViewer.remove(toRemove.toArray(new TreePath[toRemove.size()])); + } else { + treeViewer.remove(element, removals.toArray()); + } + } + for (Iterator iter = removals.iterator(); iter.hasNext();) { + Object next = iter.next(); + + TreeNode nextNode = cast(TreeNode)mapElementToTreeNode.get(next); + if (nextNode !is null) { + nextNode.removeParent(element); + removeIfUnused(nextNode); + } + } + } + } + + /* package */ void add(Object element, Set additions) { + if (additions.isEmpty()) { + return; + } + if (avoidViewerUpdates is 0) { + // Handle new parents + addParent(element, additions); + if (useRefresh) { + doRefresh(element); + } else { + if (useTreePaths) { + TreePath[] parents = getParents(element); + for (int i = 0; i < parents.length; i++) { + TreePath parent = parents[i]; + + treeViewer.add(parent.createChildPath(element), additions.toArray()); + } + } else { + treeViewer.add(element, additions.toArray()); + } + } + } + } + + private void doRefresh(Object element) { + treeViewer.refresh(element); + } + + /** + * Ensures that the given set of children have the given parent as + * one of their parents. + * + * @param parent + * @param children + */ + private void addParent(Object parent, Set children) { + for (Iterator iter = children.iterator(); iter.hasNext();) { + Object next = iter.next(); + + TreeNode nextNode = getNode(next); + nextNode.addParent(parent); + } + } + + /** + * @return saouesnth + */ + public final Object getPendingNode() { + return pendingNode; + } + + /** + * @param parent + * @return aueosnht + */ + public IObservableSet getChildrenSet(Object parent) { + IObservableSet result = getNode(parent).getChildrenSet(); + + return result; + } + + public void dispose() { + if (treeViewer !is null) { + try { + avoidViewerUpdates++; + enqueuedPrefetches.clear(); + Object[] keys = mapElementToTreeNode.keySet().toArray(); + + for (int i = 0; i < keys.length; i++) { + Object key = keys[i]; + + TreeNode result = cast(TreeNode)mapElementToTreeNode.get(key); + if (result !is null) { + result.dispose(); + } + } + setViewer(null); + } finally { + avoidViewerUpdates--; + } + } + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // This should only ever be called for a single viewer + setViewer(viewer); + + if (oldInput !is null && newInput !is null && oldInput.equals(newInput)) { + return; + } + + try { + avoidViewerUpdates++; + TreeNode oldNode = cast(TreeNode)mapElementToTreeNode.get(oldInput); + if (oldNode !is null) { + removeIfUnused(oldNode); + } + } finally { + avoidViewerUpdates--; + } + } + + private void removeIfUnused(TreeNode toRemove) { + //TreeNode result = cast(TreeNode)mapElementToTreeNode.get(element); + Object element = toRemove.getElement(); + if (toRemove.getParent() is null) { + mapElementToTreeNode.remove(element); + elements.doFireDiff(Collections.EMPTY_SET, Collections.singleton(element)); + toRemove.dispose(); + } + } + + private void setViewer(Viewer viewer) { + if (viewer !is null && !(null !is cast(TreeViewer)viewer)) { + throw new IllegalArgumentException("This content provider can only be used with TreeViewers"); //$NON-NLS-1$ + } + TreeViewer newTreeViewer = cast(TreeViewer) viewer; + + if (newTreeViewer !is treeViewer) { + if (treeViewer !is null) { + treeViewer.removeTreeListener(expandListener); + } + + this.treeViewer = newTreeViewer; + if (newTreeViewer !is null) { + newTreeViewer.addTreeListener(expandListener); + } + } + } + + public Object[] getChildren(Object parentElement) { + Set result = getNode(parentElement).getChildren(); + + addParent(parentElement, result); + + return result.toArray(); + } + + private TreeNode getNode(Object parentElement) { + TreeNode result = cast(TreeNode)mapElementToTreeNode.get(parentElement); + if (result is null) { + result = new TreeNode(parentElement, this); + mapElementToTreeNode.put(parentElement, result); + elements.fireSetChange(Diffs.createSetDiff(Collections.singleton(parentElement), + Collections.EMPTY_SET)); + } + return result; + } + + public Object getParent(Object element) { + Object result = getNode(element).getParent(); + if (result is null && rootParentProvider !is null) { + result = rootParentProvider.getParent(element); + } + return result; + } + + public bool hasChildren(Object element) { + return getNode(element).shouldShowPlus(); + } + + public Object[] getElements(Object inputElement) { + return getChildren(inputElement); + } + + /** + * @return aouesnth + */ + public IObservableSet getKnownElements() { + return elements; + } + + /* package */ void changeStale(int staleDelta) { + staleCount += staleDelta; + processPrefetches(); + elements.setStale(staleCount !is 0); + } + + /** + * @return aoueesnth + */ + public TreeViewer getViewer() { + return treeViewer; + } + + /** + * @param element + * @return aoeusnth + */ + public bool isDirty(Object element) { + return false; + } + + /* package */ void enqueuePrefetch(TreeNode node) { + if (maxPrefetches > 0 || maxPrefetches is -1) { + if (staleCount is 0) { + // Call node.getChildren()... this will cause us to start listening to the + // node and will trigger prefetching. Don't call prefetch since this method + // is intended to be called inside getters (which will simply return the + // fetched nodes) and prefetch() is intended to be called inside an asyncExec, + // which will notify the viewer directly of the newly discovered nodes. + node.getChildren(); + } else { + enqueuedPrefetches.add(node); + while (maxPrefetches >= 0 && enqueuedPrefetches.size() > maxPrefetches) { + enqueuedPrefetches.removeFirst(); + } + } + } + } + + private void processPrefetches() { + while (staleCount is 0 && !enqueuedPrefetches.isEmpty()) { + TreeNode next = cast(TreeNode)enqueuedPrefetches.removeLast(); + + // Note that we don't remove nodes from the prefetch queue when they are disposed, + // so we may encounter disposed nodes at this time. + if (!next.isDisposed()) { + next.prefetch(); + } + } + } + + public Object[] getChildren(TreePath parentPath) { + return getChildren(parentPath.getLastSegment()); + } + + public TreePath[] getParents(Object element) { + // Compute all paths that do not contain cycles + /** + * List of Lists + */ + List parentPaths = computeParents(element, new HashSet()); + + /** + * List of TreePath + */ + List result = new ArrayList(); + + for (Iterator iterator = parentPaths.iterator(); iterator.hasNext();) { + List nextPath = cast(List) iterator.next(); + + LinkedList resultPath = new LinkedList(); + resultPath.addAll(nextPath); + Object nextParent = resultPath.isEmpty() ? element : resultPath.getFirst(); + for(;nextParent !is null;) { + if (rootParentProvider !is null) { + nextParent = rootParentProvider.getParent(nextParent); + if (nextParent !is null) { + resultPath.addFirst(nextParent); + } + } else { + nextParent = null; + } + } + + result.add(new TreePath(resultPath.toArray())); + } + + if (result.isEmpty() && rootParentProvider !is null) { + Object nextParent = rootParentProvider.getParent(element); + if (nextParent !is null) { + LinkedList resultPath = new LinkedList(); + while (nextParent !is null) { + resultPath.addFirst(nextParent); + nextParent = rootParentProvider.getParent(nextParent); + } + + result.add(new TreePath(resultPath.toArray())); + } + + } + + return cast(TreePath[]) result.toArray(new TreePath[result.size()]); + } + + /** + * + * @param node + * @param toIgnore + * @return a list of Lists, indicating all known paths to the given node + */ + private List computeParents(Object node, HashSet toIgnore) { + List result = new ArrayList(); + bool containedNode = toIgnore.add(node); + + TreeNode tn = getNode(node); + + HashSet parents = new HashSet(); + parents.addAll(tn.getParents()); + parents.removeAll(toIgnore); + if (parents.isEmpty()) { + ArrayList newPath = new ArrayList(); + result.add(newPath); + } else { + for (Iterator iterator = parents.iterator(); iterator.hasNext();) { + Object parent = iterator.next(); + + List parentPaths = computeParents(parent, toIgnore); + + for (Iterator iterator2 = parentPaths.iterator(); iterator2 + .hasNext();) { + List parentPath = cast(List) iterator2.next(); + + parentPath.add(parent); + result.add(parentPath); + } + } + } + + if (containedNode) { + toIgnore.remove(node); + } + return result; + } + + public bool hasChildren(TreePath path) { + return hasChildren(path.getLastSegment()); + } +}