KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*******************************************************************************
2  * Copyright (c) 2000, 2005 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.*;
14
15 import org.eclipse.core.resources.IFile;
16 import org.eclipse.core.resources.IResource;
17 import org.eclipse.core.runtime.*;
18 import org.eclipse.core.runtime.jobs.Job;
19 import org.eclipse.core.runtime.preferences.*;
20 import org.eclipse.osgi.util.NLS;
21 import org.eclipse.team.core.ITeamStatus;
22 import org.eclipse.team.core.TeamException;
23 import org.eclipse.team.core.subscribers.Subscriber;
24 import org.eclipse.team.core.synchronize.*;
25 import org.eclipse.team.internal.core.*;
26 import org.osgi.service.prefs.BackingStoreException;
27 import org.osgi.service.prefs.Preferences;
28
29 /**
30  * This class manages the active change sets associated with a subscriber.
31  */

32 public class SubscriberChangeSetCollector extends ChangeSetCollector implements ISyncInfoSetChangeListener {
33     
34     private static final String JavaDoc PREF_CHANGE_SETS = "changeSets"; //$NON-NLS-1$
35
private static final String JavaDoc CTX_DEFAULT_SET = "defaultSet"; //$NON-NLS-1$
36

37     private static final int RESOURCE_REMOVAL = 1;
38     private static final int RESOURCE_CHANGE = 2;
39     
40     private ActiveChangeSet defaultSet;
41     private EventHandler handler;
42     private ResourceCollector collector;
43     
44     /*
45      * Background event handler for serializing and batching change set changes
46      */

47     private class EventHandler extends BackgroundEventHandler {
48
49         private List dispatchEvents = new ArrayList();
50         
51         protected EventHandler(String JavaDoc jobName, String JavaDoc errorTitle) {
52             super(jobName, errorTitle);
53         }
54
55         /* (non-Javadoc)
56          * @see org.eclipse.team.internal.core.BackgroundEventHandler#processEvent(org.eclipse.team.internal.core.BackgroundEventHandler.Event, org.eclipse.core.runtime.IProgressMonitor)
57          */

58         protected void processEvent(Event event, IProgressMonitor monitor) throws CoreException {
59             // Handle everything in the dispatch
60
if (isShutdown())
61                 throw new OperationCanceledException();
62             dispatchEvents.add(event);
63         }
64         
65         /* (non-Javadoc)
66          * @see org.eclipse.team.internal.core.BackgroundEventHandler#doDispatchEvents(org.eclipse.core.runtime.IProgressMonitor)
67          */

68         protected boolean doDispatchEvents(IProgressMonitor monitor) throws TeamException {
69             if (dispatchEvents.isEmpty()) {
70                 return false;
71             }
72             if (isShutdown())
73                 throw new OperationCanceledException();
74             SyncInfoTree[] locked = null;
75             try {
76                 locked = beginDispath();
77                 for (Iterator iter = dispatchEvents.iterator(); iter.hasNext();) {
78                     Event event = (Event) iter.next();
79                     switch (event.getType()) {
80                     case RESOURCE_REMOVAL:
81                         handleRemove(event.getResource());
82                         break;
83                     case RESOURCE_CHANGE:
84                         handleChange(event.getResource(), ((ResourceEvent)event).getDepth());
85                         break;
86                     default:
87                         break;
88                     }
89                     if (isShutdown())
90                         throw new OperationCanceledException();
91                 }
92             } finally {
93                 try {
94                     endDispatch(locked, monitor);
95                 } finally {
96                     dispatchEvents.clear();
97                 }
98             }
99             return true;
100         }
101
102         /*
103          * Begin input on all the sets and return the sync sets that were
104          * locked. If this method throws an exception then the client
105          * can assume that no sets were locked
106          */

107         private SyncInfoTree[] beginDispath() {
108             ChangeSet[] sets = getSets();
109             List lockedSets = new ArrayList();
110             try {
111                 for (int i = 0; i < sets.length; i++) {
112                     ChangeSet set = sets[i];
113                     SyncInfoTree syncInfoSet = set.getSyncInfoSet();
114                     lockedSets.add(syncInfoSet);
115                     syncInfoSet.beginInput();
116                 }
117                 return (SyncInfoTree[]) lockedSets.toArray(new SyncInfoTree[lockedSets.size()]);
118             } catch (RuntimeException JavaDoc e) {
119                 try {
120                     for (Iterator iter = lockedSets.iterator(); iter.hasNext();) {
121                         SyncInfoTree tree = (SyncInfoTree) iter.next();
122                         try {
123                             tree.endInput(null);
124                         } catch (Throwable JavaDoc e1) {
125                             // Ignore so that original exception is not masked
126
}
127                     }
128                 } catch (Throwable JavaDoc e1) {
129                     // Ignore so that original exception is not masked
130
}
131                 throw e;
132             }
133         }
134
135         private void endDispatch(SyncInfoTree[] locked, IProgressMonitor monitor) {
136             if (locked == null) {
137                 // The begin failed so there's nothing to unlock
138
return;
139             }
140             monitor.beginTask(null, 100 * locked.length);
141             for (int i = 0; i < locked.length; i++) {
142                 SyncInfoTree tree = locked[i];
143                 try {
144                     tree.endInput(Policy.subMonitorFor(monitor, 100));
145                 } catch (RuntimeException JavaDoc e) {
146                     // Don't worry about ending every set if an error occurs.
147
// Instead, log the error and suggest a restart.
148
TeamPlugin.log(IStatus.ERROR, Messages.SubscriberChangeSetCollector_0, e); //$NON-NLS-1$
149
throw e;
150                 }
151             }
152             monitor.done();
153         }
154         
155         /* (non-Javadoc)
156          * @see org.eclipse.team.internal.core.BackgroundEventHandler#queueEvent(org.eclipse.team.internal.core.BackgroundEventHandler.Event, boolean)
157          */

158         protected synchronized void queueEvent(Event event, boolean front) {
159             // Override to allow access from enclosing class
160
super.queueEvent(event, front);
161         }
162         
163         /*
164          * Handle the removal
165          */

166         private void handleRemove(IResource resource) {
167             ChangeSet[] sets = getSets();
168             for (int i = 0; i < sets.length; i++) {
169                 ChangeSet set = sets[i];
170                 // This will remove any descendants from the set and callback to
171
// resourcesChanged which will batch changes
172
if (!set.isEmpty()) {
173                     set.rootRemoved(resource, IResource.DEPTH_INFINITE);
174                     if (set.isEmpty()) {
175                         remove(set);
176                     }
177                 }
178             }
179         }
180         
181         /*
182          * Handle the change
183          */

184         private void handleChange(IResource resource, int depth) throws TeamException {
185             SyncInfo syncInfo = getSyncInfo(resource);
186             if (isModified(syncInfo)) {
187                 ActiveChangeSet[] containingSets = getContainingSets(resource);
188                 if (containingSets.length == 0) {
189                     // Consider for inclusion in the default set
190
// if the resource is not already a memebr of another set
191
if (defaultSet != null) {
192                         defaultSet.add(syncInfo);
193                      }
194                 } else {
195                     for (int i = 0; i < containingSets.length; i++) {
196                         ActiveChangeSet set = containingSets[i];
197                         // Update the sync info in the set
198
set.getSyncInfoSet().add(syncInfo);
199                     }
200                 }
201             } else {
202                 removeFromAllSets(resource);
203             }
204             if (depth != IResource.DEPTH_ZERO) {
205                 IResource[] members = getSubscriber().members(resource);
206                 for (int i = 0; i < members.length; i++) {
207                     IResource member = members[i];
208                     handleChange(member, depth == IResource.DEPTH_ONE ? IResource.DEPTH_ZERO : IResource.DEPTH_INFINITE);
209                 }
210             }
211         }
212         
213         private void removeFromAllSets(IResource resource) {
214             List toRemove = new ArrayList();
215             ChangeSet[] sets = getSets();
216             for (int i = 0; i < sets.length; i++) {
217                 ChangeSet set = sets[i];
218                 if (set.contains(resource)) {
219                     set.remove(resource);
220                     if (set.isEmpty()) {
221                         toRemove.add(set);
222                     }
223                 }
224             }
225             for (Iterator iter = toRemove.iterator(); iter.hasNext();) {
226                 ActiveChangeSet set = (ActiveChangeSet) iter.next();
227                 remove(set);
228             }
229         }
230
231         private ActiveChangeSet[] getContainingSets(IResource resource) {
232             Set result = new HashSet();
233             ChangeSet[] sets = getSets();
234             for (int i = 0; i < sets.length; i++) {
235                 ChangeSet set = sets[i];
236                 if (set.contains(resource)) {
237                     result.add(set);
238                 }
239             }
240             return (ActiveChangeSet[]) result.toArray(new ActiveChangeSet[result.size()]);
241         }
242     }
243     
244     private class ResourceCollector extends SubscriberResourceCollector {
245
246         public ResourceCollector(Subscriber subscriber) {
247             super(subscriber);
248         }
249
250         /* (non-Javadoc)
251          * @see org.eclipse.team.internal.core.subscribers.SubscriberResourceCollector#remove(org.eclipse.core.resources.IResource)
252          */

253         protected void remove(IResource resource) {
254             handler.queueEvent(new BackgroundEventHandler.ResourceEvent(resource, RESOURCE_REMOVAL, IResource.DEPTH_INFINITE), false);
255         }
256
257         /* (non-Javadoc)
258          * @see org.eclipse.team.internal.core.subscribers.SubscriberResourceCollector#change(org.eclipse.core.resources.IResource, int)
259          */

260         protected void change(IResource resource, int depth) {
261             handler.queueEvent(new BackgroundEventHandler.ResourceEvent(resource, RESOURCE_CHANGE, depth), false);
262         }
263         
264         protected boolean hasMembers(IResource resource) {
265             return SubscriberChangeSetCollector.this.hasMembers(resource);
266         }
267     }
268     
269     public SubscriberChangeSetCollector(Subscriber subscriber) {
270         collector = new ResourceCollector(subscriber);
271         load();
272         handler = new EventHandler(NLS.bind(Messages.SubscriberChangeSetCollector_1, new String JavaDoc[] { subscriber.getName() }), NLS.bind(Messages.SubscriberChangeSetCollector_2, new String JavaDoc[] { subscriber.getName() })); //$NON-NLS-1$ //$NON-NLS-2$
273
}
274     
275     public boolean hasMembers(IResource resource) {
276         ChangeSet[] sets = getSets();
277         for (int i = 0; i < sets.length; i++) {
278             ChangeSet set = sets[i];
279             if (set.getSyncInfoSet().hasMembers(resource));
280         }
281         if (defaultSet != null)
282             return (defaultSet.getSyncInfoSet().hasMembers(resource));
283         return false;
284     }
285
286     /**
287      * Add the active change set to this collector.
288      * @param set the active change set being added
289      */

290     public void add(ChangeSet set) {
291         Assert.isTrue(set instanceof ActiveChangeSet);
292         if (!contains(set)) {
293             super.add(set);
294             handleAddedResources(set, set.getSyncInfoSet().getSyncInfos());
295         }
296     }
297     
298     /**
299      * Return whether the manager allows a resource to
300      * be in mulitple sets. By default, a resource
301      * may only be in one set.
302      * @return whether the manager allows a resource to
303      * be in mulitple sets.
304      */

305     protected boolean isSingleSetPerResource() {
306         return true;
307     }
308     
309     /**
310      * Create a commit set with the given title and files. The created
311      * set is not added to the control of the commit set manager
312      * so no events are fired. The set can be added using the
313      * <code>add</code> method.
314      * @param title the title of the commit set
315      * @param infos the files contained in the set
316      * @return the created set
317      */

318     public ActiveChangeSet createSet(String JavaDoc title, SyncInfo[] infos) {
319         ActiveChangeSet commitSet = new ActiveChangeSet(this, title);
320         if (infos != null && infos.length > 0) {
321             commitSet.add(infos);
322         }
323         return commitSet;
324     }
325
326     /**
327      * Create a change set containing the given files if
328      * they have been modified locally.
329      * @param title the title of the commit set
330      * @param files the files contained in the set
331      * @return the created set
332      * @throws TeamException
333      */

334     public ActiveChangeSet createSet(String JavaDoc title, IFile[] files) throws TeamException {
335         List infos = new ArrayList();
336         for (int i = 0; i < files.length; i++) {
337             IFile file = files[i];
338             SyncInfo info = getSyncInfo(file);
339             if (info != null) {
340                 infos.add(info);
341             }
342         }
343         return createSet(title, (SyncInfo[]) infos.toArray(new SyncInfo[infos.size()]));
344     }
345
346     /**
347      * Make the given set the default set into which all new modifications
348      * that ae not already in another set go.
349      * @param set the set which is to become the default set
350      */

351     public void makeDefault(ActiveChangeSet set) {
352         // The default set must be an active set
353
if (!contains(set)) {
354             add(set);
355         }
356         ActiveChangeSet oldSet = defaultSet;
357         defaultSet = set;
358         fireDefaultChangedEvent(oldSet, defaultSet);
359     }
360
361     /**
362      * Retrn the set which is currently the default or
363      * <code>null</code> if there is no default set.
364      * @return the default change set
365      */

366     public ActiveChangeSet getDefaultSet() {
367         return defaultSet;
368     }
369     /**
370      * Return whether the given set is the default set into which all
371      * new modifications will be placed.
372      * @param set the set to test
373      * @return whether the set is the default set
374      */

375     public boolean isDefault(ActiveChangeSet set) {
376         return set == defaultSet;
377     }
378     
379     /**
380      * Return the sync info for the given resource obtained
381      * from the subscriber.
382      * @param resource the resource
383      * @return the sync info for the resource
384      * @throws TeamException
385      */

386     protected SyncInfo getSyncInfo(IResource resource) throws TeamException {
387         Subscriber subscriber = getSubscriber();
388         SyncInfo info = subscriber.getSyncInfo(resource);
389         return info;
390     }
391     
392     /**
393      * Return the subscriber associated with this collector.
394      * @return the subscriber associated with this collector
395      */

396     public Subscriber getSubscriber() {
397         return collector.getSubscriber();
398     }
399
400     protected boolean isModified(SyncInfo info) {
401         if (info != null) {
402             if (info.getComparator().isThreeWay()) {
403                 int dir = (info.getKind() & SyncInfo.DIRECTION_MASK);
404                 return dir == SyncInfo.OUTGOING || dir == SyncInfo.CONFLICTING;
405             } else {
406                 return (info.getKind() & SyncInfo.CHANGE_MASK) == SyncInfo.CHANGE;
407             }
408         }
409         return false;
410     }
411
412     /* (non-Javadoc)
413      * @see org.eclipse.team.internal.core.subscribers.SubscriberResourceCollector#dispose()
414      */

415     public void dispose() {
416         handler.shutdown();
417         collector.dispose();
418         super.dispose();
419         save();
420     }
421     
422     private void save() {
423         Preferences prefs = getPreferences();
424         // Clear the persisted state before saving the new state
425
try {
426             String JavaDoc[] oldSetNames = prefs.childrenNames();
427             for (int i = 0; i < oldSetNames.length; i++) {
428                 String JavaDoc string = oldSetNames[i];
429                 prefs.node(string).removeNode();
430             }
431         } catch (BackingStoreException e) {
432             TeamPlugin.log(IStatus.ERROR, NLS.bind("An error occurred purging the sommit set state for {0}", new String JavaDoc[] { getSubscriber().getName() }), e); //$NON-NLS-1$
433
}
434         ChangeSet[] sets = getSets();
435         for (int i = 0; i < sets.length; i++) {
436             ChangeSet set = sets[i];
437             if (set instanceof ActiveChangeSet && !set.isEmpty()) {
438                 Preferences child = prefs.node(((ActiveChangeSet)set).getTitle());
439                 ((ActiveChangeSet)set).save(child);
440             }
441         }
442         if (defaultSet != null) {
443             prefs.put(CTX_DEFAULT_SET, defaultSet.getTitle());
444         }
445         try {
446             prefs.flush();
447         } catch (BackingStoreException e) {
448             TeamPlugin.log(IStatus.ERROR, NLS.bind(Messages.SubscriberChangeSetCollector_3, new String JavaDoc[] { getSubscriber().getName() }), e); //$NON-NLS-1$
449
}
450     }
451     
452     private void load() {
453         Preferences prefs = getPreferences();
454         String JavaDoc defaultSetTitle = prefs.get(CTX_DEFAULT_SET, null);
455         try {
456             String JavaDoc[] childNames = prefs.childrenNames();
457             for (int i = 0; i < childNames.length; i++) {
458                 String JavaDoc string = childNames[i];
459                 Preferences childPrefs = prefs.node(string);
460                 ActiveChangeSet set = createSet(string, childPrefs);
461                 if (!set.isEmpty()) {
462                     if (defaultSet == null && defaultSetTitle != null && set.getTitle().equals(defaultSetTitle)) {
463                         defaultSet = set;
464                     }
465                     add(set);
466                 }
467             }
468         } catch (BackingStoreException e) {
469             TeamPlugin.log(IStatus.ERROR, NLS.bind(Messages.SubscriberChangeSetCollector_4, new String JavaDoc[] { getSubscriber().getName() }), e); //$NON-NLS-1$
470
}
471     }
472
473     /**
474      * Create a change set from the given preferences that were
475      * previously saved.
476      * @param childPrefs the previously saved preferences
477      * @return the created change set
478      */

479     protected ActiveChangeSet createSet(String JavaDoc title, Preferences childPrefs) {
480         ActiveChangeSet changeSet = new ActiveChangeSet(this, title);
481         changeSet.init(childPrefs);
482         return changeSet;
483     }
484
485     private Preferences getPreferences() {
486         return getParentPreferences().node(getSubscriberIdentifier());
487     }
488     
489     private static Preferences getParentPreferences() {
490         return getTeamPreferences().node(PREF_CHANGE_SETS);
491     }
492     
493     private static Preferences getTeamPreferences() {
494         return new InstanceScope().getNode(TeamPlugin.getPlugin().getBundle().getSymbolicName());
495     }
496     
497     /**
498      * Return the id that will uniquely identify the subscriber across
499      * restarts.
500      * @return the id that will uniquely identify the subscriber across
501      */

502     protected String JavaDoc getSubscriberIdentifier() {
503         return getSubscriber().getName();
504     }
505
506     /* (non-Javadoc)
507      * @see org.eclipse.team.core.synchronize.ISyncInfoSetChangeListener#syncInfoSetReset(org.eclipse.team.core.synchronize.SyncInfoSet, org.eclipse.core.runtime.IProgressMonitor)
508      */

509     public void syncInfoSetReset(SyncInfoSet set, IProgressMonitor monitor) {
510         handleSyncSetChange(set, set.getSyncInfos(), set.getResources());
511     }
512
513     /* (non-Javadoc)
514      * @see org.eclipse.team.core.synchronize.ISyncInfoSetChangeListener#syncInfoChanged(org.eclipse.team.core.synchronize.ISyncInfoSetChangeEvent, org.eclipse.core.runtime.IProgressMonitor)
515      */

516     public void syncInfoChanged(ISyncInfoSetChangeEvent event, IProgressMonitor monitor) {
517         SyncInfoSet set = event.getSet();
518         handleSyncSetChange(set, event.getAddedResources(), getAllResources(event));
519     }
520
521     private IResource[] getAllResources(ISyncInfoSetChangeEvent event) {
522         Set allResources = new HashSet();
523         SyncInfo[] addedResources = event.getAddedResources();
524         for (int i = 0; i < addedResources.length; i++) {
525             SyncInfo info = addedResources[i];
526             allResources.add(info.getLocal());
527         }
528         SyncInfo[] changedResources = event.getChangedResources();
529         for (int i = 0; i < changedResources.length; i++) {
530             SyncInfo info = changedResources[i];
531             allResources.add(info.getLocal());
532         }
533         allResources.addAll(Arrays.asList(event.getRemovedResources()));
534         return (IResource[]) allResources.toArray(new IResource[allResources.size()]);
535     }
536
537     private void handleAddedResources(ChangeSet set, SyncInfo[] infos) {
538         if (isSingleSetPerResource()) {
539             IResource[] resources = new IResource[infos.length];
540             for (int i = 0; i < infos.length; i++) {
541                 resources[i] = infos[i].getLocal();
542             }
543             // Remove the added files from any other set that contains them
544
ChangeSet[] sets = getSets();
545             for (int i = 0; i < sets.length; i++) {
546                 ChangeSet otherSet = sets[i];
547                 if (otherSet != set) {
548                     otherSet.remove(resources);
549                 }
550             }
551         }
552     }
553     
554     private void handleSyncSetChange(SyncInfoSet set, SyncInfo[] addedInfos, IResource[] allAffectedResources) {
555         ChangeSet changeSet = getChangeSet(set);
556         if (set.isEmpty() && changeSet != null) {
557             remove(changeSet);
558         }
559         fireResourcesChangedEvent(changeSet, allAffectedResources);
560         handleAddedResources(changeSet, addedInfos);
561     }
562
563     /* (non-Javadoc)
564      * @see org.eclipse.team.core.synchronize.ISyncInfoSetChangeListener#syncInfoSetErrors(org.eclipse.team.core.synchronize.SyncInfoSet, org.eclipse.team.core.ITeamStatus[], org.eclipse.core.runtime.IProgressMonitor)
565      */

566     public void syncInfoSetErrors(SyncInfoSet set, ITeamStatus[] errors, IProgressMonitor monitor) {
567         // Nothing to do
568
}
569
570     /* (non-Javadoc)
571      * @see org.eclipse.team.core.subscribers.ChangeSetCollector#getChangeSetSyncSetChangeListener()
572      */

573     protected ISyncInfoSetChangeListener getChangeSetChangeListener() {
574         return this;
575     }
576
577     /**
578      * Wait until the collector is done processing any events.
579      * This method is for testing purposes only.
580      */

581     public void waitUntilDone(IProgressMonitor monitor) {
582         monitor.worked(1);
583         // wait for the event handler to process changes.
584
while(handler.getEventHandlerJob().getState() != Job.NONE) {
585             monitor.worked(1);
586             try {
587                 Thread.sleep(10);
588             } catch (InterruptedException JavaDoc e) {
589             }
590             Policy.checkCanceled(monitor);
591         }
592         monitor.worked(1);
593     }
594 }
595
Popular Tags