All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.ui.progress.DeferredTreeContentManager Maven / Gradle / Ivy

Go to download

This plug-in contains the bulk of the Workbench implementation, and depends on JFace, SWT, and Core Runtime. It cannot be used independently from org.eclipse.ui. Workbench client plug-ins should not depend directly on this plug-in.

The newest version!
/*******************************************************************************
 * Copyright (c) 2003, 2007 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.ui.progress;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.internal.progress.ProgressMessages;
import org.eclipse.ui.internal.util.Util;
import org.eclipse.ui.model.IWorkbenchAdapter;

/**
 * The DeferredContentManager is a class that helps an ITreeContentProvider get
 * its deferred input.
 * 
 * NOTE AbstractTreeViewer#isExpandable may need to 
 * be implemented in AbstractTreeViewer subclasses with 
 * deferred content that use filtering as a call to 
 * #getChildren may be required to determine the correct 
 * state of the expanding control.
 * 
 * AbstractTreeViewers which use this class may wish to 
 * sacrifice accuracy of the expandable state indicator for the 
 * performance benefits of deferring content.
 * 
 * @see IDeferredWorkbenchAdapter
 * @since 3.0
 */
public class DeferredTreeContentManager {
    ITreeContentProvider contentProvider;

    AbstractTreeViewer treeViewer;

    IWorkbenchSiteProgressService progressService;
    
    /**
     * The DeferredContentFamily is a class used to keep track of a
     * manager-object pair so that only jobs scheduled by the receiver
     * are cancelled by the receiver.
     * @since 3.1
     *
     */
    class DeferredContentFamily {
    	protected DeferredTreeContentManager manager;
		protected Object element;
		
		/**
		 * Create a new instance of the receiver to define a family
		 * for object in a particular scheduling manager.
		 * @param schedulingManager
		 * @param object
		 */
		DeferredContentFamily(DeferredTreeContentManager schedulingManager, Object object) {
			this.manager = schedulingManager;
			this.element = object;
    	}
    }

    /**
     * Create a new instance of the receiver using the supplied content
     * provider and viewer. Run any jobs using the site.
     * 
     * @param provider
     * @param viewer
     * @param site
     */
    public DeferredTreeContentManager(ITreeContentProvider provider,
            AbstractTreeViewer viewer, IWorkbenchPartSite site) {
        this(provider, viewer);
        Object siteService = Util.getAdapter(site, 
                IWorkbenchSiteProgressService.class);
        if (siteService != null) {
			progressService = (IWorkbenchSiteProgressService) siteService;
		}
    }

    /**
     * Create a new instance of the receiver using the supplied content
     * provider and viewer.
     * 
     * @param provider The content provider that will be updated
     * @param viewer The tree viewer that the results are added to
     */
    public DeferredTreeContentManager(ITreeContentProvider provider,
            AbstractTreeViewer viewer) {
        contentProvider = provider;
        treeViewer = viewer;
    }

    /**
     * Provides an optimized lookup for determining if an element has children.
     * This is required because elements that are populated lazilly can't
     * answer getChildren just to determine the potential for
     * children. Throw an AssertionFailedException if element is null.
     * 
     * @param element The Object being tested. This should not be
     * null.
     * @return boolean true if there are potentially children.
     * @throws RuntimeException if the element is null.
     */
    public boolean mayHaveChildren(Object element) {
    	Assert.isNotNull(element, ProgressMessages.DeferredTreeContentManager_NotDeferred); 
        IDeferredWorkbenchAdapter adapter = getAdapter(element);
        return adapter != null && adapter.isContainer();
    }

    /**
     * Returns the child elements of the given element, or in the case of a
     * deferred element, returns a placeholder. If a deferred element is used, a
     * job is created to fetch the children in the background.
     * 
     * @param parent
     *            The parent object.
     * @return Object[] or null if parent is not an instance of
     *         IDeferredWorkbenchAdapter.
     */
    public Object[] getChildren(final Object parent) {
        IDeferredWorkbenchAdapter element = getAdapter(parent);
        if (element == null) {
			return null;
		}
        PendingUpdateAdapter placeholder = createPendingUpdateAdapter();
        startFetchingDeferredChildren(parent, element, placeholder);
        return new Object[] { placeholder };
    }

