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