blob: 2db4929adc79ddfcb5cb7122c8802337190eed27 [file] [log] [blame]
/**********************************************************************
* Copyright (c) 2000,2002 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v0.5
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v05.html
*
* Contributors:
* IBM - Initial API and implementation
**********************************************************************/
package org.eclipse.core.internal.events;
import org.eclipse.core.internal.resources.IManager;
import org.eclipse.core.internal.resources.Workspace;
import org.eclipse.core.internal.utils.Policy;
import org.eclipse.core.internal.watson.ElementTree;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
public class NotificationManager implements IManager, ILifecycleListener {
protected ResourceChangeListenerList listeners;
protected Workspace workspace;
protected ElementTree oldState;
// if there are no changes between the current tree and the last delta state then we
// can reuse the lastDelta (if any). If the lastMarkerChangeId is different then the current
// one then we have to update that delta with new marker change info
protected ElementTree lastDeltaState; // tree the last time we broadcast a change
protected ResourceDelta lastDelta; // last delta we broadcast
protected long lastMarkerChangeId = 0; // the marker change id the last time we broadcast
public final static String extensionId = Platform.PI_RUNTIME + ".notification"; //$NON-NLS-1$
public NotificationManager(Workspace workspace) {
this.workspace = workspace;
listeners = new ResourceChangeListenerList();
}
public void addListener(IResourceChangeListener listener, int eventMask) {
synchronized (listeners) {
listeners.add(listener, eventMask);
}
EventStats.listenerAdded(listener);
}
/**
* Helper method for the save participant lifecycle computation.
*/
public void broadcastChanges(IResourceChangeListener listener, int type, IResourceDelta delta, boolean lockTree) {
ResourceChangeListenerList.ListenerEntry[] listeners;
listeners = new ResourceChangeListenerList.ListenerEntry[] { new ResourceChangeListenerList.ListenerEntry(listener, type)};
notify(listeners, new ResourceChangeEvent(workspace, type, delta), lockTree);
}
/**
* The main broadcast point for notification deltas
*/
public void broadcastChanges(ElementTree lastState, int type, boolean lockTree, boolean updateState) {
try {
// Do the notification if there are listeners for events of the given type.
// Be sure to do all of this inside the try/finally as the finally will update the state
// if requested. This needs to happen regardless of whether people are listening.
if (listeners.hasListenerFor(type)) {
IResourceDelta delta = getDelta(lastState);
// if the delta is empty the root's change is undefined, there is nothing to do
if (delta == null || delta.getKind() == 0)
return;
notify(getListeners(), new ResourceChangeEvent(workspace, type, delta), lockTree);
}
} finally {
// Remember the current state as the last notified state if requested.
// Even if there are problems during the notification there is no need to abort.
// Be sure to clear out the old delta
if (updateState) {
lastState.immutable();
oldState = lastState;
lastDelta = null;
lastDeltaState = lastState;
lastMarkerChangeId = 0;
}
}
}
protected ResourceDelta getDelta(ElementTree tree) {
long id = workspace.getMarkerManager().getChangeId();
// if we have a delta from last time and no resources have changed since then, we
// can reuse the delta structure
if (lastDelta != null && !ElementTree.hasChanges(tree, lastDeltaState, ResourceComparator.getComparator(true), true)) {
// Markers may have changed since the delta was generated. If so, get the new
// marker state and insert it in to the delta which is being reused.
if (id != lastMarkerChangeId)
lastDelta.updateMarkers(workspace.getMarkerManager().getMarkerDeltas());
} else
// We don't have a delta or something changed so recompute the whole deal.
lastDelta = ResourceDeltaFactory.computeDelta(workspace, oldState, tree, Path.ROOT, true);
// remember the state of the world when this delta was consistent
lastMarkerChangeId = id;
lastDeltaState = tree;
return lastDelta;
}
protected ResourceChangeListenerList.ListenerEntry[] getListeners() {
ResourceChangeListenerList.ListenerEntry[] result;
synchronized (listeners) {
result = listeners.getListeners();
}
return result;
}
public void handleEvent(LifecycleEvent event) {
switch (event.kind) {
case LifecycleEvent.PRE_PROJECT_CLOSE:
if (!listeners.hasListenerFor(IResourceChangeEvent.PRE_CLOSE))
return;
IProject project = (IProject)event.resource;
notify(getListeners(), new ResourceChangeEvent(workspace, IResourceChangeEvent.PRE_CLOSE, project), true);
break;
case LifecycleEvent.PRE_PROJECT_DELETE:
if (!listeners.hasListenerFor(IResourceChangeEvent.PRE_DELETE))
return;
project = (IProject)event.resource;
notify(getListeners(), new ResourceChangeEvent(workspace, IResourceChangeEvent.PRE_DELETE, project), true);
break;
}
}
private void notify(ResourceChangeListenerList.ListenerEntry[] resourceListeners, final IResourceChangeEvent event, boolean lockTree) {
int type = event.getType();
for (int i = 0; i < resourceListeners.length; i++) {
if ((type & resourceListeners[i].eventMask) != 0) {
final IResourceChangeListener listener = resourceListeners[i].listener;
if (Policy.MONITOR_LISTENERS)
EventStats.startNotify(listener);
ISafeRunnable code = new ISafeRunnable() {
public void run() throws Exception {
listener.resourceChanged(event);
}
public void handleException(Throwable e) {
//ResourceStats.notifyException(e);
// don't log the exception....it is already being logged in Platform#run
}
};
boolean oldLock = workspace.isTreeLocked();
boolean immutable = workspace.getElementTree().isImmutable();
if (lockTree)
workspace.setTreeLocked(true);
else
if (immutable)
workspace.newWorkingTree();
try {
Platform.run(code);
} finally {
if (lockTree)
workspace.setTreeLocked(oldLock);
else
if (immutable)
workspace.getElementTree().immutable();
}
if (Policy.MONITOR_LISTENERS)
EventStats.endNotify();
}
}
}
public void removeListener(IResourceChangeListener listener) {
synchronized (listeners) {
listeners.remove(listener);
}
EventStats.listenerRemoved(listener);
}
public void shutdown(IProgressMonitor monitor) {
}
public void startup(IProgressMonitor monitor) {
// get the current state of the workspace as the starting point and
// tell the workspace to track changes from there. This gives the
// notificaiton manager an initial basis for comparison.
oldState = workspace.getElementTree();
workspace.addLifecycleListener(this);
}
}