KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > team > internal > ccvs > core > CVSMergeSubscriber


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.ccvs.core;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.Arrays JavaDoc;
15 import java.util.List JavaDoc;
16
17 import org.eclipse.core.resources.*;
18 import org.eclipse.core.runtime.*;
19 import org.eclipse.osgi.util.NLS;
20 import org.eclipse.team.core.RepositoryProvider;
21 import org.eclipse.team.core.TeamException;
22 import org.eclipse.team.core.subscribers.*;
23 import org.eclipse.team.core.synchronize.SyncInfo;
24 import org.eclipse.team.core.synchronize.SyncInfoFilter;
25 import org.eclipse.team.core.variants.*;
26 import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
27 import org.eclipse.team.internal.ccvs.core.resources.RemoteFile;
28 import org.eclipse.team.internal.ccvs.core.syncinfo.CVSResourceVariantTree;
29 import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
30 import org.eclipse.team.internal.ccvs.core.util.Util;
31
32 /**
33  * A CVSMergeSubscriber is responsible for maintaining the remote trees for a merge into
34  * the workspace. The remote trees represent the CVS revisions of the start and end
35  * points (version or branch) of the merge.
36  *
37  * This subscriber stores the remote handles in the resource tree sync info slot. When
38  * the merge is cancelled this sync info is cleared.
39  *
40  * A merge can persist between workbench sessions and thus can be used as an
41  * ongoing merge.
42  *
43  * TODO: Is the merge subscriber interested in workspace sync info changes?
44  * TODO: Do certain operations (e.g. replace with) invalidate a merge subscriber?
45  * TODO: How to ensure that sync info is flushed when merge roots are deleted?
46  */

