1 11 package org.eclipse.core.internal.events; 12 13 import java.util.*; 14 import org.eclipse.core.internal.resources.IManager; 15 import org.eclipse.core.internal.resources.Workspace; 16 import org.eclipse.core.internal.utils.Messages; 17 import org.eclipse.core.internal.watson.ElementTree; 18 import org.eclipse.core.resources.*; 19 import org.eclipse.core.runtime.*; 20 import org.eclipse.core.runtime.jobs.Job; 21 22 public class NotificationManager implements IManager, ILifecycleListener { 23 class NotifyJob extends Job { 24 private final IWorkspaceRunnable noop = new IWorkspaceRunnable() { 25 public void run(IProgressMonitor monitor) { 26 } 27 }; 28 29 public NotifyJob() { 30 super(Messages.resources_updating); 31 setSystem(true); 32 } 33 34 public IStatus run(IProgressMonitor monitor) { 35 if (monitor.isCanceled()) 36 return Status.CANCEL_STATUS; 37 notificationRequested = true; 38 try { 39 workspace.run(noop, null, IResource.NONE, null); 40 } catch (CoreException e) { 41 return e.getStatus(); 42 } 43 return Status.OK_STATUS; 44 } 45 } 46 47 private static final long NOTIFICATION_DELAY = 1500; 48 51 private Set avoidNotify = new HashSet(); 52 53 57 protected boolean isNotifying; 58 59 65 private ResourceDelta lastDelta; 66 69 private long lastDeltaId; 70 73 private ElementTree lastDeltaState; 74 protected long lastNotifyDuration = 0L; 75 78 private long lastPostBuildId = 0; 79 83 private ElementTree lastPostBuildTree; 84 87 private long lastPostChangeId = 0; 88 92 private ElementTree lastPostChangeTree; 93 94 private ResourceChangeListenerList listeners; 95 96 protected boolean notificationRequested = false; 97 private Job notifyJob; 98 Workspace workspace; 99 100 public NotificationManager(Workspace workspace) { 101 this.workspace = workspace; 102 listeners = new ResourceChangeListenerList(); 103 notifyJob = new NotifyJob(); 104 } 105 106 public void addListener(IResourceChangeListener listener, int eventMask) { 107 listeners.add(listener, eventMask); 108 if (ResourceStats.TRACE_LISTENERS) 109 ResourceStats.listenerAdded(listener); 110 } 111 112 117 public boolean beginAvoidNotify() { 118 return avoidNotify.add(Thread.currentThread()); 119 } 120 121 124 public void beginNotify() { 125 notifyJob.cancel(); 126 notificationRequested = false; 127 } 128 129 132 public void broadcastChanges(ElementTree lastState, ResourceChangeEvent event, boolean lockTree) { 133 final int type = event.getType(); 134 try { 135 if (!listeners.hasListenerFor(type)) 137 return; 138 isNotifying = true; 139 ResourceDelta delta = getDelta(lastState, type); 140 if (delta == null || delta.getKind() == 0) { 142 int trigger = event.getBuildKind(); 143 if (trigger == IncrementalProjectBuilder.AUTO_BUILD || trigger == 0) 144 return; 145 } 146 event.setDelta(delta); 147 long start = System.currentTimeMillis(); 148 notify(getListeners(), event, lockTree); 149 lastNotifyDuration = System.currentTimeMillis() - start; 150 } finally { 151 isNotifying = false; 153 cleanUp(lastState, type); 154 } 155 } 156 157 160 private void cleanUp(ElementTree lastState, int type) { 161 boolean postChange = type == IResourceChangeEvent.POST_CHANGE; 164 if (postChange || type == IResourceChangeEvent.POST_BUILD) { 165 long id = workspace.getMarkerManager().getChangeId(); 166 lastState.immutable(); 167 if (postChange) { 168 lastPostChangeTree = lastState; 169 lastPostChangeId = id; 170 } else { 171 lastPostBuildTree = lastState; 172 lastPostBuildId = id; 173 } 174 workspace.getMarkerManager().resetMarkerDeltas(Math.min(lastPostBuildId, lastPostChangeId)); 175 lastDelta = null; 176 lastDeltaState = lastState; 177 } 178 } 179 180 182 public void broadcastChanges(IResourceChangeListener listener, int type, IResourceDelta delta) { 183 ResourceChangeListenerList.ListenerEntry[] entries; 184 entries = new ResourceChangeListenerList.ListenerEntry[] {new ResourceChangeListenerList.ListenerEntry(listener, type)}; 185 notify(entries, new ResourceChangeEvent(workspace, type, 0, delta), false); 186 } 187 188 191 public void endAvoidNotify() { 192 avoidNotify.remove(Thread.currentThread()); 193 } 194 195 198 public void requestNotify() { 199 if (isNotifying || avoidNotify.contains(Thread.currentThread())) 201 return; 202 long delay = Math.max(NOTIFICATION_DELAY, lastNotifyDuration * 10); 204 if (notifyJob.getState() == Job.NONE) 205 notifyJob.schedule(delay); 206 } 207 208 212 protected ResourceDelta getDelta(ElementTree tree, int type) { 213 long id = workspace.getMarkerManager().getChangeId(); 214 boolean postChange = type == IResourceChangeEvent.POST_CHANGE; 219 if (!postChange && lastDelta != null && !ElementTree.hasChanges(tree, lastDeltaState, ResourceComparator.getNotificationComparator(), true)) { 220 if (id != lastDeltaId) { 223 Map markerDeltas = workspace.getMarkerManager().getMarkerDeltas(lastPostBuildId); 224 lastDelta.updateMarkers(markerDeltas); 225 } 226 } else { 227 ElementTree oldTree = postChange ? lastPostChangeTree : lastPostBuildTree; 229 long markerId = postChange ? lastPostChangeId : lastPostBuildId; 230 lastDelta = ResourceDeltaFactory.computeDelta(workspace, oldTree, tree, Path.ROOT, markerId + 1); 231 } 232 lastDeltaState = tree; 234 lastDeltaId = id; 235 return lastDelta; 236 } 237 238 protected ResourceChangeListenerList.ListenerEntry[] getListeners() { 239 return listeners.getListeners(); 240 } 241 242 public void handleEvent(LifecycleEvent event) { 243 switch (event.kind) { 244 case LifecycleEvent.PRE_PROJECT_CLOSE : 245 if (!listeners.hasListenerFor(IResourceChangeEvent.PRE_CLOSE)) 246 return; 247 IProject project = (IProject) event.resource; 248 notify(getListeners(), new ResourceChangeEvent(workspace, IResourceChangeEvent.PRE_CLOSE, project), true); 249 break; 250 case LifecycleEvent.PRE_PROJECT_MOVE : 251 if (event.resource.equals(event.newResource)) 254 return; 255 case LifecycleEvent.PRE_PROJECT_DELETE : 257 if (!listeners.hasListenerFor(IResourceChangeEvent.PRE_DELETE)) 258 return; 259 project = (IProject) event.resource; 260 notify(getListeners(), new ResourceChangeEvent(workspace, IResourceChangeEvent.PRE_DELETE, project), true); 261 break; 262 } 263 } 264 265 private void notify(ResourceChangeListenerList.ListenerEntry[] resourceListeners, final IResourceChangeEvent event, final boolean lockTree) { 266 int type = event.getType(); 267 boolean oldLock = workspace.isTreeLocked(); 268 if (lockTree) 269 workspace.setTreeLocked(true); 270 try { 271 for (int i = 0; i < resourceListeners.length; i++) { 272 if ((type & resourceListeners[i].eventMask) != 0) { 273 final IResourceChangeListener listener = resourceListeners[i].listener; 274 if (ResourceStats.TRACE_LISTENERS) 275 ResourceStats.startNotify(listener); 276 SafeRunner.run(new ISafeRunnable() { 277 public void handleException(Throwable e) { 278 } 280 281 public void run() throws Exception { 282 listener.resourceChanged(event); 283 } 284 }); 285 if (ResourceStats.TRACE_LISTENERS) 286 ResourceStats.endNotify(); 287 } 288 } 289 } finally { 290 if (lockTree) 291 workspace.setTreeLocked(oldLock); 292 } 293 } 294 295 public void removeListener(IResourceChangeListener listener) { 296 listeners.remove(listener); 297 if (ResourceStats.TRACE_LISTENERS) 298 ResourceStats.listenerRemoved(listener); 299 } 300 301 306 public boolean shouldNotify() { 307 return !isNotifying && notificationRequested; 308 } 309 310 public void shutdown(IProgressMonitor monitor) { 311 listeners = new ResourceChangeListenerList(); 313 } 314 315 public void startup(IProgressMonitor monitor) { 316 lastPostBuildTree = lastPostChangeTree = workspace.getElementTree(); 320 workspace.addLifecycleListener(this); 321 } 322 } 323 | Popular Tags |