blob: 496fff1f8237c1a9d30e4d0f56a396e6e6644ffc [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 2015 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.time.Duration;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import org.eclipse.jface.util.Throttler;
import org.eclipse.ui.IWorkbenchPreferenceConstants;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.internal.util.PrefUtil;
/**
* The ProgressViewUpdater is the singleton that updates viewers.
*/
class ProgressViewUpdater implements IJobProgressManagerListener {
private static ProgressViewUpdater singleton;
private Set<IProgressUpdateCollector> collectors;
UpdatesInfo currentInfo = new UpdatesInfo();
boolean debug;
Throttler throttledUpdate = new Throttler(PlatformUI.getWorkbench().getDisplay(), Duration.ofMillis(100),
this::update);
/**
* The UpdatesInfo is a private class for keeping track of the updates
* required.
*/
static class UpdatesInfo {
Collection<JobTreeElement> additions = new LinkedHashSet<>();
Collection<JobTreeElement> deletions = new LinkedHashSet<>();
Collection<JobTreeElement> refreshes = new LinkedHashSet<>();
boolean updateAll;
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<JobTreeElement> staleAdditions = new HashSet<>();
Iterator<JobTreeElement> additionsIterator = additions.iterator();
while (additionsIterator.hasNext()) {
JobTreeElement treeElement = additionsIterator
.next();
if (!treeElement.isActive()) {
if (deletions.contains(treeElement)) {
staleAdditions.add(treeElement);
}
}
}
additions.removeAll(staleAdditions);
HashSet<JobTreeElement> obsoleteRefresh = new HashSet<>();
Iterator<JobTreeElement> refreshIterator = refreshes.iterator();
while (refreshIterator.hasNext()) {
JobTreeElement treeElement = 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() {
collectors = new LinkedHashSet<>();
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) {
collectors.add(newCollector);
}
/**
* Remove the collector from the list of collectors.
*
* @param provider
*/
void removeCollector(IProgressUpdateCollector provider) {
collectors.remove(provider);
//Remove ourselves if there is nothing to update
if (collectors.isEmpty()) {
clearSingleton();
}
}
private void update() {
// Abort the update if there isn't anything
if (collectors.isEmpty()) {
return;
}
if (currentInfo.updateAll) {
currentInfo.reset();
for (IProgressUpdateCollector collector : collectors) {
collector.refresh();
}
} else {
// Lock while getting local copies of the caches.
JobTreeElement[] updateItems;
JobTreeElement[] additionItems;
JobTreeElement[] deletionItems;
currentInfo.processForUpdate();
updateItems = currentInfo.refreshes.toArray(new JobTreeElement[0]);
additionItems = currentInfo.additions.toArray(new JobTreeElement[0]);
deletionItems = currentInfo.deletions.toArray(new JobTreeElement[0]);
currentInfo.reset();
for (IProgressUpdateCollector collector : collectors) {
if (updateItems.length > 0) {
collector.refresh(updateItems);
}
if (additionItems.length > 0) {
collector.add(additionItems);
}
if (deletionItems.length > 0) {
collector.remove(deletionItems);
}
}
}
}
/**
* Refresh the supplied JobInfo.
* @param info
*/
public void refresh(JobInfo info) {
currentInfo.refresh(info);
GroupInfo group = info.getGroupInfo();
if (group != null) {
currentInfo.refresh(group);
}
//Add in a 100ms delay so as to keep priority low
throttledUpdate.throttledExec();
}
@Override
public void refreshJobInfo(JobInfo info) {
currentInfo.refresh(info);
//Add in a 100ms delay so as to keep priority low
throttledUpdate.throttledExec();
}
@Override
public void refreshGroup(GroupInfo info) {
currentInfo.refresh(info);
//Add in a 100ms delay so as to keep priority low
throttledUpdate.throttledExec();
}
@Override
public void addGroup(GroupInfo info) {
currentInfo.add(info);
throttledUpdate.throttledExec();
}
@Override
public void refreshAll() {
currentInfo.updateAll = true;
//Add in a 100ms delay so as to keep priority low
throttledUpdate.throttledExec();
}
@Override
public void addJob(JobInfo info) {
GroupInfo group = info.getGroupInfo();
if (group == null) {
currentInfo.add(info);
} else {
currentInfo.refresh(group);
}
throttledUpdate.throttledExec();
}
@Override
public void removeJob(JobInfo info) {
GroupInfo group = info.getGroupInfo();
if (group == null) {
currentInfo.remove(info);
} else {
currentInfo.refresh(group);
}
throttledUpdate.throttledExec();
}
@Override
public void removeGroup(GroupInfo group) {
currentInfo.remove(group);
throttledUpdate.throttledExec();
}
@Override
public boolean showsDebug() {
return debug;
}
}