	/**
	 * Factory method for creating the pending update adapter representing the
	 * placeholder node. Subclasses may override.
	 * 
	 * @return a pending update adapter
	 * @since 3.2
	 */
	protected PendingUpdateAdapter createPendingUpdateAdapter() {
		return new PendingUpdateAdapter();
	}

    /**
     * Return the IDeferredWorkbenchAdapter for element or the element if it is
     * an instance of IDeferredWorkbenchAdapter. If it does not exist return
     * null.
     * 
     * @param element
     * @return IDeferredWorkbenchAdapter or null
     */
    protected IDeferredWorkbenchAdapter getAdapter(Object element) {
        return (IDeferredWorkbenchAdapter)Util.getAdapter(element, IDeferredWorkbenchAdapter.class);
    }

    /**
     * Starts a job and creates a collector for fetching the children of this
     * deferred adapter. If children are waiting to be retrieved for this parent
     * already, that job is cancelled and another is started.
     * 
     * @param parent
     *            The parent object being filled in,
     * @param adapter
     *            The adapter being used to fetch the children.
     * @param placeholder
     *            The adapter that will be used to indicate that results are
     *            pending.
     */
    protected void startFetchingDeferredChildren(final Object parent,
            final IDeferredWorkbenchAdapter adapter,
            final PendingUpdateAdapter placeholder) {
        final IElementCollector collector = createElementCollector(parent,
                placeholder);
        // Cancel any jobs currently fetching children for the same parent
        // instance.
        cancel(parent);
        String jobName = getFetchJobName(parent, adapter);
        Job job = new Job(jobName) {
            /* (non-Javadoc)
             * @see org.eclipse.core.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
             */
            public IStatus run(IProgressMonitor monitor) {
                adapter.fetchDeferredChildren(parent, collector, monitor);
                if(monitor.isCanceled()) {
					return Status.CANCEL_STATUS;
				}
				return Status.OK_STATUS;
            }

           
            /* (non-Javadoc)
             * @see org.eclipse.core.jobs.Job#belongsTo(java.lang.Object)
             */
            public boolean belongsTo(Object family) {
            	if (family instanceof DeferredContentFamily) {
            		DeferredContentFamily contentFamily = (DeferredContentFamily) family;
            		if (contentFamily.manager == DeferredTreeContentManager.this) {
						return isParent(contentFamily, parent);
					}
            	}
                return false;
               
            }

            /**
             * Check if the parent of element is equal to the parent used in
             * this job.
             * 
             * @param family
             *            The DeferredContentFamily that defines a potential 
             *            ancestor of the current parent in a particualr manager.
             * @param child
             *            The object to check against.
             * @return boolean true if the child or one of its
             * 			  parents are the same as the element of the family.
             */
            private boolean isParent(DeferredContentFamily family, Object child) {
                if (family.element.equals(child)) {
					return true;
				}
                IWorkbenchAdapter workbenchAdapter = getWorkbenchAdapter(child);
                if (workbenchAdapter == null) {
					return false;
				}
                Object elementParent = workbenchAdapter.getParent(child);
                if (elementParent == null) {
					return false;
				}
                return isParent(family, elementParent);
            }

            /**
             * Get the workbench adapter for the element.
             * 
             * @param element
             *            The object we are adapting to.
             */
            private IWorkbenchAdapter getWorkbenchAdapter(Object element) {
                return (IWorkbenchAdapter) Util.getAdapter(element, IWorkbenchAdapter.class);
            }
        };
        job.addJobChangeListener(new JobChangeAdapter() {
            /*
             * (non-Javadoc)
             * 
             * @see org.eclipse.core.runtime.jobs.JobChangeAdapter#done(org.eclipse.core.runtime.jobs.IJobChangeEvent)
             */
            public void done(IJobChangeEvent event) {
                runClearPlaceholderJob(placeholder);
            }
        });
        job.setRule(adapter.getRule(parent));
        if (progressService == null) {
			job.schedule();
		} else {
			progressService.schedule(job);
		}
    }
    
