blob: 7039863ee3bede81809d33dd0c2ceabaf813a7a5 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.team.internal.ccvs.core;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.team.core.RepositoryProvider;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.core.subscribers.ContentComparisonCriteria;
import org.eclipse.team.core.subscribers.ITeamResourceChangeListener;
import org.eclipse.team.core.subscribers.RemoteBytesSynchronizer;
import org.eclipse.team.core.subscribers.RemoteSynchronizer;
import org.eclipse.team.core.subscribers.SyncInfo;
import org.eclipse.team.core.subscribers.TeamDelta;
import org.eclipse.team.core.sync.IRemoteResource;
import org.eclipse.team.internal.ccvs.core.syncinfo.MergedSynchronizer;
import org.eclipse.team.internal.ccvs.core.syncinfo.RemoteTagSynchronizer;
/**
* A CVSMergeSubscriber is responsible for maintaining the remote trees for a merge into
* the workspace. The remote trees represent the CVS revisions of the start and end
* points (version or branch) of the merge.
*
* This subscriber stores the remote handles in the resource tree sync info slot. When
* the merge is cancelled this sync info is cleared.
*
* A merge can persist between workbench sessions and thus can be used as an
* ongoing merge.
*
* TODO: Is the merge subscriber interested in workspace sync info changes?
* TODO: Do certain operations (e.g. replace with) invalidate a merge subscriber?
* TODO: How to ensure that sync info is flushed when merge roots are deleted?
*/
public class CVSMergeSubscriber extends CVSSyncTreeSubscriber implements IResourceChangeListener, ITeamResourceChangeListener {
public static final String QUALIFIED_NAME = "org.eclipse.team.cvs.ui.cvsmerge-participant"; //$NON-NLS-1$
private static final String UNIQUE_ID_PREFIX = "merge-"; //$NON-NLS-1$
private CVSTag start, end;
private List roots;
private RemoteTagSynchronizer remoteSynchronizer;
private RemoteBytesSynchronizer mergedSynchronizer;
private RemoteTagSynchronizer baseSynchronizer;
private static final byte[] NO_REMOTE = new byte[0];
protected IResource[] refreshRemote(IResource[] resources, int depth, IProgressMonitor monitor) throws TeamException {
IResource[] remoteChanges = super.refreshRemote(resources, depth, monitor);
adjustMergedResources(remoteChanges);
return remoteChanges;
}
private void adjustMergedResources(IResource[] remoteChanges) throws TeamException {
for (int i = 0; i < remoteChanges.length; i++) {
IResource resource = remoteChanges[i];
mergedSynchronizer.removeSyncBytes(resource, IResource.DEPTH_ZERO);
}
}
private static QualifiedName getUniqueId() {
String uniqueId = Long.toString(System.currentTimeMillis());
return new QualifiedName(QUALIFIED_NAME, "CVS" + UNIQUE_ID_PREFIX + uniqueId); //$NON-NLS-1$
}
public CVSMergeSubscriber(IResource[] roots, CVSTag start, CVSTag end) {
this(getUniqueId(), roots, start, end);
}
public CVSMergeSubscriber(QualifiedName id, IResource[] roots, CVSTag start, CVSTag end) {
super(id, Policy.bind("CVSMergeSubscriber.2", start.getName(), end.getName()), Policy.bind("CVSMergeSubscriber.4")); //$NON-NLS-1$ //$NON-NLS-2$
this.start = start;
this.end = end;
this.roots = new ArrayList(Arrays.asList(roots));
initialize();
}
/* (non-Javadoc)
* @see org.eclipse.team.internal.ccvs.core.CVSWorkspaceSubscriber#initialize()
*/
private void initialize() {
QualifiedName id = getId();
String syncKeyPrefix = id.getLocalName();
remoteSynchronizer = new RemoteTagSynchronizer(syncKeyPrefix + end.getName(), end);
baseSynchronizer = new RemoteTagSynchronizer(syncKeyPrefix + start.getName(), start);
mergedSynchronizer = new MergedSynchronizer(syncKeyPrefix + "0merged"); //$NON-NLS-1$
try {
setCurrentComparisonCriteria(ContentComparisonCriteria.ID_IGNORE_WS);
} catch (TeamException e) {
// use the default but log an exception because the content comparison should
// always be available.
CVSProviderPlugin.log(e);
}
ResourcesPlugin.getWorkspace().addResourceChangeListener(this);
CVSProviderPlugin.getPlugin().getCVSWorkspaceSubscriber().addListener(this);
}
protected SyncInfo getSyncInfo(IResource local, IRemoteResource base, IRemoteResource remote, IProgressMonitor monitor) throws TeamException {
return new CVSMergeSyncInfo(local, base, remote, this, monitor);
}
public void merged(IResource[] resources) throws TeamException {
for (int i = 0; i < resources.length; i++) {
IResource resource = resources[i];
byte[] remoteBytes = remoteSynchronizer.getSyncBytes(resource);
if (remoteBytes == null) {
// If there is no remote, use a place holder to indicate the resouce was merged
remoteBytes = NO_REMOTE;
}
mergedSynchronizer.setSyncBytes(resource, remoteBytes);
}
fireTeamResourceChange(TeamDelta.asSyncChangedDeltas(this, resources));
}
/* (non-Javadoc)
* @see org.eclipse.team.core.sync.TeamSubscriber#cancel()
*/
public void cancel() {
super.cancel();
ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
remoteSynchronizer.dispose();
baseSynchronizer.dispose();
mergedSynchronizer.dispose();
}
/* (non-Javadoc)
* @see org.eclipse.team.core.sync.TeamSubscriber#roots()
*/
public IResource[] roots() {
return (IResource[]) roots.toArray(new IResource[roots.size()]);
}
/* (non-Javadoc)
* @see org.eclipse.team.internal.ccvs.core.CVSSyncTreeSubscriber#getRemoteSynchronizer()
*/
protected RemoteSynchronizer getRemoteSynchronizer() {
return remoteSynchronizer;
}
/* (non-Javadoc)
* @see org.eclipse.team.internal.ccvs.core.CVSSyncTreeSubscriber#getBaseSynchronizer()
*/
protected RemoteSynchronizer getBaseSynchronizer() {
return baseSynchronizer;
}
/* (non-Javadoc)
* @see org.eclipse.team.core.sync.TeamSubscriber#isSupervised(org.eclipse.core.resources.IResource)
*/
public boolean isSupervised(IResource resource) throws TeamException {
return getBaseSynchronizer().hasRemote(resource) || getRemoteSynchronizer().hasRemote(resource);
}
public CVSTag getStartTag() {
return start;
}
public CVSTag getEndTag() {
return end;
}
public boolean isReleaseSupported() {
// you can't release changes to a merge
return false;
}
/*
* What to do when a root resource for this merge changes?
* Deleted, Move, Copied
* Changed in a CVS way (tag changed, revision changed...)
* Contents changed by user
* @see IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent)
*/
public void resourceChanged(IResourceChangeEvent event) {
try {
IResourceDelta delta = event.getDelta();
if(delta != null) {
delta.accept(new IResourceDeltaVisitor() {
public boolean visit(IResourceDelta delta) throws CoreException {
IResource resource = delta.getResource();
if (resource.getType()==IResource.PROJECT) {
IProject project = (IProject)resource;
if (!project.isAccessible()) {
return false;
}
if ((delta.getFlags() & IResourceDelta.OPEN) != 0) {
return false;
}
if (RepositoryProvider.getProvider(project, CVSProviderPlugin.getTypeId()) == null) {
return false;
}
}
if (roots.contains(resource)) {
if (delta.getKind() == IResourceDelta.REMOVED || delta.getKind() == IResourceDelta.MOVED_TO) {
cancel();
}
// stop visiting children
return false;
}
// keep visiting children
return true;
}
});
}
} catch (CoreException e) {
CVSProviderPlugin.log(e.getStatus());
}
}
public boolean isMerged(IResource resource) throws TeamException {
return (mergedSynchronizer.getSyncBytes(resource) != null ||
mergedSynchronizer.isRemoteKnown(resource));
}
/*
* Currently only the workspace subscriber knows when a project has been deconfigured. We will listen for these events
* and remove the root then forward to merge subscriber listeners.
* (non-Javadoc)
* @see org.eclipse.team.core.subscribers.ITeamResourceChangeListener#teamResourceChanged(org.eclipse.team.core.subscribers.TeamDelta[])
*/
public void teamResourceChanged(TeamDelta[] deltas) {
for (int i = 0; i < deltas.length; i++) {
TeamDelta delta = deltas[i];
switch(delta.getFlags()) {
case TeamDelta.PROVIDER_DECONFIGURED:
IResource resource = delta.getResource();
if(roots.remove(resource)) {
fireTeamResourceChange(new TeamDelta[] {delta});
}
break;
}
}
}
}