KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > team > internal > core > subscribers > SubscriberEventHandler


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.team.internal.core.subscribers;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.List JavaDoc;
15
16 import org.eclipse.core.resources.IResource;
17 import org.eclipse.core.resources.IWorkspaceRunnable;
18 import org.eclipse.core.resources.mapping.ResourceMapping;
19 import org.eclipse.core.resources.mapping.ResourceTraversal;
20 import org.eclipse.core.runtime.*;
21 import org.eclipse.core.runtime.jobs.IJobChangeEvent;
22 import org.eclipse.core.runtime.jobs.Job;
23 import org.eclipse.osgi.util.NLS;
24 import org.eclipse.team.core.ITeamStatus;
25 import org.eclipse.team.core.TeamException;
26 import org.eclipse.team.core.mapping.ISynchronizationScope;
27 import org.eclipse.team.core.mapping.ISynchronizationScopeChangeListener;
28 import org.eclipse.team.core.subscribers.Subscriber;
29 import org.eclipse.team.internal.core.*;
30
31 /**
32  * This handler collects changes and removals to resources and calculates their
33  * synchronization state in a background job. The result is fed input the SyncSetInput.
34  *
35  * Exceptions that occur when the job is processing the events are collected and
36  * returned as part of the Job's status.
37  */

38 public abstract class SubscriberEventHandler extends BackgroundEventHandler {
39
40     // Changes accumulated by the event handler
41
private List JavaDoc resultCache = new ArrayList JavaDoc();
42     
43     private boolean started = false;
44     private boolean initializing = true;
45
46     private IProgressMonitor progressGroup;
47
48     private int ticks;
49
50     private final Subscriber subscriber;
51     private ISynchronizationScope scope;
52
53     private ISynchronizationScopeChangeListener scopeChangeListener;
54     
55     /**
56      * Internal resource synchronization event. Can contain a result.
57      */

58     class SubscriberEvent extends ResourceEvent{
59         static final int REMOVAL = 1;
60         static final int CHANGE = 2;
61         static final int INITIALIZE = 3;
62
63         SubscriberEvent(IResource resource, int type, int depth) {
64             super(resource, type, depth);
65         }
66         protected String JavaDoc getTypeString() {
67             switch (getType()) {
68                 case REMOVAL :
69                     return "REMOVAL"; //$NON-NLS-1$
70
case CHANGE :
71                     return "CHANGE"; //$NON-NLS-1$
72
case INITIALIZE :
73                     return "INITIALIZE"; //$NON-NLS-1$
74
default :
75                     return "INVALID"; //$NON-NLS-1$
76
}
77         }
78         public ResourceTraversal asTraversal() {
79             return new ResourceTraversal(new IResource[] { getResource() }, getDepth(), IResource.NONE);
80         }
81     }
82     
83     /**
84      * Create a handler. This will initialize all resources for the subscriber associated with
85      * the set.
86      * @param subscriber the subscriber
87      * @param scope the scope
88      */

89     public SubscriberEventHandler(Subscriber subscriber, ISynchronizationScope scope) {
90         super(
91             NLS.bind(Messages.SubscriberEventHandler_jobName, new String JavaDoc[] { subscriber.getName() }),
92             NLS.bind(Messages.SubscriberEventHandler_errors, new String JavaDoc[] { subscriber.getName() }));
93         this.subscriber = subscriber;
94         this.scope = scope;
95         scopeChangeListener = new ISynchronizationScopeChangeListener() {
96             public void scopeChanged(ISynchronizationScope scope,
97                     ResourceMapping[] newMappings,
98                     ResourceTraversal[] newTraversals) {
99                 reset(new ResourceTraversal[0], scope.getTraversals());
100             }
101         };
102         this.scope.addScopeChangeListener(scopeChangeListener);
103     }
104     
105     /**
106      * The traversals of the scope have changed
107      * @param oldTraversals the old traversals
108      * @param newTraversals the new traversals
109      */

110     protected synchronized void reset(ResourceTraversal[] oldTraversals, ResourceTraversal[] newTraversals) {
111         reset(newTraversals, SubscriberEvent.CHANGE);
112     }
113
114     /**
115      * Start the event handler by queuing events to prime the sync set input with the out-of-sync
116      * resources of the subscriber.
117      */

118     public synchronized void start() {
119         // Set the started flag to enable event queuing.
120
// We are guaranteed to be the first since this method is synchronized.
121
started = true;
122         ResourceTraversal[] traversals = scope.getTraversals();
123         reset(traversals, SubscriberEvent.INITIALIZE);
124         initializing = false;
125     }
126
127     protected synchronized void queueEvent(Event event, boolean front) {
128         // Only post events if the handler is started
129
if (started) {
130             super.queueEvent(event, front);
131         }
132     }
133     /**
134      * Schedule the job or process the events now.
135      */

136     public void schedule() {
137         Job job = getEventHandlerJob();
138         if (job.getState() == Job.NONE) {
139             if(progressGroup != null) {
140                 job.setSystem(false);
141                 job.setProgressGroup(progressGroup, ticks);
142             } else {
143                 job.setSystem(isSystemJob());
144             }
145         }
146         getEventHandlerJob().schedule();
147     }
148
149     protected boolean isSystemJob() {
150         return !initializing;
151     }
152     
153     
154     /* (non-Javadoc)
155      * @see org.eclipse.team.internal.core.BackgroundEventHandler#jobDone(org.eclipse.core.runtime.jobs.IJobChangeEvent)
156      */

157     protected void jobDone(IJobChangeEvent event) {
158         super.jobDone(event);
159         progressGroup = null;
160     }
161     
162     /**
163      * Called by a client to indicate that a resource has changed and its synchronization state
164      * should be recalculated.
165      * @param resource the changed resource
166      * @param depth the depth of the change calculation
167      */

168     public void change(IResource resource, int depth) {
169         queueEvent(new SubscriberEvent(resource, SubscriberEvent.CHANGE, depth), false);
170     }
171     
172     /**
173      * Called by a client to indicate that a resource has been removed and should be removed. The
174      * removal will propagate to the set.
175      * @param resource the resource that was removed
176      */

177     public void remove(IResource resource) {
178         queueEvent(
179             new SubscriberEvent(resource, SubscriberEvent.REMOVAL, IResource.DEPTH_INFINITE), false);
180     }
181     
182     /**
183      * Collect the calculated synchronization information for the given resource at the given depth. The
184      * results are added to the provided list.
185      */

186     private void collect(
187         IResource resource,
188         int depth,
189         IProgressMonitor monitor) {
190         
191         Policy.checkCanceled(monitor);
192         
193         // handle any preemptive events before continuing
194
handlePreemptiveEvents(monitor);
195         
196         if (resource.getType() != IResource.FILE
197             && depth != IResource.DEPTH_ZERO) {
198             try {
199                 IResource[] members =
200                     getSubscriber().members(resource);
201                 for (int i = 0; i < members.length; i++) {
202                     collect(
203                         members[i],
204                         depth == IResource.DEPTH_INFINITE
205                             ? IResource.DEPTH_INFINITE
206                             : IResource.DEPTH_ZERO,
207                         monitor);
208                 }
209             } catch (TeamException e) {
210                 // We only handle the exception if the resource's project is accessible.
211
// The project close delta will clean up.
212
if (resource.getProject().isAccessible())
213                     handleException(e, resource, ITeamStatus.SYNC_INFO_SET_ERROR, NLS.bind(Messages.SubscriberEventHandler_8, new String JavaDoc[] { resource.getFullPath().toString(), e.getMessage() }));
214             }
215         }
216
217         monitor.subTask(NLS.bind(Messages.SubscriberEventHandler_2, new String JavaDoc[] { resource.getFullPath().toString() }));
218         try {
219             handleChange(resource);
220             handlePendingDispatch(monitor);
221         } catch (CoreException e) {
222             handleException(e, resource, ITeamStatus.RESOURCE_SYNC_INFO_ERROR, NLS.bind(Messages.SubscriberEventHandler_9, new String JavaDoc[] { resource.getFullPath().toString(), e.getMessage() }));
223         }
224         monitor.worked(1);
225     }
226
227     /**
228      * Return the subscriber associated with this event handler
229      * @return the subscriber associated with this event handler
230      */

231     protected Subscriber getSubscriber() {
232         return subscriber;
233     }
234
235     /**
236      * The given resource has changed. Subclasses should handle
237      * this in an appropriate fashion
238      * @param resource the resource whose state has changed
239      */

240     protected abstract void handleChange(IResource resource) throws CoreException;
241
242     protected void handlePendingDispatch(IProgressMonitor monitor) {
243         if (isReadyForDispatch(false /*don't wait if queue is empty*/)) {
244             try {
245                 dispatchEvents(Policy.subMonitorFor(monitor, 5));
246             } catch (TeamException e) {
247                 handleException(e, null, ITeamStatus.SYNC_INFO_SET_ERROR, e.getMessage());
248             }
249         }
250     }
251
252     /**
253      * Handle the exception by returning it as a status from the job but also by
254      * dispatching it to the sync set input so any down stream views can react
255      * accordingly.
256      * The resource passed may be null.
257      */

258     protected void handleException(CoreException e, IResource resource, int code, String JavaDoc message) {
259         handleException(e);
260     }
261
262     /**
263      * Called to initialize to calculate the synchronization information using the optimized subscriber method. For
264      * subscribers that don't support the optimization, all resources in the subscriber are manually re-calculated.
265      * @param resource the resources to check
266      * @param depth the depth
267      * @param monitor
268      */

269     protected abstract void collectAll(
270         IResource resource,
271         int depth,
272         IProgressMonitor monitor);
273
274     /**
275      * Feed the given events to the set. The appropriate method on the set is called
276      * for each event type.
277      * @param events
278      */

279     protected abstract void dispatchEvents(SubscriberEvent[] events, IProgressMonitor monitor);
280     
281     /**
282      * Initialize all resources for the subscriber associated with the set. This will basically recalculate
283      * all synchronization information for the subscriber.
284      * @param type can be Event.CHANGE to recalculate all states or Event.INITIALIZE to perform the
285      * optimized recalculation if supported by the subscriber.
286      */

287     protected void reset(ResourceTraversal[] traversals, int type) {
288         for (int i = 0; i < traversals.length; i++) {
289             ResourceTraversal traversal = traversals[i];
290             IResource[] resources = traversal.getResources();
291             for (int j = 0; j < resources.length; j++) {
292                 queueEvent(new SubscriberEvent(resources[j], type, traversal.getDepth()), false);
293             }
294         }
295     }
296
297     protected void processEvent(Event event, IProgressMonitor monitor) {
298         try {
299             // Cancellation is dangerous because this will leave the sync info in a bad state.
300
// Purposely not checking -
301
int type = event.getType();
302             switch (type) {
303                 case BackgroundEventHandler.RUNNABLE_EVENT :
304                     executeRunnable(event, monitor);
305                     break;
306                 case SubscriberEvent.REMOVAL :
307                     queueDispatchEvent(event);
308                     break;
309                 case SubscriberEvent.CHANGE :
310                     collect(
311                         event.getResource(),
312                         ((ResourceEvent)event).getDepth(),
313                         monitor);
314                     break;
315                 case SubscriberEvent.INITIALIZE :
316                     monitor.subTask(NLS.bind(Messages.SubscriberEventHandler_2, new String JavaDoc[] { event.getResource().getFullPath().toString() }));
317                     collectAll(
318                             event.getResource(),
319                             ((ResourceEvent)event).getDepth(),
320                             Policy.subMonitorFor(monitor, 64));
321                     break;
322             }
323         } catch (OperationCanceledException e) {
324             // the job has been canceled.
325
// Clear the queue and propagate the cancellation through the sets.
326
handleCancel(e);
327         } catch (RuntimeException JavaDoc e) {
328             // handle the exception and keep processing
329
if (event.getType() == BackgroundEventHandler.RUNNABLE_EVENT ) {
330                 handleException(new TeamException(Messages.SubscriberEventHandler_10, e));
331             } else {
332                 handleException(new TeamException(Messages.SubscriberEventHandler_10, e), event.getResource(), ITeamStatus.SYNC_INFO_SET_ERROR, NLS.bind(Messages.SubscriberEventHandler_11, new String JavaDoc[] { event.getResource().getFullPath().toString(), e.getMessage() }));
333             }
334         }
335     }
336
337     /**
338      * Queue the event to be handle during the dispatch phase.
339      * @param event the event
340      */

341     protected void queueDispatchEvent(Event event) {
342         resultCache.add(event);
343     }
344
345     /**
346      * Handle the cancel exception
347      * @param e the cancel exception
348      */

349     protected void handleCancel(OperationCanceledException e) {
350         resultCache.clear();
351     }
352         
353     /*
354      * Execute the RunnableEvent
355      */

356     private void executeRunnable(Event event, IProgressMonitor monitor) {
357         try {
358             // Dispatch any queued results to clear pending output events
359
dispatchEvents(Policy.subMonitorFor(monitor, 1));
360         } catch (TeamException e) {
361             handleException(e, null, ITeamStatus.SYNC_INFO_SET_ERROR, e.getMessage());
362         }
363         try {
364             ((RunnableEvent)event).run(Policy.subMonitorFor(monitor, 1));
365         } catch (CoreException e) {
366             handleException(e, null, ITeamStatus.SYNC_INFO_SET_ERROR, e.getMessage());
367         }
368     }
369
370     /* (non-Javadoc)
371      * @see org.eclipse.team.core.subscribers.BackgroundEventHandler#dispatchEvents()
372      */

373     protected boolean doDispatchEvents(IProgressMonitor monitor) {
374         if (!resultCache.isEmpty()) {
375             dispatchEvents((SubscriberEvent[]) resultCache.toArray(new SubscriberEvent[resultCache.size()]), monitor);
376             resultCache.clear();
377             return true;
378         }
379         return false;
380     }
381
382     /**
383      * Queue up the given runnable in an event to be processed by this job
384      * @param runnable the runnable to be run by the handler
385      */

386     public void run(IWorkspaceRunnable runnable, boolean frontOnQueue) {
387         queueEvent(new RunnableEvent(runnable, frontOnQueue), frontOnQueue);
388     }
389     
390     public void setProgressGroupHint(IProgressMonitor progressGroup, int ticks) {
391         this.progressGroup = progressGroup;
392         this.ticks = ticks;
393     }
394     
395     protected void handlePreemptiveEvents(IProgressMonitor monitor) {
396         Event event = peek();
397         if (event instanceof RunnableEvent && ((RunnableEvent)event).isPreemtive()) {
398             executeRunnable(nextElement(), monitor);
399         }
400     }
401
402     /**
403      * Return the scope of this event handler. The scope is
404      * used to determine the resources that are processed by the handler
405      * @return the scope of this event handler
406      */

407     protected ISynchronizationScope getScope() {
408         return scope;
409     }
410     
411     /* (non-Javadoc)
412      * @see org.eclipse.team.internal.core.BackgroundEventHandler#shutdown()
413      */

414     public void shutdown() {
415         super.shutdown();
416         scope.removeScopeChangeListener(scopeChangeListener);
417     }
418 }
419
Popular Tags