blob: e83325a6d960306eb25dfd74a7da4edf0e4c6d8a [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.resources;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.team.internal.ccvs.core.CVSException;
import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin;
import org.eclipse.team.internal.ccvs.core.CVSStatus;
import org.eclipse.team.internal.ccvs.core.CVSTag;
import org.eclipse.team.internal.ccvs.core.ICVSFile;
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.client.Command;
import org.eclipse.team.internal.ccvs.core.client.Session;
import org.eclipse.team.internal.ccvs.core.client.Update;
import org.eclipse.team.internal.ccvs.core.client.Command.GlobalOption;
import org.eclipse.team.internal.ccvs.core.client.Command.LocalOption;
import org.eclipse.team.internal.ccvs.core.client.Command.QuietOption;
import org.eclipse.team.internal.ccvs.core.client.listeners.IStatusListener;
import org.eclipse.team.internal.ccvs.core.client.listeners.IUpdateMessageListener;
import org.eclipse.team.internal.ccvs.core.client.listeners.StatusListener;
import org.eclipse.team.internal.ccvs.core.client.listeners.UpdateListener;
import org.eclipse.team.internal.ccvs.core.connection.CVSServerException;
import org.eclipse.team.internal.ccvs.core.util.Util;
/**
* Fetch the children for the given parent folder. When fetchMembers is invoked,
* the children of the folder will be fecthced from the server and assigned to
* the children of the parent folder.
*/
public class RemoteFolderMemberFetcher implements IUpdateMessageListener, IStatusListener {
private final RemoteFolder parentFolder;
private CVSTag tag;
List folders = new ArrayList(); // RemoteFolder
List files = new ArrayList(); // RemoteFile
boolean exists = true;
List exceptions = new ArrayList(); // CVSException
protected RemoteFolderMemberFetcher(RemoteFolder parentFolder, CVSTag tag) {
this.tag = tag;
this.parentFolder = parentFolder;
}
/**
* Fetch the members for a given tag and returns them.
* During the execution of this method, the instance variable children
* will be used to contain the children. However, the variable is reset
* and the result returned. Thus, instances of RemoteFolder do not
* persist the children. Subclasses (namely RemoteFolderTree) may
* persist the children.
*/
public void fetchMembers(IProgressMonitor monitor) throws CVSException {
fetchMembers(monitor, tag);
}
public void fetchMembers(IProgressMonitor monitor, CVSTag tag) throws CVSException {
final IProgressMonitor progress = Policy.monitorFor(monitor);
progress.beginTask(Policy.bind("RemoteFolder.getMembers"), 100); //$NON-NLS-1$
try {
// Update the parent folder children so there are no children
updateParentFolderChildren();
// Perform an update to retrieve the child files and folders
IStatus status = performUpdate(Policy.subMonitorFor(progress, 50), tag);
// Update the parent folder with the new children
updateParentFolderChildren();
Policy.checkCanceled(monitor);
// Handle any errors that were identified by the listener
performErrorCheck(status, Policy.bind("RemoteFolder.errorFetchingMembers")); //$NON-NLS-1$
// Get the revision numbers for the files
ICVSFile[] remoteFiles = getFiles();
if (remoteFiles.length > 0) {
updateFileRevisions(remoteFiles, Policy.subMonitorFor(progress, 50));
} else {
progress.worked(50);
}
} catch (CVSServerException e) {
if ( ! e.isNoTagException() && e.containsErrors())
throw e;
if (tag == null)
throw e;
// we now know that this is an exception caused by a cvs bug.
// if the folder has no files in it (just subfolders) cvs does not respond with the subfolders...
// workaround: retry the request with no tag to get the directory names (if any)
Policy.checkCanceled(progress);
fetchMembers(Policy.subMonitorFor(progress, 50), null);
} finally {
progress.done();
}
}
protected IStatus performUpdate(IProgressMonitor progress, CVSTag tag) throws CVSException {
progress.beginTask(null, 100);
Session session = new Session(parentFolder.getRepository(), parentFolder, false /* output to console */);
session.open(Policy.subMonitorFor(progress, 10), false /* read-only */);
try {
// Build the local options
final List localOptions = new ArrayList();
localOptions.add(Update.RETRIEVE_ABSENT_DIRECTORIES);
if (tag != null) localOptions.add(Update.makeTagOption(tag));
return Command.UPDATE.execute(
session,
new GlobalOption[] { Command.DO_NOT_CHANGE },
(LocalOption[])localOptions.toArray(new LocalOption[localOptions.size()]),
new ICVSResource[] { parentFolder },
new UpdateListener(this),
Policy.subMonitorFor(progress, 90));
} finally {
session.close();
}
}
protected void updateFileRevisions(final ICVSFile[] files, IProgressMonitor monitor) throws CVSException {
// Perform a "cvs status..." with a listener
monitor = Policy.monitorFor(monitor);
monitor.beginTask(null, 100);
QuietOption quietness = CVSProviderPlugin.getPlugin().getQuietness();
try {
CVSProviderPlugin.getPlugin().setQuietness(Command.VERBOSE);
Session session = new Session(parentFolder.getRepository(), parentFolder, false /* output to console */);
session.open(Policy.subMonitorFor(monitor, 10), false /* read-only */);
try {
IStatus status = Command.STATUS.execute(
session,
Command.NO_GLOBAL_OPTIONS,
Command.NO_LOCAL_OPTIONS,
files,
new StatusListener(this),
Policy.subMonitorFor(monitor, 90));
performErrorCheck(status, Policy.bind("RemoteFolder.errorFetchingRevisions")); //$NON-NLS-1$
// TODO: Ensure all files have a revision?
} finally {
session.close();
}
} finally {
CVSProviderPlugin.getPlugin().setQuietness(quietness);
}
}
private void performErrorCheck(IStatus status, String errorTitle) throws CVSException {
if (status.getCode() == CVSStatus.SERVER_ERROR) {
// Only throw the exception if no files or folders were found
if (folders.size() + files.size() == 0) {
throw new CVSServerException(status);
} else {
CVSProviderPlugin.log(new CVSServerException(status));
}
}
if (!exists) {
throw new CVSException(new CVSStatus(CVSStatus.ERROR, CVSStatus.DOES_NOT_EXIST, Policy.bind("RemoteFolder.doesNotExist", this.parentFolder.getRepositoryRelativePath()))); //$NON-NLS-1$
}
// Report any internal exceptions that occured fetching the members
if ( ! exceptions.isEmpty()) {
if (exceptions.size() == 1) {
throw (CVSException)exceptions.get(0);
} else {
MultiStatus multi = new MultiStatus(CVSProviderPlugin.ID, 0, errorTitle, null);
for (int i = 0; i < exceptions.size(); i++) {
multi.merge(((CVSException)exceptions.get(i)).getStatus());
}
throw new CVSException(multi);
}
}
}
/* (non-Javadoc)
* @see org.eclipse.team.internal.ccvs.core.client.listeners.IUpdateMessageListener#directoryInformation(org.eclipse.team.internal.ccvs.core.ICVSFolder, java.lang.String, boolean)
*/
public void directoryInformation(ICVSFolder commandRoot, String stringPath, boolean newDirectory) {
try {
IPath path = this.parentFolder.getRelativePathFromRootRelativePath(commandRoot, new Path(stringPath));
if (newDirectory && path.segmentCount() == 1) {
recordFolder(path.lastSegment());
}
} catch (CVSException e) {
exceptions.add(e);
}
}
/* (non-Javadoc)
* @see org.eclipse.team.internal.ccvs.core.client.listeners.IUpdateMessageListener#directoryDoesNotExist(org.eclipse.team.internal.ccvs.core.ICVSFolder, java.lang.String)
*/
public void directoryDoesNotExist(ICVSFolder parent, String stringPath) {
try {
IPath path = this.parentFolder.getRelativePathFromRootRelativePath(parent, new Path(stringPath));
if (path.isEmpty()) {
parentDoesNotExist();
}
} catch (CVSException e) {
exceptions.add(e);
}
}
/* (non-Javadoc)
* @see org.eclipse.team.internal.ccvs.core.client.listeners.IUpdateMessageListener#fileInformation(int, org.eclipse.team.internal.ccvs.core.ICVSFolder, java.lang.String)
*/
public void fileInformation(int type, ICVSFolder parent, String filename) {
try {
IPath filePath = new Path(filename);
filePath = this.parentFolder.getRelativePathFromRootRelativePath(parent, filePath);
if( filePath.segmentCount() == 1 ) {
String properFilename = filePath.lastSegment();
recordFile(properFilename);
}
} catch (CVSException e) {
exceptions.add(e);
}
}
/* (non-Javadoc)
* @see org.eclipse.team.internal.ccvs.core.client.listeners.IUpdateMessageListener#fileDoesNotExist(org.eclipse.team.internal.ccvs.core.ICVSFolder, java.lang.String)
*/
public void fileDoesNotExist(ICVSFolder parent, String filename) {
}
/* (non-Javadoc)
* @see org.eclipse.team.internal.ccvs.core.client.listeners.IStatusListener#fileStatus(org.eclipse.team.internal.ccvs.core.ICVSFolder, java.lang.String, java.lang.String)
*/
public void fileStatus(ICVSFolder commandRoot, String path, String remoteRevision) {
if (remoteRevision == IStatusListener.FOLDER_REVISION)
// Ignore any folders
return;
try {
((RemoteFile)parentFolder.getChild(Util.getLastSegment(path))).setRevision(remoteRevision);
} catch (CVSException e) {
exceptions.add(e);
}
}
/**
* This method is invoked for each child folder as the reponses are being recieved from
* the server. Default behavior is to record the folder for later retrieval using <code>getChilren()</code>.
* Subclasses may override but should invoke the inherited method to ensure the folder gets recorded.
* @param name the name of the child folder
*/
protected RemoteFolder recordFolder(String name) {
RemoteFolder folder = new RemoteFolder(
parentFolder,
parentFolder.getRepository(),
Util.appendPath(parentFolder.getRepositoryRelativePath(), name),
tag);
folders.add(folder);
return folder;
}
/**
* This method is invoked for each child file as the reponses are being recieved from
* the server. Default behavior is to record the file for later retrieval using <code>getChildren()</code>.
* Subclasses may override but should invoke the inherited method to ensure the file gets recorded.
* This is important because the file revisions for any files are fetched subsequent to the fecthing
* of the children.
* @param name the name of the child folder
*/
protected RemoteFile recordFile(String name) {
RemoteFile file = new RemoteFile(
parentFolder,
Update.STATE_NONE,
name,
null, /* revision unknown */
null, /* keyword mode unknown */
tag);
files.add(file);
return file;
}
/**
* This method is invoked to indicate that the parent beig queried for children
* does not exist. Subclasses may override to get early notification of this but
* should still invoke the inherited method.
*/
protected void parentDoesNotExist() {
exists = false;
}
/**
* Update the parent folder such that it's children are the
* children that have been fecthed by the reciever.
*/
protected void updateParentFolderChildren() {
parentFolder.setChildren(getFetchedChildren());
}
/**
* Return the child files fetched from the server.
* @return
*/
protected ICVSFile[] getFiles() {
return (ICVSFile[]) files.toArray(new ICVSFile[files.size()]);
}
/**
* Return an array of all fecthed children.
* @return
*/
public ICVSRemoteResource[] getFetchedChildren() {
ICVSRemoteResource[] resources = new ICVSRemoteResource[folders.size() + files.size()];
int count = 0;
for (Iterator iter = folders.iterator(); iter.hasNext();) {
ICVSRemoteResource resource = (ICVSRemoteResource) iter.next();
resources[count++] = resource;
}
for (Iterator iter = files.iterator(); iter.hasNext();) {
ICVSRemoteResource resource = (ICVSRemoteResource) iter.next();
resources[count++] = resource;
}
return resources;
}
}