rocks.inspectit.ui.rcp.editor.tree.DeferredTreeViewer.java Source code

Java tutorial

Introduction

Here is the source code for rocks.inspectit.ui.rcp.editor.tree.DeferredTreeViewer.java

Source

package rocks.inspectit.ui.rcp.editor.tree;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.commons.lang.ArrayUtils;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.progress.PendingUpdateAdapter;

/**
 * This tree viewer works in conjunction with the
 * {@link org.eclipse.ui.progress.DeferredTreeContentManager} so that the expand function will work.
 * <p>
 * <b>IMPORTANT:</b> The class is licensed under the Eclipse Public License v1.0 as it includes the
 * code from the {@link org.eclipse.jface.viewers.TreeViewer} class belonging to the Eclipse Rich
 * Client Platform. EPL v1.0 license can be found
 * <a href="https://www.eclipse.org/legal/epl-v10.html">here</a>.
 * <p>
 * Please relate to the LICENSEEXCEPTIONS.txt file for more information about license exceptions
 * that apply regarding to InspectIT and Eclipse RCP and/or EPL Components.
 *
 * @author Patrice Bouillet
 *
 */
public class DeferredTreeViewer extends TreeViewer {

    /**
     * Maps the parent widgets to the level so that we know how deep we want to go.
     */
    private Map<Widget, Integer> parentWidgets = Collections.synchronizedMap(new HashMap<Widget, Integer>());

    /**
     * List of the elements that need to be expanded.
     */
    private Set<Object> objectsToBeExpanded = Collections.synchronizedSet(new HashSet<>());

    /**
     * Object to be selected.
     */
    private AtomicReference<Object> objectToSelect = new AtomicReference<>();

    /**
     * Creates a tree viewer on a newly-created tree control under the given parent. The tree
     * control is created using the SWT style bits <code>MULTI, H_SCROLL, V_SCROLL,</code> and
     * <code>BORDER</code>. The viewer has no input, no content provider, a default label provider,
     * no sorter, and no filters.
     *
     * @param parent
     *            the parent control
     */
    public DeferredTreeViewer(Composite parent) {
        super(parent);
    }

    /**
     * Creates a tree viewer on the given tree control. The viewer has no input, no content
     * provider, a default label provider, no sorter, and no filters.
     *
     * @param tree
     *            the tree control
     */
    public DeferredTreeViewer(Tree tree) {
        super(tree);
    }

