| /******************************************************************************* |
| * Copyright (c) 2003 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-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.PlatformUI; |
| 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 = false; |
| |
| /** |
| * 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 addition |
| */ |
| void remove(JobTreeElement removal) { |
| deletions.add(removal); |
| } |
| /** |
| * Add a refresh update |
| * |
| * @param addition |
| */ |
| 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); |
| 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 |
| */ |
| 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 |
| */ |
| 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. |
| * |
| * @return |
| */ |
| private ProgressViewUpdater() { |
| createUpdateJob(); |
| collectors = new IProgressUpdateCollector[0]; |
| ProgressManager.getInstance().addListener(this); |
| |
| } |
| |
| /** |
| * 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 newCollector |
| */ |
| 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.getString("ProgressContentProvider.UpdateProgressJob")) {//$NON-NLS-1$ |
| /* |
| * (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) { |
| 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(); |
| |
| } |
| |
| 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); |
| } |
| } |
| |
| synchronized (updateLock) { |
| currentInfo.reset(); |
| } |
| |
| return Status.OK_STATUS; |
| |
| } |
| |
| }; |
| updateJob.setSystem(true); |
| updateJob.setPriority(Job.DECORATE); |
| |
| } |
| /** |
| * Get the updates info that we are using in the receiver. |
| * |
| * @return Returns the currentInfo. |
| */ |
| UpdatesInfo getCurrentInfo() { |
| return currentInfo; |
| } |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ui.internal.progress.IJobProgressManagerListener#refresh(org.eclipse.ui.internal.progress.JobInfo) |
| */ |
| 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 isUpdateJob(Job job) { |
| return job.equals(updateJob); |
| } |
| } |