blob: 74d0f5958ae87fc25459b8ce5b615750f8691dac [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 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.team.core.subscribers;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.internal.core.ExceptionCollector;
import org.eclipse.team.internal.core.Policy;
import org.eclipse.team.internal.core.TeamPlugin;
/**
* Thsi class provides the infrastucture for processing events in the background
*/
public abstract class BackgroundEventHandler {
// Events that need to be processed
private List awaitingProcessing = new ArrayList();
// The job that runs when events need to be processed
private Job eventHandlerJob;
// Indicate if the event handler has benn shutdown
private boolean shutdown;
// manages exceptions
private ExceptionCollector errors;
/**
* Internal resource synchronization event. Can contain a result.
*/
public static class Event {
IResource resource;
int type;
int depth;
public Event(IResource resource, int type, int depth) {
this.resource = resource;
this.type = type;
this.depth = depth;
}
public int getDepth() {
return depth;
}
public IResource getResource() {
return resource;
}
public int getType() {
return type;
}
}
protected BackgroundEventHandler() {
errors =
new ExceptionCollector(
getErrorsTitle(),
TeamPlugin.ID,
IStatus.ERROR,
null /* don't log */
);
}
/**
* Create the event handling job and schedule it
*/
protected void initializeEventHandlingJob() {
createEventHandlingJob();
schedule();
}
/**
* Handle the exception by recording it in the errors list.
* @param e
*/
protected void handleException(TeamException e) {
errors.handleException(e);
}
/**
* Create the job used for processing the events in the queue. The job stops working when
* the queue is empty.
*/
protected void createEventHandlingJob() {
eventHandlerJob = new Job(getName()) {
public IStatus run(IProgressMonitor monitor) {
return processEvents(monitor);
}
};
eventHandlerJob.addJobChangeListener(new JobChangeAdapter() {
public void done(IJobChangeEvent event) {
jobDone();
}
});
eventHandlerJob.setPriority(Job.SHORT);
}
/**
* Return the name of the handler, which is used as the job name.
* @return the name of the handler
*/
public abstract String getName();
/**
* Return the text to be displayed as the title for any errors that occur.
* @return
*/
public abstract String getErrorsTitle();
/**
* Process the event in the context of a background job.
*
* @param event
* @param monitor
*/
protected abstract void processEvent(Event event, IProgressMonitor monitor) throws TeamException;
/**
* Shutdown the event handler. Any events on the queue will be removed from the queue
* and will not be processed.
*/
public void shutdown() {
shutdown = true;
eventHandlerJob.cancel();
}
/**
* Queue the event and start the job if it's not already doing work.
*/
protected synchronized void queueEvent(Event event) {
awaitingProcessing.add(event);
if (shutdown
|| eventHandlerJob == null
|| eventHandlerJob.getState() != Job.NONE)
return;
else {
schedule();
}
}
/**
* Get the next resource to be calculated.
* @return Event to be processed
*/
private synchronized Event nextElement() {
if (shutdown || awaitingProcessing.isEmpty()) {
return null;
}
return (Event) awaitingProcessing.remove(0);
}
/**
* Process events from the events queue and dispatch results.
*/
protected IStatus processEvents(IProgressMonitor monitor) {
Event event;
errors.clear();
try {
// It's hard to know how much work is going to happen
// since the queue can grow. Use the current queue size as a hint to
// an infinite progress monitor
monitor.beginTask(null, 100);
IProgressMonitor subMonitor = Policy.infiniteSubMonitorFor(monitor, 90);
subMonitor.beginTask(null, 1024);
while ((event = nextElement()) != null) {
// Cancellation is dangerous because this will leave the sync info in a bad state.
// Purposely not checking -
try {
processEvent(event, subMonitor);
} catch (TeamException e) {
// handle exception but keep going
handleException(e);
}
}
} finally {
monitor.done();
}
return errors.getStatus();
}
/**
* This method is invoked when the processing job completes. The
* default behavior of the handler is to restart the job if the queue
* is no longer empty and to clear the queue if the handler was shut down.
*
*/
protected void jobDone() {
// Make sure an unhandled event didn't squeak in unless we are shutdown
if (shutdown == false && hasUnprocessedEvents()) {
schedule();
} else {
synchronized(this) {
awaitingProcessing.clear();
}
}
}
protected boolean hasUnprocessedEvents() {
return !awaitingProcessing.isEmpty();
}
/**
* Schedule the job or process the events now.
*/
protected void schedule() {
eventHandlerJob.schedule();
}
/**
* @return Returns the eventHandlerJob.
*/
public Job getEventHandlerJob() {
return eventHandlerJob;
}
}