blob: 8fd6487817ed05624ca8bf89c92e0033c3654957 [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.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.resources.IContainer;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
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.resources.RemoteResource;
import org.eclipse.team.internal.ccvs.core.util.Assert;
import org.eclipse.team.internal.ccvs.core.util.Util;
/**
* This RemoteSynchronizr uses a CVS Tag to fetch the remote tree
*/
public class RemoteTagSynchronizer extends RemoteSynchronizer {
private static final byte[] NO_REMOTE = new byte[0];
private CVSTag tag;
public RemoteTagSynchronizer(String id, CVSTag tag) {
super(id);
this.tag = tag;
}
public void collectChanges(IResource local, ICVSRemoteResource remote, int depth, IProgressMonitor monitor) throws TeamException {
byte[] remoteBytes;
if (remote != null) {
remoteBytes = ((RemoteResource)remote).getSyncBytes();
} else {
remoteBytes = NO_REMOTE;
}
setSyncBytes(local, remoteBytes);
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();
ICVSRemoteResource remoteChild = (ICVSRemoteResource)children.get(localChild);
collectChanges(localChild, remoteChild,
depth == IResource.DEPTH_INFINITE ? IResource.DEPTH_INFINITE : IResource.DEPTH_ZERO,
monitor);
}
}
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) {
List syncChildren = new ArrayList(10);
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;
}
/*
* 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));
}
}
/**
* Return true if remote bytes for the resource have been fetched during
* a refresh. This will also return true for remote resources that do not exist
* (i.e. they have no sync bytes but did not exist remotely at the time of the
* last refresh.
*
* @param resource
* @return
*/
public boolean hasRemoteBytesFor(IResource resource) throws CVSException {
return super.getSyncBytes(resource) != null;
}
/**
* This method will return null in both cases when the remote has never been fetched
* or when the remote does not exist. Use <code>hasRemoteBytesFor</code> to
* differentiate these cases.
*/
public byte[] getSyncBytes(IResource resource) throws CVSException {
byte[] syncBytes = super.getSyncBytes(resource);
if (syncBytes != null && Util.equals(syncBytes, NO_REMOTE)) {
// If it is known that there is no remote, return null
return null;
}
return syncBytes;
}
/**
* @return
*/
public IResource[] getChangedResources() {
return (IResource[]) changedResources.toArray(new IResource[changedResources.size()]);
}
public void resetChanges() {
changedResources.clear();
}
/**
* 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);
resetChanges();
try {
for (int i = 0; i < resources.length; i++) {
IResource resource = resources[i];
// build the remote tree only if an initial tree hasn't been provided
ICVSRemoteResource tree = buildRemoteTree(resource, depth, cacheFileContentsHint, Policy.subMonitorFor(monitor, 70));
// update the known remote handles
IProgressMonitor sub = Policy.infiniteSubMonitorFor(monitor, 30);
try {
sub.beginTask(null, 512);
//removeSyncBytes(resource, IResource.DEPTH_INFINITE);
collectChanges(resource, tree, depth, sub);
} finally {
sub.done();
}
}
} finally {
monitor.done();
}
IResource[] changes = getChangedResources();
resetChanges();
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);
}
}