blob: 5cac0628ed861f111fe7ff290c11afd9f383b60b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2002 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v0.5
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v05.html
*
* Contributors:
* IBM - Initial implementation
******************************************************************************/
package org.eclipse.team.internal.core.target;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Properties;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceStatus;
import org.eclipse.core.resources.ISynchronizer;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
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.QualifiedName;
import org.eclipse.core.runtime.Status;
import org.eclipse.team.core.*;
import org.eclipse.team.core.sync.IRemoteResource;
import org.eclipse.team.core.target.*;
import org.eclipse.team.internal.core.Policy;
import org.eclipse.team.internal.core.Assert;
import org.eclipse.team.internal.core.TeamPlugin;
public abstract class SynchronizedTargetProvider extends TargetProvider {
private static final int CONFIG_FORMAT_VERSION = 2;
private final int depth = IResource.DEPTH_INFINITE;
/**
* These interfaces are to operations that can be performed on the array of resources,
* and on all resources identified by the depth parameter.
* @see execute(IOperation, IResource[], int, IProgressMonitor)
*/
protected static interface IOperation {
}
protected static interface IIterativeOperation extends IOperation {
public IStatus visit(IResource resource, int depth, IProgressMonitor progress);
}
protected static interface IRecursiveOperation extends IOperation {
public IStatus visit(IResource resource, IProgressMonitor progress);
}
/**
* Answers the synchronizer.
*/
final protected static ISynchronizer getSynchronizer() {
return ResourcesPlugin.getWorkspace().getSynchronizer();
}
/**
* Answers a new state based on an existing local resource.
*/
abstract public ResourceState newState(IResource resource);
/**
* Get the state descriptor for a given resource.
*/
public ResourceState getState(IResource resource) {
// Create a new resource state with default values.
ResourceState state = newState(resource);
state.loadState();
return state;
}
/**
* Get the resource from the provider to the workspace, and remember the fetched
* state as the base state of the resource.
*
* @see ITeamProvider.get(IResource[], int, IProgressMonitor)
*/
public void get(IResource[] resources, IProgressMonitor progress)
throws TeamException {
execute(new IIterativeOperation() {
public IStatus visit(IResource resource, int depth, IProgressMonitor progress) {
ResourceState state = getState(resource);
return new Symmetria().get(state, depth, progress);
}
}, resources, depth, progress);
}
/**
* Put the resources to the remote.
*/
public void put(IResource[] resources, IProgressMonitor progress)
throws TeamException {
execute(new IRecursiveOperation() {
public IStatus visit(IResource resource, IProgressMonitor progress) {
// The resource state must be checked-out.
ResourceState state = getState(resource);
return state.checkin(progress);
}
}, resources, depth, progress);
}
/**
* Delete the corresponding remote resource.
* Note that deletes are always deep.
*/
public void delete(IResource[] resources, IProgressMonitor progress)
throws TeamException {
execute(new IIterativeOperation() {
public IStatus visit(IResource resource, int depth, IProgressMonitor progress) {
ResourceState state = getState(resource);
return state.delete(progress);
}
}, resources, IResource.DEPTH_INFINITE, progress);
}
/**
* Answer if the local resource currently has a different timestamp to the
* base timestamp for this resource.
*
* @param resource the resource to test.
* @return <code>true</code> if the resource has a different modification
* timestamp, and <code>false</code> otherwise.
* @see ITeamSynch#isDirty(IResource)
*/
public boolean isDirty(IResource resource) {
ResourceState state = getState(resource);
return state.isDirty(resource);
}
/**
* Answers true if the base identifier of the given resource is different to the
* current released state of the resource.
*
* @param resource the resource state to test.
* @return <code>true</code> if the resource base identifier is different to the
* current released state of the resource, and <code>false</code> otherwise.
* @see ITeamSynch#isOutOfDate(IResource)
*/
public boolean isOutOfDate(IResource resource) {
ResourceState state = getState(resource);
return state.isOutOfDate();
}
/**
* Answer whether the resource has a corresponding remote resource in the provider.
*
* @param resource the resource state to test.
* @return <code>true</code> if the resource has a corresponding remote resource,
* and <code>false</code> otherwise.
* @see ITeamSynch#hasRemote(IResource)
*/
public boolean hasRemote(IResource resource) {
ResourceState state = getState(resource);
return state.hasRemote();
}
/**
* Perform the given operation on the array of resources, each to the
* specified depth. Throw an exception if a problem ocurs, otherwise
* remain silent.
*/
protected void execute(
IOperation operation,
IResource[] resources,
int depth,
IProgressMonitor progress)
throws TeamException {
// Create an array to hold the status for each resource.
IStatus[] statuses = new IStatus[resources.length];
// Remember if a failure occurred in any resource, so we can throw an exception at the end.
boolean failureOccurred = false;
// For each resource in the local resources array.
for (int i = 0; i < resources.length; i++) {
if (operation instanceof IRecursiveOperation)
statuses[i] = execute((IRecursiveOperation)operation, resources[i], depth, progress);
else
statuses[i] = ((IIterativeOperation)operation).visit(resources[i], depth, progress);
failureOccurred = failureOccurred || (!statuses[i].isOK());
}
// Finally, if any problems occurred, throw the exeption with all the statuses,
// but if there were no problems exit silently.
if (failureOccurred)
throw new TeamException(
new MultiStatus(
TeamPlugin.ID,
IStatus.ERROR,
statuses,
Policy.bind("multiStatus.errorsOccurred"), //$NON-NLS-1$
null));
// Cause all the resource changes to be broadcast to listeners.
// TeamPlugin.getManager().broadcastResourceStateChanges(resources);
}
/**
* Perform the given operation on a resource to the given depth.
*/
protected IStatus execute(
IRecursiveOperation operation,
IResource resource,
int depth,
IProgressMonitor progress) {
// Visit the given resource first.
IStatus status = operation.visit(resource, progress);
// If the resource is a file then the depth parameter is irrelevant.
if (resource.getType() == IResource.FILE)
return status;
// If we are not considering any members of the container then we are done.
if (depth == IResource.DEPTH_ZERO)
return status;
// If the operation was unsuccessful, do not attempt to go deep.
if (!status.isOK())
return status;
// If the container has no children then we are done.
IResource[] members = getMembers(resource);
if (members.length == 0)
return status;
// There are children and we are going deep, the response will be a multi-status.
MultiStatus multiStatus =
new MultiStatus(
status.getPlugin(),
status.getCode(),
status.getMessage(),
status.getException());
// The next level will be one less than the current level...
int childDepth =
(depth == IResource.DEPTH_ONE)
? IResource.DEPTH_ZERO
: IResource.DEPTH_INFINITE;
// Collect the responses in the multistatus.
for (int i = 0; i < members.length; i++)
multiStatus.add(execute(operation, members[i], childDepth, progress));
return multiStatus;
}
/**
* Answers an array of local resource members for the given resource
* or an empty arrray if the resource has no members.
*
* @param resource the local resource whose members are required.
* @return an array of <code>IResource</code> or an empty array if
* the resource has no members.
*/
protected IResource[] getMembers(IResource resource) {
if (resource.getType() != IResource.FILE) {
try {
return ((IContainer) resource).members();
} catch (CoreException exception) {
exception.printStackTrace();
throw new RuntimeException();
}
} //end-if
else
return new IResource[0];
}
}