blob: b1426661e017bdf76d9e6e0c00b90238e943aa8f [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.syncinfo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.internal.jobs.JobManager;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.core.sync.IRemoteResource;
import org.eclipse.team.internal.ccvs.core.CVSException;
import org.eclipse.team.internal.ccvs.core.CVSTag;
import org.eclipse.team.internal.ccvs.core.ICVSFolder;
import org.eclipse.team.internal.ccvs.core.ICVSRemoteResource;
import org.eclipse.team.internal.ccvs.core.ICVSResource;
import org.eclipse.team.internal.ccvs.core.Policy;
import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
import org.eclipse.team.internal.ccvs.core.util.Assert;
/**
* This RemoteSynchronizr uses a CVS Tag to fetch the remote tree
*/
public class RemoteTagSynchronizer extends CVSRemoteSynchronizer {
private CVSTag tag;
public RemoteTagSynchronizer(String id, CVSTag tag) {
super(id);
this.tag = tag;
}
public void collectChanges(IResource local, IRemoteResource remote, Collection changedResources, int depth, IProgressMonitor monitor) throws TeamException {
byte[] remoteBytes = getRemoteSyncBytes(local, remote);
boolean changed;
if (remoteBytes == null) {
changed = setRemoteDoesNotExist(local);
} else {
changed = setSyncBytes(local, remoteBytes);
}
if (changed) {
changedResources.add(local);
}
if (depth == IResource.DEPTH_ZERO) return;
Map children = mergedMembers(local, remote, monitor);
for (Iterator it = children.keySet().iterator(); it.hasNext();) {
IResource localChild = (IResource) it.next();
IRemoteResource remoteChild = (IRemoteResource)children.get(localChild);
collectChanges(localChild, remoteChild, changedResources,
depth == IResource.DEPTH_INFINITE ? IResource.DEPTH_INFINITE : IResource.DEPTH_ZERO,
monitor);
}
// Look for resources that have sync bytes but are not in the resources we care about
IResource[] resources = getChildrenWithSyncBytes(local);
for (int i = 0; i < resources.length; i++) {
IResource resource = resources[i];
if (!children.containsKey(resource)) {
// These sync bytes are stale. Purge them
removeSyncBytes(resource, IResource.DEPTH_INFINITE);
changedResources.add(resource);
}
}
}
protected Map mergedMembers(IResource local, IRemoteResource remote, IProgressMonitor progress) throws TeamException {
// {IResource -> IRemoteResource}
Map mergedResources = new HashMap();
IRemoteResource[] remoteChildren = getRemoteChildren(remote, progress);
IResource[] localChildren = getLocalChildren(local);
if (remoteChildren.length > 0 || localChildren.length > 0) {
Set allSet = new HashSet(20);
Map localSet = null;
Map remoteSet = null;
if (localChildren.length > 0) {
localSet = new HashMap(10);
for (int i = 0; i < localChildren.length; i++) {
IResource localChild = localChildren[i];
String name = localChild.getName();
localSet.put(name, localChild);
allSet.add(name);
}
}
if (remoteChildren.length > 0) {
remoteSet = new HashMap(10);
for (int i = 0; i < remoteChildren.length; i++) {
IRemoteResource remoteChild = remoteChildren[i];
String name = remoteChild.getName();
remoteSet.put(name, remoteChild);
allSet.add(name);
}
}
Iterator e = allSet.iterator();
while (e.hasNext()) {
String keyChildName = (String) e.next();
if (progress != null) {
if (progress.isCanceled()) {
throw new OperationCanceledException();
}
// XXX show some progress?
}
IResource localChild =
localSet != null ? (IResource) localSet.get(keyChildName) : null;
IRemoteResource remoteChild =
remoteSet != null ? (IRemoteResource) remoteSet.get(keyChildName) : null;
if (localChild == null) {
// there has to be a remote resource available if we got this far
Assert.isTrue(remoteChild != null);
boolean isContainer = remoteChild.isContainer();
localChild = getResourceChild(local /* parent */, keyChildName, isContainer);
}
mergedResources.put(localChild, remoteChild);
}
}
return mergedResources;
}
private IRemoteResource[] getRemoteChildren(IRemoteResource remote, IProgressMonitor progress) throws TeamException {
return remote != null ? remote.members(progress) : new IRemoteResource[0];
}
private IResource[] getLocalChildren(IResource local) throws TeamException {
IResource[] localChildren = null;
if( local.getType() != IResource.FILE && (local.exists() || local.isPhantom())) {
// Include all non-ignored resources including outgoing deletions
ICVSFolder cvsFolder = CVSWorkspaceRoot.getCVSFolderFor((IContainer)local);
// Look inside existing folders and phantoms that are CVS folders
if (local.exists() || cvsFolder.isCVSFolder()) {
ICVSResource[] cvsChildren = cvsFolder.members(ICVSFolder.MANAGED_MEMBERS | ICVSFolder.UNMANAGED_MEMBERS);
List resourceChildren = new ArrayList();
for (int i = 0; i < cvsChildren.length; i++) {
ICVSResource cvsResource = cvsChildren[i];
resourceChildren.add(cvsResource.getIResource());
}
localChildren = (IResource[]) resourceChildren.toArray(new IResource[resourceChildren.size()]);
}
}
if (localChildren == null) {
localChildren = new IResource[0];
}
return localChildren;
}
private IResource[] getChildrenWithSyncBytes(IResource local) throws TeamException {
try {
if (local.getType() != IResource.FILE && (local.exists() || local.isPhantom())) {
IResource[] allChildren = ((IContainer)local).members(true /* include phantoms */);
List childrenWithSyncBytes = new ArrayList();
for (int i = 0; i < allChildren.length; i++) {
IResource resource = allChildren[i];
if (internalGetSyncBytes(resource) != null) {
childrenWithSyncBytes.add(resource);
}
}
return (IResource[]) childrenWithSyncBytes.toArray(
new IResource[childrenWithSyncBytes.size()]);
}
} catch (CoreException e) {
throw CVSException.wrapException(e);
}
return new IResource[0];
}
private byte[] internalGetSyncBytes(IResource resource) throws TeamException {
return super.getSyncBytes(resource);
}
/*
* Returns a handle to a non-existing resource.
*/
private IResource getResourceChild(IResource parent, String childName, boolean isContainer) {
if (parent.getType() == IResource.FILE) {
return null;
}
if (isContainer) {
return ((IContainer) parent).getFolder(new Path(childName));
} else {
return ((IContainer) parent).getFile(new Path(childName));
}
}
/**
* Refreshes the contents of the remote synchronizer and returns the list
* of resources whose remote sync state changed.
*
* @param resources
* @param depth
* @param monitor
* @return
* @throws TeamException
*/
public IResource[] refresh(IResource[] resources, int depth, boolean cacheFileContentsHint, IProgressMonitor monitor) throws TeamException {
int work = 100 * resources.length;
monitor.beginTask(null, work);
List changedResources = new ArrayList();
try {
for (int i = 0; i < resources.length; i++) {
IResource resource = resources[i];
ISchedulingRule rule = resource.getProject();
try {
// Get a scheduling rule on the project since CVS may obtain a lock higher then
// the resource itself.
JobManager.getInstance().beginRule(rule, monitor);
monitor.setTaskName(Policy.bind("RemoteTagSynchronizer.0", resource.getFullPath().makeRelative().toString())); //$NON-NLS-1$
// build the remote tree only if an initial tree hasn't been provided
IRemoteResource tree = buildRemoteTree(resource, depth, cacheFileContentsHint, Policy.subMonitorFor(monitor, 70));
// update the known remote handles
IProgressMonitor sub = Policy.infiniteSubMonitorFor(monitor, 30);
try {
sub.beginTask(null, 64);
collectChanges(resource, tree, changedResources, depth, sub);
} finally {
sub.done();
}
} finally {
JobManager.getInstance().endRule(rule);
}
}
} finally {
monitor.done();
}
IResource[] changes = (IResource[]) changedResources.toArray(new IResource[changedResources.size()]);
return changes;
}
/**
* Build a remote tree for the given parameters.
*/
protected ICVSRemoteResource buildRemoteTree(IResource resource, int depth, boolean cacheFileContentsHint, IProgressMonitor monitor) throws TeamException {
// TODO: we are currently ignoring the depth parameter because the build remote tree is
// by default deep!
return CVSWorkspaceRoot.getRemoteTree(resource, tag, cacheFileContentsHint, monitor);
}
}