/*******************************************************************************
* Copyright (c) 2005, 2012 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
*******************************************************************************/
package org.eclipse.bpel.ui.details.providers;
import java.util.HashMap;
import java.util.Vector;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.Viewer;
/**
* An abstract content provider that caches the tree nodes as they are discovered.
*
* This is useful for two reasons: it guarantees that the same object will be returned
* until the cache is cleared (which is convenient for trees that wrap a different set
* of model objects), and it handles the parent relationship automatically.
*
* Note that the parent of a node will only be known if the node appeared in an array
* previously returned by primGetChildren()! The value UNKNOWN_PARENT is returned
* as the parent of a node whose parent has never had primGetChildren() called on it.
* null is returned as the parent of the root nodes, that is, those returned by
* primGetElements().
*/
public abstract class CachedTreeContentProvider implements ITreeContentProvider {
protected static final Object[] EMPTY_ARRAY = new Object[0];
public static final Object UNKNOWN_PARENT = new Object();
// A null value in the treeNodeToParent map means the node is a root element.
protected HashMap treeNodeToParent;
// A null value in the treeNodeToChildren map means the node has children, but
// we don't yet know what they are. (If it has no children, an empty array would
// be present instead).
protected HashMap treeNodeToChildren;
Object[] rootChildren;
public CachedTreeContentProvider() {
super();
treeNodeToParent = new HashMap();
treeNodeToChildren = new HashMap();
}
protected abstract boolean primHasChildren(Object node);
protected abstract Object[] primGetChildren(Object node);
protected abstract Object[] primGetElements(Object node);
/**
* Returns an array describing the path from a node up to the root. The node will
* be the first element of the array, its parent will be the 2nd, and so on, with
* a top-level node (i.e. a root) as the last element of the array. Note that the
* input object of the content provider is *NOT* included in this path, because the
* top-level nodes returned by getElements() have a parent value of null.
*/
public Object[] getPathToRoot(Object node) {
Vector v = new Vector();
while (node != null && node != UNKNOWN_PARENT) {
v.add(node);
node = getParent(node);
}
return v.toArray();
}
/* IStructuredContentProvider */
public final Object[] getElements(Object node) {
if (rootChildren == null) {
rootChildren = primGetElements(node);
if (rootChildren == null) rootChildren = EMPTY_ARRAY;
for (int i = 0; i<rootChildren.length; i++) {
treeNodeToParent.put(rootChildren[i], null);
}
}
return rootChildren;
}
/* ITreeContentProvider */
public final boolean hasChildren(Object node) {
Object[] result = (Object[])treeNodeToChildren.get(node);
if (result != null) return (result.length > 0);
if (treeNodeToChildren.containsKey(node)) {
// it has children, we just haven't seen them yet.
return true;
}
if (primHasChildren(node)) {
treeNodeToChildren.put(node, null);
return true;
}
treeNodeToChildren.put(node, EMPTY_ARRAY);
return false;
}
public final Object[] getChildren(Object node) {
Object[] result = (Object[])treeNodeToChildren.get(node);
if (result == null) {
result = primGetChildren(node);
if (result == null) result = EMPTY_ARRAY;
treeNodeToChildren.put(node, result);
for (int i = 0; i<result.length; i++) {
treeNodeToParent.put(result[i], node);
}
}
return result;
}
public final Object getParent(Object node) {
// Prevent stack overflow in cases where a caller is walking up the
// parent chain of a stale object and doesn't recognize UNKNOWN_PARENT.
// (e.g. in a StructuredViewer, internalExpand() will keep asking for
// the parent of UNKNOWN_PARENT until stack overflow).
if (node==UNKNOWN_PARENT) return null;
Object result = treeNodeToParent.get(node);
if (result != null || treeNodeToParent.containsKey(node)) return result;
return UNKNOWN_PARENT;
}
/* IContentProvider */
public void dispose() {
}
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
rootChildren = null;
treeNodeToParent.clear();
treeNodeToChildren.clear();
}
}