org.eclipse.emf.ecp.internal.ui.model.TreeContentProvider.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.emf.ecp.internal.ui.model.TreeContentProvider.java

Source

/********************************************************************************
 * Copyright (c) 2011 Eike Stepper (Berlin, Germany) 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:
 * Eike Stepper - initial API and implementation
 ********************************************************************************/
package org.eclipse.emf.ecp.internal.ui.model;

import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecp.core.ECPProject;
import org.eclipse.emf.ecp.core.util.ECPUtil;
import org.eclipse.emf.ecp.internal.core.util.ChildrenListImpl;
import org.eclipse.emf.ecp.internal.ui.Activator;
import org.eclipse.emf.ecp.internal.ui.Messages;
import org.eclipse.emf.ecp.spi.core.InternalProvider;
import org.eclipse.emf.ecp.spi.core.util.InternalChildrenList;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;

/**
 * @author Eike Stepper
 * @param <INPUT> The type of input (root of the tree)
 */
public abstract class TreeContentProvider<INPUT> extends StructuredContentProvider<INPUT>
        implements ITreeContentProvider {
    private static final Object[] NO_CHILDREN = new Object[0];

    private final Map<Object, Object> parentsCache = new WeakHashMap<Object, Object>();

    private final Map<Object, InternalChildrenList> slowLists = new HashMap<Object, InternalChildrenList>();

    @Override
    public TreeViewer getViewer() {
        return (TreeViewer) super.getViewer();
    }

    /** {@inheritDoc} */
    @Override
    public final Object[] getElements(Object parent) {
        return getChildren(parent);
    }

    /** {@inheritDoc} */
    @Override
    public final boolean hasChildren(Object parent) {
        if (parent instanceof SyntheticElement || ECPUtil.isDisposed(parent) || ECPUtil.isClosed(parent)) {
            return false;
        }

        final InternalChildrenList childrenList = getChildrenList(parent);
        synchronized (childrenList) {
            if (!childrenList.isComplete()) {
                return true;
            }

            return childrenList.hasChildren();
        }
    }

    /** {@inheritDoc} */
    @Override
    public final Object[] getChildren(Object parent) {
        if (parent instanceof SyntheticElement || ECPUtil.isDisposed(parent) || ECPUtil.isClosed(parent)) {
            return NO_CHILDREN;
        }

        Object[] result;
        boolean complete;

        final InternalChildrenList childrenList = getChildrenList(parent);
        synchronized (childrenList) {
            result = childrenList.getChildren();
            complete = childrenList.isComplete();
        }

        for (int i = 0; i < result.length; i++) {
            final Object child = result[i];
            parentsCache.put(child, parent);
        }

        if (!complete) {
            final Object[] withPending = new Object[result.length + 1];
            System.arraycopy(result, 0, withPending, 0, result.length);
            withPending[result.length] = new SlowElement(parent);
            result = withPending;
        }

        return result;
    }

    /** {@inheritDoc} */
    @Override
    public final Object getParent(Object child) {
        if (child instanceof SyntheticElement) {
            return ((SyntheticElement) child).getParent();
        }

        Object result = parentsCache.get(child);
        if (result == null && EObject.class.isInstance(child)) {
            final EObject childEObject = (EObject) child;
            result = childEObject.eContainer();
            if (result != null && parentsCache.containsKey(result)) {
                return result;

            }
        }
        return result;
    }

    public final void refreshViewer(final boolean isStructuralChange, final Object... objects) {
        if (objects.length == 0) {
            return;
        }

        final TreeViewer viewer = getViewer();
        final Control control = viewer.getControl();
        if (!control.isDisposed()) {
            final Display display = control.getDisplay();
            final ECPProject ecpProject = ECPUtil.getECPProjectManager().getProject(objects[0]);
            boolean isThreadSafe = true;
            if (ecpProject != null) {
                final InternalProvider provider = (InternalProvider) ecpProject.getProvider();
                isThreadSafe = provider.isThreadSafe();
            }
            if (display.getSyncThread() != Thread.currentThread()) {
                final Runnable refreshRunnable = createRefreshRunnable(isStructuralChange, viewer, objects);
                if (Boolean.getBoolean("enableDisplaySync") && !isThreadSafe) {
                    display.syncExec(refreshRunnable);
                } else {
                    display.asyncExec(refreshRunnable);
                }
            } else {
                if (isStructuralChange) {
                    refresh(viewer, objects);
                } else {
                    update(viewer, objects);
                }
            }
        }
    }

    private Runnable createRefreshRunnable(final boolean isStructuralChange, final TreeViewer viewer,
            final Object... objects) {
        return new Runnable() {
            @Override
            public void run() {
                if (isStructuralChange) {
                    refresh(viewer, objects);
                } else {
                    update(viewer, objects);
                }
            }
        };
    }

    protected boolean isSlow(Object parent) {
        return false;
    }

    protected InternalChildrenList getChildrenList(Object parent) {
        InternalChildrenList childrenList;
        if (isSlow(parent)) {
            SlowChildrenList newList = null;
            synchronized (slowLists) {
                childrenList = slowLists.get(parent);
                if (childrenList == null) {
                    newList = new SlowChildrenList(parent);
                    childrenList = newList;
                    slowLists.put(parent, childrenList);
                }
            }

            if (newList != null) {
                newList.startThread();
            }
        } else {
            childrenList = new ChildrenListImpl(parent);
            fillChildrenDetectError(parent, childrenList);
        }

        return childrenList;
    }

    protected void fillChildrenDetectError(Object parent, InternalChildrenList childrenList) {
        try {
            fillChildren(parent, childrenList);
        } catch (final Throwable t) {
            Activator.log(t);
            final ErrorElement errorElement = new ErrorElement(parent, t);
            childrenList.addChildWithoutRefresh(errorElement);
        }
    }

    protected abstract void fillChildren(Object parent, InternalChildrenList childrenList);

    public static void refresh(TreeViewer viewer, Object... objects) {
        if (!viewer.getControl().isDisposed()) {
            for (final Object object : objects) {
                viewer.refresh(object);
            }
        }
    }

    public static void update(TreeViewer viewer, Object... objects) {
        if (!viewer.getControl().isDisposed()) {
            for (final Object object : objects) {
                if (object != null) {
                    viewer.update(object, null);
                }
            }
        }
    }

    /**
     * @author Eike Stepper
     */
    public static class SyntheticElement {
        private final Object parent;

        public SyntheticElement(Object parent) {
            this.parent = parent;
        }

        public final Object getParent() {
            return parent;
        }
    }

    /**
     * @author Eike Stepper
     */
    public static final class ErrorElement extends SyntheticElement {
        private final Throwable cause;

        public ErrorElement(Object parent, Throwable cause) {
            super(parent);
            this.cause = cause;
        }

        public Throwable getCause() {
            return cause;
        }

        @Override
        public String toString() {
            return Messages.TreeContentProvider_ErrorElement_Error;
        }
    }

    /**
     * @author Eike Stepper
     */
    public static final class SlowElement extends SyntheticElement {
        public SlowElement(Object parent) {
            super(parent);
        }

        @Override
        public String toString() {
            return Messages.TreeContentProvider_SlowElement_Pending;
        }
    }

    /**
     * @author Eike Stepper
     */
    private final class SlowChildrenList extends ChildrenListImpl implements Runnable {
        private static final long serialVersionUID = 1L;

        private boolean complete;

        public SlowChildrenList(Object parent) {
            super(parent);
        }

        public void startThread() {
            final Thread thread = new Thread(this, "SlowChildrenList"); //$NON-NLS-1$
            thread.setDaemon(true);
            thread.start();
        }

        @Override
        public void run() {
            fillChildrenDetectError(getParent(), this);
            setComplete();
        }

        @Override
        public boolean isSlow() {
            return true;
        }

        @Override
        public boolean isComplete() {
            return complete;
        }

        @Override
        public void setComplete() {
            if (!complete) {
                try {
                    complete = true;
                    childrenAdded();
                } finally {
                    synchronized (slowLists) {
                        slowLists.remove(getParent());
                    }
                }
            }
        }

        @Override
        protected void childrenAdded() {
            final TreeViewer viewer = getViewer();
            final Control control = viewer.getControl();
            if (!control.isDisposed()) {
                final Display display = control.getDisplay();

                // asyncExec() would lead to infinite recursion in setComplete()
                display.syncExec(new Runnable() {
                    @Override
                    public void run() {
                        if (!control.isDisposed()) {
                            refresh(viewer, getParent());
                        }
                    }
                });
            }
        }

        // private void dumpStack(String msg)
        // {
        // try
        // {
        // Object parent = getParent();
        // throw new RuntimeException(msg + " for " + parent + " (" + System.identityHashCode(parent) + ")");
        // }
        // catch (Exception ex)
        // {
        // ex.printStackTrace();
        // }
        // }
    }
}