    /**
     * Creates a tree viewer on a newly-created tree control under the given parent. The tree
     * control is created using the given SWT style bits. The viewer has no input, no content
     * provider, a default label provider, no sorter, and no filters.
     *
     * @param parent
     *            the parent control
     * @param style
     *            the SWT style bits used to create the tree.
     */
    public DeferredTreeViewer(Composite parent, int style) {
        super(parent, style);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void internalAdd(Widget widget, Object parentElement, Object[] childElements) {
        // we have to activate our own filters first, stupid eclipse
        // implementation which has got two different paths of applying filters
        // ...
        ViewerFilter[] filters = getFilters();
        for (ViewerFilter filter : filters) {
            childElements = filter.filter(this, parentElement, childElements);
        }

        super.internalAdd(widget, parentElement, childElements);

        // check if we are currently in the process of expanding the child
        // elements
        if (parentWidgets.containsKey(widget)) {
            // iterate over all child elements
            for (Object object : childElements) {
                // is it expandable
                if (super.isExpandable(object)) {
                    // get the level
                    Integer level = parentWidgets.get(widget);
                    if (level == AbstractTreeViewer.ALL_LEVELS) {
                        super.expandToLevel(object, AbstractTreeViewer.ALL_LEVELS);
                    } else {
                        super.expandToLevel(object, level - 1);
                    }
                }
            }
        }

        if ((objectsToBeExpanded != null) && !objectsToBeExpanded.isEmpty()) {
            // iterate over all child elements
            for (Object object : childElements) {
                // is object in List of objects that need to be expanded?
                if (objectsToBeExpanded.contains(object)) {
                    // then expand it
                    if (!getExpandedState(object)) {
                        super.expandToLevel(object, 1);
                    }
                }
            }
        }

        // if there is object to be selected, we will selected if its parent is expanded
        while (true) {
            Object objToSelect = objectToSelect.get();
            if ((objToSelect != null)
                    && (!isRootElement(objToSelect) || getExpandedState(getParentElement(objToSelect)))) {
                List<Object> selectionList = new ArrayList<>();
                Widget w = internalGetWidgetToSelect(objToSelect);
                if (w != null) {
                    if (objectToSelect.compareAndSet(objToSelect, null)) {
                        selectionList.add(w);
                        setSelection(selectionList);
                        break;
                    }
                } else {
                    break;
                }
            } else {
                break;
            }
        }

    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void internalExpandToLevel(Widget widget, int level) {
        if ((level > 1) || (AbstractTreeViewer.ALL_LEVELS == level)) {
            // we want to open more than one level, have to take care of that.
            Object data = widget.getData();
            if (!(data instanceof PendingUpdateAdapter)) {
                // just care about our own widgets
                parentWidgets.put(widget, Integer.valueOf(level));
            }
        }

        // when the widget is actually expanding, we have to remove its data from the list of object
        // that
        // needs to be expanded, if the data of the widget is found in the list
        Object data = widget.getData();
        if ((data != null) && objectsToBeExpanded.contains(data)) {
            objectsToBeExpanded.remove(data);
        }

        super.internalExpandToLevel(widget, level);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void internalRemove(Object[] elementsOrPaths) {
        // we want to remove the parent of the PendingUpdateAdapter items from
        // our Map
        if (1 == elementsOrPaths.length) {
            Object object = elementsOrPaths[0];
            if (object instanceof PendingUpdateAdapter) {
                Widget[] widgets = findItems(object);
                if ((null != widgets) && (widgets.length > 0)) {
                    Widget widget = widgets[0];
                    Widget parentWidget = getParentItem((Item) widget);
                    if (parentWidgets.containsKey(parentWidget)) {
                        parentWidgets.remove(parentWidget);
                    }
                }
            }
        }

        super.internalRemove(elementsOrPaths);
    }

    /**
     * Expands all ancestors of the given element or tree path so that the given element becomes
     * visible in this viewer's tree control, and then expands the subtree rooted at the given
     * element to the given level. The element will be then selected.
     *
     * @param elementOrTreePath
     *            the element
     * @param level
     *            non-negative level, or <code>ALL_LEVELS</code> to expand all levels of the tree
     */
    public void expandToObjectAndSelect(Object elementOrTreePath, int level) {
        if (checkBusy()) {
            return;
        }
        Object parent = getParentElement(elementOrTreePath);
        // check if the element is already visible, or if it is root
        if (((parent != null) && getExpandedState(parent)) || isRootElement(elementOrTreePath)) {
            // then only set selection
            Widget w = internalGetWidgetToSelect(elementOrTreePath);
            if (null != w) {
                // if widget is already available selected it
                List<Object> selectionList = new ArrayList<>();
                selectionList.add(w);
                setSelection(selectionList);
                // and overwrite any earlier set selection object
                objectToSelect.set(null);
            } else {
                // otherwise set object to selected
                objectToSelect.set(elementOrTreePath);
            }
        } else {
            // get all the objects that need to be expanded so that object is visible
            objectToSelect.set(elementOrTreePath);
            List<Object> objectsToExpand = createObjectList(parent, new ArrayList<>());
            if (!objectsToExpand.isEmpty()) {
                objectsToBeExpanded.addAll(objectsToExpand);
                Widget w = internalExpand(elementOrTreePath, true);
                if (w != null) {
                    internalExpandToLevel(w, level);
                }
            } else {
                // if the list if empty, this means that no object in the tree has to load the
                // children, and they are all expanded, thus we can just select the wanted object
                Widget w = internalGetWidgetToSelect(elementOrTreePath);
                if (null != w) {
                    // if widget is here available
                    List<Object> selectionList = new ArrayList<>();
                    selectionList.add(w);
                    setSelection(selectionList);
                    // and overwrite any earlier set selection object
                    objectToSelect.set(null);
                }
            }
        }
    }

    /**
     * Expands all ancestors of the given element or tree path so that the given element becomes
     * visible in this viewer's tree control and additionally expands the element if it has
     * children.
     *
     * @param elementOrTreePath
     *            the element
     * @param level
     *            non-negative level, or <code>ALL_LEVELS</code> to expand all levels of the tree
     */
    public void expandObject(Object elementOrTreePath, int level) {
        if (checkBusy()) {
            return;
        }
        Object parent = getParentElement(elementOrTreePath);

        // check if the element is already visible, or if it is root
        if ((parent != null) && (!(getExpandedState(parent) || isRootElement(elementOrTreePath)))) {
            // get all the objects that need to be expanded so that object is visible
            List<Object> objectsToExpand = createObjectList(parent, new ArrayList<>());
            if (!objectsToExpand.isEmpty()) {
                objectsToBeExpanded.addAll(objectsToExpand);
            }
        }
        objectsToBeExpanded.add(elementOrTreePath);
        Widget w = internalExpand(elementOrTreePath, true);
        if (w != null) {
            internalExpandToLevel(w, level);
        }
    }

    /**
     * Constructs the list of elements that need to be expanded, so that object supplied can be
     * visible.
     *
     * @param object
     *            Object that expansion should reach.
     * @param objectList
     *            List where the results are stored.
     * @return List of objects for expansion.
     */
    private List<Object> createObjectList(Object object, List<Object> objectList) {
        if (object == null) {
            return objectList;
        }
        if (areFiltersPassed(object) && !getExpandedState(object)) {
            if (childrenLoaded(object)) {
                // if children are loaded for this object we simply expand it directly
                expandToLevel(object, 1);
            } else {
                if (objectList == null) {
                    objectList = new ArrayList<>();
                }
                objectList.add(object);
            }
        }
        Object parent = getParentElement(object);
        if (null != parent) {
            createObjectList(parent, objectList);
        }
        return objectList;
    }

    /**
     * Returns if all the filters are passed for the specific object.
     *
     * @param object
     *            Object to test.
     * @return True if all the filters are passed, and thus object is visible in the tree. False
     *         otherwise.
     */
    private boolean areFiltersPassed(Object object) {
        ViewerFilter[] filters = getFilters();
        if (null != filters) {
            for (ViewerFilter filer : filters) {
                if (!filer.select(this, getParentElement(object), object)) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Tests if the children of the tree item have been loaded.
     *
     * @param object
     *            Object to test.
     * @return True if the children have been fetched.
     */
    private boolean childrenLoaded(Object object) {
        Item[] children = getChildren(doFindItem(object));
        if (null == children) {
            return false;
        }
        for (Item item : children) {
            if (!(item instanceof TreeItem)) {
                return false;
            }
        }
        return true;
    }

    /**
     * Checks if the given element is one of the root object in the input list of the tree viewer.
     *
     * @param element
     *            Element to check.
     * @return True if the element is one of the root objects.
     */
    private boolean isRootElement(Object element) {
        Object input = getRoot();
        Object[] rootElemens = ((ITreeContentProvider) getContentProvider()).getElements(input);
        return ArrayUtils.contains(rootElemens, element);
    }
}