    /**
     * Returns a name to use for the job that fetches children of the given parent.
     * Subclasses may override. Default job name is parent's label.
     * 
     * @param parent parent that children are to be fetched for
     * @param adapter parent's deferred adapter
     * @return job name
     */
    protected String getFetchJobName(Object parent, IDeferredWorkbenchAdapter adapter) {
        return NLS.bind(
				ProgressMessages.DeferredTreeContentManager_FetchingName,
               adapter.getLabel(parent));
    }

    /**
     * Create a UIJob to add the children to the parent in the tree viewer.
     * 
     * @param parent
     * @param children
     * @param monitor
     */
    protected void addChildren(final Object parent, final Object[] children,
            IProgressMonitor monitor) {
        WorkbenchJob updateJob = new WorkbenchJob(
				ProgressMessages.DeferredTreeContentManager_AddingChildren) {
            /*
             * (non-Javadoc)
             * 
             * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
             */
            public IStatus runInUIThread(IProgressMonitor updateMonitor) {
                //Cancel the job if the tree viewer got closed
                if (treeViewer.getControl().isDisposed() || updateMonitor.isCanceled()) {
					return Status.CANCEL_STATUS;
				}
                treeViewer.add(parent, children);
                return Status.OK_STATUS;
            }
        };
        updateJob.setSystem(true);
        updateJob.schedule();

    }

    /**
     * Return whether or not the element is or adapts to an
     * IDeferredWorkbenchAdapter.
     * 
     * @param element
     * @return boolean true if the element is an
     *         IDeferredWorkbenchAdapter
     */
    public boolean isDeferredAdapter(Object element) {
        return getAdapter(element) != null;
    }

    /**
     * Run a job to clear the placeholder. This is used when the update
     * for the tree is complete so that the user is aware that no more 
     * updates are pending.
     * 
     * @param placeholder
     */
    protected void runClearPlaceholderJob(final PendingUpdateAdapter placeholder) {
        if (placeholder.isRemoved() || !PlatformUI.isWorkbenchRunning()) {
			return;
		}
        //Clear the placeholder if it is still there
        WorkbenchJob clearJob = new WorkbenchJob(ProgressMessages.DeferredTreeContentManager_ClearJob) {
            /*
             * (non-Javadoc)
             * 
             * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
             */
            public IStatus runInUIThread(IProgressMonitor monitor) {
                if (!placeholder.isRemoved()) {
                    Control control = treeViewer.getControl();
                    if (control.isDisposed()) {
						return Status.CANCEL_STATUS;
					}
                    treeViewer.remove(placeholder);
                    placeholder.setRemoved(true);
                }
                return Status.OK_STATUS;
            }
        };
        clearJob.setSystem(true);
        clearJob.schedule();
    }

    /**
     * Cancel all jobs that are fetching content for the given parent or any of
     * its children.
     * 
     * @param parent
     */
    public void cancel(Object parent) {
    	if(parent == null) {
			return;
		}
    	
        Platform.getJobManager().cancel(new DeferredContentFamily(this, parent));
    }

    /**
     * Create the element collector for the receiver.
     *@param parent
     *            The parent object being filled in,
     * @param placeholder
     *            The adapter that will be used to indicate that results are
     *            pending.
     * @return IElementCollector
     */
    protected IElementCollector createElementCollector(final Object parent,
            final PendingUpdateAdapter placeholder) {
        return new IElementCollector() {
            /*
             *  (non-Javadoc)
             * @see org.eclipse.jface.progress.IElementCollector#add(java.lang.Object, org.eclipse.core.runtime.IProgressMonitor)
             */
            public void add(Object element, IProgressMonitor monitor) {
                add(new Object[] { element }, monitor);
            }

            /*
             *  (non-Javadoc)
             * @see org.eclipse.jface.progress.IElementCollector#add(java.lang.Object[], org.eclipse.core.runtime.IProgressMonitor)
             */
            public void add(Object[] elements, IProgressMonitor monitor) {
                addChildren(parent, elements, monitor);
            }

            /*
             * (non-Javadoc)
             * 
             * @see org.eclipse.jface.progress.IElementCollector#done()
             */
            public void done() {
                runClearPlaceholderJob(placeholder);
            }
        };
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy