blob: 33e9078d7d8c3bf070b15a58fd40b53d16dd8988 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 2006 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.internal.progress;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.ui.IWorkbenchPreferenceConstants;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.internal.util.PrefUtil;
import org.eclipse.ui.progress.WorkbenchJob;
/**
* The ProgressViewUpdater is the singleton that updates viewers.
*/
class ProgressViewUpdater implements IJobProgressManagerListener {
private static ProgressViewUpdater singleton;
private IProgressUpdateCollector[] collectors;
Job updateJob;
UpdatesInfo currentInfo = new UpdatesInfo();
Object updateLock = new Object();
boolean debug;
/**
* The UpdatesInfo is a private class for keeping track of the updates
* required.
*/
class UpdatesInfo {
Collection additions = new HashSet();
Collection deletions = new HashSet();
Collection refreshes = new HashSet();
boolean updateAll = false;
private UpdatesInfo() {
//Create a new instance of the info
}
/**
* Add an add update
*
* @param addition
*/
void add(JobTreeElement addition) {
additions.add(addition);
}
/**
* Add a remove update
*
* @param removal
*/
void remove(JobTreeElement removal) {
deletions.add(removal);
}
/**
* Add a refresh update
*
* @param refresh
*/
void refresh(JobTreeElement refresh) {
refreshes.add(refresh);
}
/**
* Reset the caches after completion of an update.
*/
void reset() {
additions.clear();
deletions.clear();
refreshes.clear();
updateAll = false;
}
void processForUpdate() {
HashSet staleAdditions = new HashSet();
Iterator additionsIterator = additions.iterator();
while (additionsIterator.hasNext()) {
JobTreeElement treeElement = (JobTreeElement) additionsIterator
.next();
if (!treeElement.isActive()) {
if (deletions.contains(treeElement)) {
staleAdditions.add(treeElement);
}
}
}
additions.removeAll(staleAdditions);
HashSet obsoleteRefresh = new HashSet();
Iterator refreshIterator = refreshes.iterator();
while (refreshIterator.hasNext()) {
JobTreeElement treeElement = (JobTreeElement) refreshIterator
.next();
if (deletions.contains(treeElement)
|| additions.contains(treeElement)) {
obsoleteRefresh.add(treeElement);
}
//Also check for groups that are being added
Object parent = treeElement.getParent();
if(parent != null && (deletions.contains(parent)
|| additions.contains(parent))){
obsoleteRefresh.add(treeElement);
}
if (!treeElement.isActive()) {
//If it is done then delete it
obsoleteRefresh.add(treeElement);
deletions.add(treeElement);
}
}
refreshes.removeAll(obsoleteRefresh);
}
}
/**
* Return a new instance of the receiver.
*
* @return ProgressViewUpdater
*/
static ProgressViewUpdater getSingleton() {
if (singleton == null) {
singleton = new ProgressViewUpdater();
}
return singleton;
}
/**
* Return whether or not there is a singleton for updates to avoid creating
* extra listeners.
*
* @return boolean <code>true</code> if there is already
* a singleton
*/
static boolean hasSingleton() {
return singleton != null;
}
static void clearSingleton() {
if (singleton != null) {
ProgressManager.getInstance().removeListener(singleton);
}
singleton = null;
}
/**
* Create a new instance of the receiver.
*/
private ProgressViewUpdater() {
createUpdateJob();
collectors = new IProgressUpdateCollector[0];
ProgressManager.getInstance().addListener(this);
debug =
PrefUtil.getAPIPreferenceStore().
getBoolean(IWorkbenchPreferenceConstants.SHOW_SYSTEM_JOBS);
}
/**
* Add the new collector to the list of collectors.
*
* @param newCollector
*/
void addCollector(IProgressUpdateCollector newCollector) {
IProgressUpdateCollector[] newCollectors = new IProgressUpdateCollector[collectors.length + 1];
System.arraycopy(collectors, 0, newCollectors, 0, collectors.length);
newCollectors[collectors.length] = newCollector;
collectors = newCollectors;
}
/**
* Remove the collector from the list of collectors.
*
* @param provider
*/
void removeCollector(IProgressUpdateCollector provider) {
HashSet newCollectors = new HashSet();
for (int i = 0; i < collectors.length; i++) {
if (!collectors[i].equals(provider)) {
newCollectors.add(collectors[i]);
}
}
IProgressUpdateCollector[] newArray = new IProgressUpdateCollector[newCollectors
.size()];
newCollectors.toArray(newArray);
collectors = newArray;
//Remove ourselves if there is nothing to update
if (collectors.length == 0) {
clearSingleton();
}
}
/**
* Schedule an update.
*/
void scheduleUpdate() {
if (PlatformUI.isWorkbenchRunning()) {
//Add in a 100ms delay so as to keep priority low
updateJob.schedule(100);
}
}
/**
* Create the update job that handles the updatesInfo.
*/
private void createUpdateJob() {
updateJob = new WorkbenchJob(ProgressMessages.ProgressContentProvider_UpdateProgressJob) {
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
*/
public IStatus runInUIThread(IProgressMonitor monitor) {
//Abort the job if there isn't anything
if (collectors.length == 0) {
return Status.CANCEL_STATUS;
}
if (currentInfo.updateAll) {
synchronized (updateLock) {
currentInfo.reset();
}
for (int i = 0; i < collectors.length; i++) {
collectors[i].refresh();
}
} else {
//Lock while getting local copies of the caches.
Object[] updateItems;
Object[] additionItems;
Object[] deletionItems;
synchronized (updateLock) {
currentInfo.processForUpdate();
updateItems = currentInfo.refreshes.toArray();
additionItems = currentInfo.additions.toArray();
deletionItems = currentInfo.deletions.toArray();
currentInfo.reset();
}
for (int v = 0; v < collectors.length; v++) {
IProgressUpdateCollector collector = collectors[v];
if (updateItems.length > 0) {
collector.refresh(updateItems);
}
if (additionItems.length > 0) {
collector.add(additionItems);
}
if (deletionItems.length > 0) {
collector.remove(deletionItems);
}
}
}
return Status.OK_STATUS;
}
};
updateJob.setSystem(true);
updateJob.setPriority(Job.DECORATE);
updateJob.setProperty(ProgressManagerUtil.INFRASTRUCTURE_PROPERTY, new Object());
}
/**
* Get the updates info that we are using in the receiver.
*
* @return Returns the currentInfo.
*/
UpdatesInfo getCurrentInfo() {
return currentInfo;
}
/**
* Refresh the supplied JobInfo.
* @param info
*/
public void refresh(JobInfo info) {
if (isUpdateJob(info.getJob())) {
return;
}
synchronized (updateLock) {
currentInfo.refresh(info);
GroupInfo group = info.getGroupInfo();
if (group != null) {
currentInfo.refresh(group);
}
}
//Add in a 100ms delay so as to keep priority low
scheduleUpdate();
}
/* (non-Javadoc)
* @see org.eclipse.ui.internal.progress.IJobProgressManagerListener#refreshJobInfo(org.eclipse.ui.internal.progress.JobInfo)
*/
public void refreshJobInfo(JobInfo info) {
if (isUpdateJob(info.getJob())) {
return;
}
synchronized (updateLock) {
currentInfo.refresh(info);
}
//Add in a 100ms delay so as to keep priority low
scheduleUpdate();
}
/* (non-Javadoc)
* @see org.eclipse.ui.internal.progress.IJobProgressManagerListener#refreshGroup(org.eclipse.ui.internal.progress.GroupInfo)
*/
public void refreshGroup(GroupInfo info) {
synchronized (updateLock) {
currentInfo.refresh(info);
}
//Add in a 100ms delay so as to keep priority low
scheduleUpdate();
}
/* (non-Javadoc)
* @see org.eclipse.ui.internal.progress.IJobProgressManagerListener#addGroup(org.eclipse.ui.internal.progress.GroupInfo)
*/
public void addGroup(GroupInfo info) {
synchronized (updateLock) {
currentInfo.add(info);
}
scheduleUpdate();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.internal.progress.IJobProgressManagerListener#refreshAll()
*/
public void refreshAll() {
synchronized (updateLock) {
currentInfo.updateAll = true;
}
//Add in a 100ms delay so as to keep priority low
scheduleUpdate();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.internal.progress.IJobProgressManagerListener#add(org.eclipse.ui.internal.progress.JobInfo)
*/
public void addJob(JobInfo info) {
if (isUpdateJob(info.getJob())) {
return;
}
synchronized (updateLock) {
GroupInfo group = info.getGroupInfo();
if (group == null) {
currentInfo.add(info);
} else {
currentInfo.refresh(group);
}
}
scheduleUpdate();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.internal.progress.IJobProgressManagerListener#removeJob(org.eclipse.ui.internal.progress.JobInfo)
*/
public void removeJob(JobInfo info) {
if (isUpdateJob(info.getJob())) {
return;
}
synchronized (updateLock) {
GroupInfo group = info.getGroupInfo();
if (group == null) {
currentInfo.remove(info);
} else {
group.removeJobInfo(info);
currentInfo.refresh(group);
}
}
scheduleUpdate();
}
/* (non-Javadoc)
* @see org.eclipse.ui.internal.progress.IJobProgressManagerListener#removeGroup(org.eclipse.ui.internal.progress.GroupInfo)
*/
public void removeGroup(GroupInfo group) {
synchronized (updateLock) {
currentInfo.remove(group);
}
scheduleUpdate();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.internal.progress.IJobProgressManagerListener#showsDebug()
*/
public boolean showsDebug() {
return debug;
}
/**
* Return whether or not this is the update job. This is used to determine
* if a final refresh is required.
*
* @param job
* @return boolean <code>true</true> if this is the
* update job
*/
boolean isUpdateJob(Job job) {
return job.equals(updateJob);
}
}