47 public class CVSMergeSubscriber extends CVSSyncTreeSubscriber implements IResourceChangeListener, ISubscriberChangeListener {
48
49     private final class MergeBaseTree extends CVSResourceVariantTree {
50         // The merge synchronizer has been kept so that those upgrading
51
// from 3.0 M8 to 3.0 M9 so not lose there ongoing merge state
52
private PersistantResourceVariantByteStore mergedSynchronizer;
53         private MergeBaseTree(ResourceVariantByteStore cache, CVSTag tag, boolean cacheFileContentsHint, String JavaDoc syncKeyPrefix) {
54             super(cache, tag, cacheFileContentsHint);
55             mergedSynchronizer = new PersistantResourceVariantByteStore(new QualifiedName(SYNC_KEY_QUALIFIER, syncKeyPrefix + "0merged")); //$NON-NLS-1$
56
}
57         public IResource[] refresh(IResource[] resources, int depth, IProgressMonitor monitor) throws TeamException {
58             // Only refresh the base of a resource once as it should not change
59
List JavaDoc unrefreshed = new ArrayList JavaDoc();
60             for (int i = 0; i < resources.length; i++) {
61                 IResource resource = resources[i];
62                 if (!hasResourceVariant(resource)) {
63                     unrefreshed.add(resource);
64                 }
65             }
66             if (unrefreshed.isEmpty()) {
67                 monitor.done();
68                 return new IResource[0];
69             }
70             IResource[] refreshed = super.refresh((IResource[]) unrefreshed.toArray(new IResource[unrefreshed.size()]), depth, monitor);
71             return refreshed;
72         }
73         public IResourceVariant getResourceVariant(IResource resource) throws TeamException {
74             // Use the merged bytes for the base if there are some
75
byte[] mergedBytes = mergedSynchronizer.getBytes(resource);
76             if (mergedBytes != null) {
77                 byte[] parentBytes = getByteStore().getBytes(resource.getParent());
78                 if (parentBytes != null) {
79                     return RemoteFile.fromBytes(resource, mergedBytes, parentBytes);
80                 }
81             }
82             return super.getResourceVariant(resource);
83         }
84         
85         /**
86          * Mark the resource as merged by making it's base equal the remote
87          */

88         public void merged(IResource resource, byte[] remoteBytes) throws TeamException {
89             if (remoteBytes == null) {
90                 getByteStore().deleteBytes(resource);
91             } else {
92                 getByteStore().setBytes(resource, remoteBytes);
93             }
94         }
95         
96         /**
97          * Return true if the remote has already been merged
98          * (i.e. the base equals the remote).
99          */

100         public boolean isMerged(IResource resource, byte[] remoteBytes) throws TeamException {
101             byte[] mergedBytes = getByteStore().getBytes(resource);
102             return Util.equals(mergedBytes, remoteBytes);
103         }
104         
105         /* (non-Javadoc)
106          * @see org.eclipse.team.internal.ccvs.core.syncinfo.CVSResourceVariantTree#dispose()
107          */

108         public void dispose() {
109             mergedSynchronizer.dispose();
110             super.dispose();
111         }
112     }
113
114     public static final String JavaDoc ID = "org.eclipse.team.cvs.ui.cvsmerge-participant"; //$NON-NLS-1$
115
public static final String JavaDoc ID_MODAL = "org.eclipse.team.cvs.ui.cvsmerge-participant-modal"; //$NON-NLS-1$
116
private static final String JavaDoc UNIQUE_ID_PREFIX = "merge-"; //$NON-NLS-1$
117

118     private CVSTag start, end;
119     private List JavaDoc roots;
120     private CVSResourceVariantTree remoteTree;
121     private MergeBaseTree baseTree;
122     
123     public CVSMergeSubscriber(IResource[] roots, CVSTag start, CVSTag end) {
124         this(getUniqueId(), roots, start, end);
125     }
126
127     private static QualifiedName getUniqueId() {
128         String JavaDoc uniqueId = Long.toString(System.currentTimeMillis());
129         return new QualifiedName(ID, "CVS" + UNIQUE_ID_PREFIX + uniqueId); //$NON-NLS-1$
130
}
131     
132     public CVSMergeSubscriber(QualifiedName id, IResource[] roots, CVSTag start, CVSTag end) {
133         super(id, NLS.bind(CVSMessages.CVSMergeSubscriber_2, new String JavaDoc[] { start.getName(), end.getName() }));
134         this.start = start;
135         this.end = end;
136         this.roots = new ArrayList JavaDoc(Arrays.asList(roots));
137         initialize();
138     }
139
140     /* (non-Javadoc)
141      * @see org.eclipse.team.internal.ccvs.core.CVSWorkspaceSubscriber#initialize()
142      */

143     private void initialize() {
144         QualifiedName id = getId();
145         String JavaDoc syncKeyPrefix = id.getLocalName();
146         PersistantResourceVariantByteStore remoteSynchronizer = new PersistantResourceVariantByteStore(new QualifiedName(SYNC_KEY_QUALIFIER, syncKeyPrefix + end.getName()));
147         remoteTree = new CVSResourceVariantTree(remoteSynchronizer, getEndTag(), getCacheFileContentsHint()) {
148             public IResource[] refresh(IResource[] resources, int depth, IProgressMonitor monitor) throws TeamException {
149                 // Override refresh to compare file contents
150
monitor.beginTask(null, 100);
151                 try {
152                     IResource[] refreshed = super.refresh(resources, depth, monitor);
153                     compareWithRemote(refreshed, Policy.subMonitorFor(monitor, 50));
154                     return refreshed;
155                 } finally {
156                     monitor.done();
157                 }
158             }
159         };
160         PersistantResourceVariantByteStore baseSynchronizer = new PersistantResourceVariantByteStore(new QualifiedName(SYNC_KEY_QUALIFIER, syncKeyPrefix + start.getName()));
161         baseTree = new MergeBaseTree(baseSynchronizer, getStartTag(), getCacheFileContentsHint(), syncKeyPrefix);
162         
163         ResourcesPlugin.getWorkspace().addResourceChangeListener(this);
164         CVSProviderPlugin.getPlugin().getCVSWorkspaceSubscriber().addListener(this);
165     }
166
167     protected SyncInfo getSyncInfo(IResource local, IResourceVariant base, IResourceVariant remote) throws TeamException {
168         CVSMergeSyncInfo info = new CVSMergeSyncInfo(local, base, remote, this);
169         info.init();
170         return info;
171     }
172
173     public void merged(IResource[] resources) throws TeamException {
174         for (int i = 0; i < resources.length; i++) {
175             IResource resource = resources[i];
176             internalMerged(resource);
177         }
178         fireTeamResourceChange(SubscriberChangeEvent.asSyncChangedDeltas(this, resources));
179     }
180     
181     private void internalMerged(IResource resource) throws TeamException {
182         byte[] remoteBytes = getRemoteByteStore().getBytes(resource);
183         baseTree.merged(resource, remoteBytes);
184     }
185
186     /* (non-Javadoc)
187      * @see org.eclipse.team.core.sync.TeamSubscriber#cancel()
188      */

189     public void cancel() {
190         ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
191         remoteTree.dispose();
192         baseTree.dispose();
193     }
194
195     /* (non-Javadoc)
196      * @see org.eclipse.team.core.sync.TeamSubscriber#roots()
197      */

198     public IResource[] roots() {
199         return (IResource[]) roots.toArray(new IResource[roots.size()]);
200     }
201
202     /* (non-Javadoc)
203      * @see org.eclipse.team.core.sync.TeamSubscriber#isSupervised(org.eclipse.core.resources.IResource)
204      */

205     public boolean isSupervised(IResource resource) throws TeamException {
206         return getBaseTree().hasResourceVariant(resource) || getRemoteTree().hasResourceVariant(resource);
207     }
208
209     public CVSTag getStartTag() {
210         return start;
211     }
212     
213     public CVSTag getEndTag() {
214         return end;
215     }
216
217     /*
218      * What to do when a root resource for this merge changes?
219      * Deleted, Move, Copied
220      * Changed in a CVS way (tag changed, revision changed...)
221      * Contents changed by user
222      * @see IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent)
223      */

224     public void resourceChanged(IResourceChangeEvent event) {
225         try {
226             IResourceDelta delta = event.getDelta();
227             if(delta != null) {
228                 delta.accept(new IResourceDeltaVisitor() {
229                 public boolean visit(IResourceDelta delta) throws CoreException {
230                     IResource resource = delta.getResource();
231             
232                     if (resource.getType()==IResource.PROJECT) {
233                         IProject project = (IProject)resource;
234                         if (!project.isAccessible()) {
235                             return false;
236                         }
237                         if ((delta.getFlags() & IResourceDelta.OPEN) != 0) {
238                             return false;
239                         }
240                         if (RepositoryProvider.getProvider(project, CVSProviderPlugin.getTypeId()) == null) {
241                             return false;
242                         }
243                     }
244             
245                     if (roots.contains(resource)) {
246                         if (delta.getKind() == IResourceDelta.REMOVED || delta.getKind() == IResourceDelta.MOVED_TO) {
247                             cancel();
248                         }
249                         // stop visiting children
250
return false;
251                     }
252                     // keep visiting children
253
return true;
254                 }
255             });
256             }
257         } catch (CoreException e) {
258             CVSProviderPlugin.log(e.getStatus());
259         }
260     }
261
262     /**
263      * Return whether the given resource has been merged with its
264      * corresponding remote.
265      * @param resource the local resource
266      * @return boolean
267      * @throws TeamException
268      */

269     public boolean isMerged(IResource resource) throws TeamException {
270         byte[] remoteBytes = getRemoteByteStore().getBytes(resource);
271         return baseTree.isMerged(resource, remoteBytes);
272     }
273
274     /*
275      * Currently only the workspace subscriber knows when a project has been deconfigured. We will listen for these events
276      * and remove the root then forward to merge subscriber listeners.
277      * (non-Javadoc)
278      * @see org.eclipse.team.core.subscribers.ITeamResourceChangeListener#teamResourceChanged(org.eclipse.team.core.subscribers.TeamDelta[])
279      */

280     public void subscriberResourceChanged(ISubscriberChangeEvent[] deltas) {
281         for (int i = 0; i < deltas.length; i++) {
282             ISubscriberChangeEvent delta = deltas[i];
283             switch(delta.getFlags()) {
284                 case ISubscriberChangeEvent.ROOT_REMOVED:
285                     IResource resource = delta.getResource();
286                     if(roots.remove(resource)) {
287                         fireTeamResourceChange(new ISubscriberChangeEvent[] {delta});
288                     }
289                     break;
290             }
291         }
292     }
293
294     /* (non-Javadoc)
295      * @see org.eclipse.team.internal.ccvs.core.CVSSyncTreeSubscriber#getBaseSynchronizationCache()
296      */

297     protected IResourceVariantTree getBaseTree() {
298         return baseTree;
299     }
300
301     /* (non-Javadoc)
302      * @see org.eclipse.team.internal.ccvs.core.CVSSyncTreeSubscriber#getRemoteSynchronizationCache()
303      */

304     protected IResourceVariantTree getRemoteTree() {
305         return remoteTree;
306     }
307     
308     protected boolean getCacheFileContentsHint() {
309         return true;
310     }
311
312     /*
313      * Mark as merged any local resources whose contents match that of the remote resource.
314      */

315     private void compareWithRemote(IResource[] refreshed, IProgressMonitor monitor) throws CVSException, TeamException {
316         // For any remote changes, if the revision differs from the local, compare the contents.
317
if (refreshed.length == 0) return;
318         SyncInfoFilter.ContentComparisonSyncInfoFilter contentFilter =
319             new SyncInfoFilter.ContentComparisonSyncInfoFilter();
320         monitor.beginTask(null, refreshed.length * 100);
321         for (int i = 0; i < refreshed.length; i++) {
322             IResource resource = refreshed[i];
323             if (resource.getType() == IResource.FILE) {
324                 ICVSFile local = CVSWorkspaceRoot.getCVSFileFor((IFile)resource);
325                 byte[] localBytes = local.getSyncBytes();
326                 byte[] remoteBytes = getRemoteByteStore().getBytes(resource);
327                 if (remoteBytes != null
328                         && localBytes != null
329                         && local.exists()
330                         && !ResourceSyncInfo.getRevision(remoteBytes).equals(ResourceSyncInfo.getRevision(localBytes))
331                         && contentFilter.select(getSyncInfo(resource), Policy.subMonitorFor(monitor, 100))) {
332                     // The contents are equals so mark the file as merged
333
internalMerged(resource);
334                 }
335             }
336         }
337         monitor.done();
338     }
339     
340     
341     private PersistantResourceVariantByteStore getRemoteByteStore() {
342         return (PersistantResourceVariantByteStore)((CVSResourceVariantTree)getRemoteTree()).getByteStore();
343     }
344     
345     /* (non-Javadoc)
346      * @see java.lang.Object#equals(java.lang.Object)
347      */

348     public boolean equals(Object JavaDoc other) {
349         if(this == other) return true;
350         if(! (other instanceof CVSMergeSubscriber)) return false;
351         CVSMergeSubscriber s = (CVSMergeSubscriber)other;
352         return getEndTag().equals(s.getEndTag()) &&
353                getStartTag().equals(s.getStartTag()) && rootsEqual(s);
354     }
355 }
356
Popular Tags